mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge branch 'trunk' into dec-div
This commit is contained in:
commit
94dd8a7253
29 changed files with 2448 additions and 168 deletions
|
@ -519,7 +519,7 @@ where
|
||||||
}
|
}
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
|
|
@ -830,9 +830,10 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
CallType::HigherOrderLowLevel {
|
CallType::HigherOrderLowLevel {
|
||||||
op,
|
op,
|
||||||
closure_layout,
|
|
||||||
function_owns_closure_data,
|
function_owns_closure_data,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let bytes = specialization_id.to_bytes();
|
let bytes = specialization_id.to_bytes();
|
||||||
|
@ -846,8 +847,9 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
scope,
|
scope,
|
||||||
layout,
|
layout,
|
||||||
*op,
|
*op,
|
||||||
*closure_layout,
|
|
||||||
func_spec,
|
func_spec,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
*function_owns_closure_data,
|
*function_owns_closure_data,
|
||||||
arguments,
|
arguments,
|
||||||
)
|
)
|
||||||
|
@ -1436,6 +1438,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
structure,
|
structure,
|
||||||
wrapped: Wrapped::RecordOrSingleTagUnion,
|
wrapped: Wrapped::RecordOrSingleTagUnion,
|
||||||
..
|
..
|
||||||
|
}
|
||||||
|
| AccessAtIndex {
|
||||||
|
index,
|
||||||
|
structure,
|
||||||
|
wrapped: Wrapped::LikeARoseTree,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
// extract field from a record
|
// extract field from a record
|
||||||
match load_symbol_and_layout(scope, structure) {
|
match load_symbol_and_layout(scope, structure) {
|
||||||
|
@ -2137,7 +2145,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
remainder,
|
remainder,
|
||||||
continuation,
|
body: continuation,
|
||||||
} => {
|
} => {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
|
@ -3161,7 +3169,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
let it = procedures.iter().map(|x| x.1);
|
let it = procedures.iter().map(|x| x.1);
|
||||||
|
|
||||||
let solutions = match roc_mono::alias_analysis::spec_program(entry_point, it) {
|
let solutions = match roc_mono::alias_analysis::spec_program(entry_point, it) {
|
||||||
Err(e) => panic!("Error in alias analysis: {:?}", e),
|
Err(e) => panic!("Error in alias analysis: {}", e),
|
||||||
Ok(solutions) => solutions,
|
Ok(solutions) => solutions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3815,8 +3823,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
return_layout: &Layout<'a>,
|
return_layout: &Layout<'a>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
function_layout: Layout<'a>,
|
|
||||||
func_spec: FuncSpec,
|
func_spec: FuncSpec,
|
||||||
|
argument_layouts: &[Layout<'a>],
|
||||||
|
result_layout: &Layout<'a>,
|
||||||
function_owns_closure_data: bool,
|
function_owns_closure_data: bool,
|
||||||
args: &[Symbol],
|
args: &[Symbol],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
@ -3828,6 +3837,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
macro_rules! passed_function_at_index {
|
macro_rules! passed_function_at_index {
|
||||||
($index:expr) => {{
|
($index:expr) => {{
|
||||||
let function_symbol = args[$index];
|
let function_symbol = args[$index];
|
||||||
|
let function_layout = Layout::FunctionPointer(argument_layouts, return_layout);
|
||||||
|
|
||||||
function_value_by_func_spec(env, func_spec, function_symbol, function_layout)
|
function_value_by_func_spec(env, func_spec, function_symbol, function_layout)
|
||||||
}};
|
}};
|
||||||
|
@ -4095,7 +4105,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
roc_function_call,
|
roc_function_call,
|
||||||
&function_layout,
|
result_layout,
|
||||||
list,
|
list,
|
||||||
before_layout,
|
before_layout,
|
||||||
after_layout,
|
after_layout,
|
||||||
|
@ -4139,7 +4149,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
roc_function_call,
|
roc_function_call,
|
||||||
&function_layout,
|
result_layout,
|
||||||
list,
|
list,
|
||||||
before_layout,
|
before_layout,
|
||||||
after_layout,
|
after_layout,
|
||||||
|
|
|
@ -601,18 +601,12 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
roc_function_call: RocFunctionCall<'ctx>,
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
function_layout: &Layout<'a>,
|
// Layout of the `Result after *`
|
||||||
|
result_layout: &Layout<'a>,
|
||||||
list: BasicValueEnum<'ctx>,
|
list: BasicValueEnum<'ctx>,
|
||||||
before_layout: &Layout<'a>,
|
before_layout: &Layout<'a>,
|
||||||
after_layout: &Layout<'a>,
|
after_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
// Layout of the `Result after *`
|
|
||||||
let result_layout = match function_layout {
|
|
||||||
Layout::FunctionPointer(_, ret) => ret,
|
|
||||||
Layout::Closure(_, _, ret) => ret,
|
|
||||||
_ => unreachable!("not a callable layout"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||||
|
|
||||||
call_bitcode_fn(
|
call_bitcode_fn(
|
||||||
|
@ -638,18 +632,12 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
roc_function_call: RocFunctionCall<'ctx>,
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
function_layout: &Layout<'a>,
|
// Layout of the `Result * err`
|
||||||
|
result_layout: &Layout<'a>,
|
||||||
list: BasicValueEnum<'ctx>,
|
list: BasicValueEnum<'ctx>,
|
||||||
before_layout: &Layout<'a>,
|
before_layout: &Layout<'a>,
|
||||||
after_layout: &Layout<'a>,
|
after_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
// Layout of the `Result after *`
|
|
||||||
let result_layout = match function_layout {
|
|
||||||
Layout::FunctionPointer(_, ret) => ret,
|
|
||||||
Layout::Closure(_, _, ret) => ret,
|
|
||||||
_ => unreachable!("not a callable layout"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||||
|
|
||||||
call_bitcode_fn(
|
call_bitcode_fn(
|
||||||
|
|
|
@ -19,19 +19,22 @@ pub const STATIC_STR_NAME: ConstName = ConstName(&Symbol::STR_ALIAS_ANALYSIS_STA
|
||||||
|
|
||||||
const ENTRY_POINT_NAME: &[u8] = b"mainForHost";
|
const ENTRY_POINT_NAME: &[u8] = b"mainForHost";
|
||||||
|
|
||||||
pub fn func_name_bytes(proc: &Proc) -> [u8; 16] {
|
pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] {
|
||||||
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout)
|
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), proc.ret_layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEBUG: bool = false;
|
||||||
|
const SIZE: usize = if DEBUG { 50 } else { 16 };
|
||||||
|
|
||||||
pub fn func_name_bytes_help<'a, I>(
|
pub fn func_name_bytes_help<'a, I>(
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
argument_layouts: I,
|
argument_layouts: I,
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
) -> [u8; 16]
|
) -> [u8; SIZE]
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Layout<'a>>,
|
I: Iterator<Item = Layout<'a>>,
|
||||||
{
|
{
|
||||||
let mut name_bytes = [0u8; 16];
|
let mut name_bytes = [0u8; SIZE];
|
||||||
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
@ -75,9 +78,27 @@ where
|
||||||
*target = *source;
|
*target = *source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DEBUG {
|
||||||
|
for (i, c) in (format!("{:?}", symbol)).chars().take(25).enumerate() {
|
||||||
|
name_bytes[25 + i] = c as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
name_bytes
|
name_bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bytes_as_ascii(bytes: &[u8]) -> String {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
for byte in bytes {
|
||||||
|
write!(buf, "{:02X}", byte).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
pub fn spec_program<'a, I>(
|
pub fn spec_program<'a, I>(
|
||||||
entry_point: crate::ir::EntryPoint<'a>,
|
entry_point: crate::ir::EntryPoint<'a>,
|
||||||
procs: I,
|
procs: I,
|
||||||
|
@ -102,15 +123,34 @@ where
|
||||||
m.add_const(STATIC_STR_NAME, static_str_def)?;
|
m.add_const(STATIC_STR_NAME, static_str_def)?;
|
||||||
|
|
||||||
// the entry point wrapper
|
// the entry point wrapper
|
||||||
|
let roc_main_bytes = func_name_bytes_help(
|
||||||
|
entry_point.symbol,
|
||||||
|
entry_point.layout.arguments.iter().copied(),
|
||||||
|
entry_point.layout.result,
|
||||||
|
);
|
||||||
|
let roc_main = FuncName(&roc_main_bytes);
|
||||||
|
|
||||||
|
let entry_point_function = build_entry_point(entry_point.layout, roc_main)?;
|
||||||
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
||||||
let entry_point_function = build_entry_point(entry_point.layout, entry_point_name)?;
|
|
||||||
m.add_func(entry_point_name, entry_point_function)?;
|
m.add_func(entry_point_name, entry_point_function)?;
|
||||||
|
|
||||||
// all other functions
|
// all other functions
|
||||||
for proc in procs {
|
for proc in procs {
|
||||||
|
let bytes = func_name_bytes(proc);
|
||||||
|
let func_name = FuncName(&bytes);
|
||||||
|
|
||||||
|
if DEBUG {
|
||||||
|
eprintln!(
|
||||||
|
"{:?}: {:?} with {:?} args",
|
||||||
|
proc.name,
|
||||||
|
bytes_as_ascii(&bytes),
|
||||||
|
proc.args.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let spec = proc_spec(proc)?;
|
let spec = proc_spec(proc)?;
|
||||||
|
|
||||||
m.add_func(FuncName(&func_name_bytes(proc)), spec)?;
|
m.add_func(func_name, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
m.build()?
|
m.build()?
|
||||||
|
@ -126,7 +166,9 @@ where
|
||||||
p.build()?
|
p.build()?
|
||||||
};
|
};
|
||||||
|
|
||||||
// eprintln!("{}", program.to_source_string());
|
if DEBUG {
|
||||||
|
eprintln!("{}", program.to_source_string());
|
||||||
|
}
|
||||||
|
|
||||||
morphic_lib::solve(program)
|
morphic_lib::solve(program)
|
||||||
}
|
}
|
||||||
|
@ -198,11 +240,25 @@ fn stmt_spec(
|
||||||
use Stmt::*;
|
use Stmt::*;
|
||||||
|
|
||||||
match stmt {
|
match stmt {
|
||||||
Let(symbol, expr, layout, continuation) => {
|
Let(symbol, expr, expr_layout, mut continuation) => {
|
||||||
let value_id = expr_spec(builder, env, block, layout, expr)?;
|
let value_id = expr_spec(builder, env, block, expr_layout, expr)?;
|
||||||
env.symbols.insert(*symbol, value_id);
|
env.symbols.insert(*symbol, value_id);
|
||||||
|
|
||||||
|
let mut queue = vec![symbol];
|
||||||
|
|
||||||
|
while let Let(symbol, expr, expr_layout, c) = continuation {
|
||||||
|
let value_id = expr_spec(builder, env, block, expr_layout, expr)?;
|
||||||
|
env.symbols.insert(*symbol, value_id);
|
||||||
|
|
||||||
|
queue.push(symbol);
|
||||||
|
continuation = c;
|
||||||
|
}
|
||||||
|
|
||||||
let result = stmt_spec(builder, env, block, layout, continuation)?;
|
let result = stmt_spec(builder, env, block, layout, continuation)?;
|
||||||
env.symbols.remove(symbol);
|
|
||||||
|
for symbol in queue {
|
||||||
|
env.symbols.remove(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -235,7 +291,7 @@ fn stmt_spec(
|
||||||
cond_layout: _,
|
cond_layout: _,
|
||||||
branches,
|
branches,
|
||||||
default_branch,
|
default_branch,
|
||||||
ret_layout,
|
ret_layout: _lies,
|
||||||
} => {
|
} => {
|
||||||
let mut cases = Vec::with_capacity(branches.len() + 1);
|
let mut cases = Vec::with_capacity(branches.len() + 1);
|
||||||
|
|
||||||
|
@ -246,7 +302,7 @@ fn stmt_spec(
|
||||||
|
|
||||||
for branch in it {
|
for branch in it {
|
||||||
let block = builder.add_block();
|
let block = builder.add_block();
|
||||||
let value_id = stmt_spec(builder, env, block, ret_layout, branch)?;
|
let value_id = stmt_spec(builder, env, block, layout, branch)?;
|
||||||
cases.push(BlockExpr(block, value_id));
|
cases.push(BlockExpr(block, value_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +338,7 @@ fn stmt_spec(
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let mut type_ids = Vec::new();
|
let mut type_ids = Vec::new();
|
||||||
|
@ -298,27 +354,33 @@ fn stmt_spec(
|
||||||
let (jpid, jp_argument) =
|
let (jpid, jp_argument) =
|
||||||
builder.declare_continuation(block, jp_arg_type_id, ret_type_id)?;
|
builder.declare_continuation(block, jp_arg_type_id, ret_type_id)?;
|
||||||
|
|
||||||
|
// NOTE join point arguments can shadow variables from the outer scope
|
||||||
|
// the ordering of steps here is important
|
||||||
|
|
||||||
|
// add this ID so both body and remainder can reference it
|
||||||
|
env.join_points.insert(*id, jpid);
|
||||||
|
|
||||||
|
// first, with the current variable bindings, process the remainder
|
||||||
|
let cont_block = builder.add_block();
|
||||||
|
let cont_value_id = stmt_spec(builder, env, cont_block, layout, remainder)?;
|
||||||
|
|
||||||
|
// only then introduce variables bound by the jump point, and process its body
|
||||||
let join_body_sub_block = {
|
let join_body_sub_block = {
|
||||||
env.join_points.insert(*id, jpid);
|
|
||||||
let jp_body_block = builder.add_block();
|
let jp_body_block = builder.add_block();
|
||||||
|
|
||||||
// unpack the argument
|
// unpack the argument
|
||||||
for (i, p) in parameters.iter().enumerate() {
|
for (i, p) in parameters.iter().enumerate() {
|
||||||
let value_id =
|
let value_id =
|
||||||
builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?;
|
builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?;
|
||||||
|
|
||||||
env.symbols.insert(p.symbol, value_id);
|
env.symbols.insert(p.symbol, value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?;
|
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, body)?;
|
||||||
|
|
||||||
BlockExpr(jp_body_block, jp_body_value_id)
|
BlockExpr(jp_body_block, jp_body_value_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE the symbols bound by the join point can shadow the argument symbols of the
|
|
||||||
// surrounding function, so we don't remove them from the env here
|
|
||||||
|
|
||||||
let cont_block = builder.add_block();
|
|
||||||
let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?;
|
|
||||||
|
|
||||||
env.join_points.remove(id);
|
env.join_points.remove(id);
|
||||||
builder.define_continuation(jpid, join_body_sub_block)?;
|
builder.define_continuation(jpid, join_body_sub_block)?;
|
||||||
|
|
||||||
|
@ -423,7 +485,7 @@ fn call_spec(
|
||||||
),
|
),
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
specialization_id,
|
specialization_id,
|
||||||
closure_layout: _,
|
closure_env_layout,
|
||||||
op,
|
op,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -458,15 +520,160 @@ fn call_spec(
|
||||||
DictWalk => {
|
DictWalk => {
|
||||||
let dict = env.symbols[&call.arguments[0]];
|
let dict = env.symbols[&call.arguments[0]];
|
||||||
let default = env.symbols[&call.arguments[1]];
|
let default = env.symbols[&call.arguments[1]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[3]];
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
|
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 _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?;
|
||||||
|
|
||||||
let first = builder.add_bag_get(block, bag)?;
|
let first = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
let argument = builder.add_make_tuple(block, &[first, default])?;
|
let key = builder.add_get_tuple_field(block, first, 0)?;
|
||||||
|
let val = builder.add_get_tuple_field(block, first, 1)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[key, val, default])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[key, val, default, closure_env])?
|
||||||
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListWalk | ListWalkBackwards | ListWalkUntil => {
|
||||||
|
let list = env.symbols[&call.arguments[0]];
|
||||||
|
let default = env.symbols[&call.arguments[1]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[3]];
|
||||||
|
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let first = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[first, default])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[first, default, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListMapWithIndex => {
|
||||||
|
let list = env.symbols[&call.arguments[0]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[2]];
|
||||||
|
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let first = builder.add_bag_get(block, bag)?;
|
||||||
|
let index = builder.add_make_tuple(block, &[])?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[first, index])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[first, index, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListMap => {
|
||||||
|
let list1 = env.symbols[&call.arguments[0]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[2]];
|
||||||
|
|
||||||
|
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||||
|
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[elem1])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[elem1, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListSortWith => {
|
||||||
|
let list1 = env.symbols[&call.arguments[0]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[2]];
|
||||||
|
|
||||||
|
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||||
|
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem1])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem1, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListMap2 => {
|
||||||
|
let list1 = env.symbols[&call.arguments[0]];
|
||||||
|
let list2 = env.symbols[&call.arguments[1]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[3]];
|
||||||
|
|
||||||
|
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||||
|
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||||
|
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||||
|
|
||||||
|
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||||
|
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
||||||
|
let elem2 = builder.add_bag_get(block, bag2)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem2])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListMap3 => {
|
||||||
|
let list1 = env.symbols[&call.arguments[0]];
|
||||||
|
let list2 = env.symbols[&call.arguments[1]];
|
||||||
|
let list3 = env.symbols[&call.arguments[2]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[4]];
|
||||||
|
|
||||||
|
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||||
|
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
||||||
|
let elem1 = builder.add_bag_get(block, bag1)?;
|
||||||
|
|
||||||
|
let bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||||
|
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
||||||
|
let elem2 = builder.add_bag_get(block, bag2)?;
|
||||||
|
|
||||||
|
let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
||||||
|
let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?;
|
||||||
|
let elem3 = builder.add_bag_get(block, bag3)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem2, elem3])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])?
|
||||||
|
};
|
||||||
|
builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListKeepIf | ListKeepOks | ListKeepErrs => {
|
||||||
|
let list = env.symbols[&call.arguments[0]];
|
||||||
|
let closure_env = env.symbols[&call.arguments[2]];
|
||||||
|
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
// let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let first = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
builder.add_make_tuple(block, &[first])?
|
||||||
|
} else {
|
||||||
|
builder.add_make_tuple(block, &[first, closure_env])?
|
||||||
|
};
|
||||||
|
let result = builder.add_call(block, spec_var, module, name, argument)?;
|
||||||
|
let unit = builder.add_tuple_type(&[])?;
|
||||||
|
builder.add_unknown_with(block, &[result], unit)?;
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// fake a call to the function argument
|
// fake a call to the function argument
|
||||||
// to make sure the function is specialized
|
// to make sure the function is specialized
|
||||||
|
@ -653,7 +860,7 @@ fn expr_spec(
|
||||||
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
||||||
AccessAtIndex {
|
AccessAtIndex {
|
||||||
index,
|
index,
|
||||||
field_layouts,
|
field_layouts: _,
|
||||||
structure,
|
structure,
|
||||||
wrapped,
|
wrapped,
|
||||||
} => {
|
} => {
|
||||||
|
@ -673,17 +880,20 @@ fn expr_spec(
|
||||||
Wrapped::RecordOrSingleTagUnion => {
|
Wrapped::RecordOrSingleTagUnion => {
|
||||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||||
}
|
}
|
||||||
|
Wrapped::LikeARoseTree => {
|
||||||
|
let result_type = layout_spec(builder, layout)?;
|
||||||
|
builder.add_unknown_with(block, &[value_id], result_type)
|
||||||
|
}
|
||||||
Wrapped::MultiTagUnion => {
|
Wrapped::MultiTagUnion => {
|
||||||
// Clearly this is not generally correct, but it should be for our examples
|
// Clearly this is not generally correct, but it should be for our examples
|
||||||
let hacky_is_recursive =
|
// let hacky_is_recursive = field_layouts.iter().any(|l| l == &Layout::RecursivePointer);
|
||||||
field_layouts.iter().any(|l| l == &Layout::RecursivePointer);
|
// if hacky_is_recursive {
|
||||||
|
|
||||||
if hacky_is_recursive {
|
// we don't know what constructor we are at this point, so how can we get a
|
||||||
let result_type = layout_spec(builder, layout)?;
|
// field from an enum value?
|
||||||
builder.add_unknown_with(block, &[value_id], result_type)
|
|
||||||
} else {
|
let result_type = layout_spec(builder, layout)?;
|
||||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
builder.add_unknown_with(block, &[value_id], result_type)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,7 +910,9 @@ fn expr_spec(
|
||||||
bag = builder.add_bag_insert(block, bag, value_id)?;
|
bag = builder.add_bag_insert(block, bag, value_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(bag)
|
let cell = builder.add_new_heap_cell(block)?;
|
||||||
|
|
||||||
|
builder.add_make_tuple(block, &[cell, bag])
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyArray => {
|
EmptyArray => {
|
||||||
|
@ -827,8 +1039,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeI
|
||||||
|
|
||||||
fn str_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
fn str_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||||
let cell_id = builder.add_heap_cell_type();
|
let cell_id = builder.add_heap_cell_type();
|
||||||
let len_id = builder.add_tuple_type(&[])?;
|
builder.add_tuple_type(&[cell_id])
|
||||||
builder.add_tuple_type(&[cell_id, len_id])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const OK_TAG_ID: u8 = 1u8;
|
// const OK_TAG_ID: u8 = 1u8;
|
||||||
|
|
|
@ -193,7 +193,7 @@ impl<'a> ParamMap<'a> {
|
||||||
id: j,
|
id: j,
|
||||||
parameters: xs,
|
parameters: xs,
|
||||||
remainder: v,
|
remainder: v,
|
||||||
continuation: b,
|
body: b,
|
||||||
} => {
|
} => {
|
||||||
let already_in_there = self
|
let already_in_there = self
|
||||||
.items
|
.items
|
||||||
|
@ -393,15 +393,20 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
op, closure_layout, ..
|
op,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
use roc_module::low_level::LowLevel::*;
|
use roc_module::low_level::LowLevel::*;
|
||||||
|
|
||||||
debug_assert!(op.is_higher_order());
|
debug_assert!(op.is_higher_order());
|
||||||
|
|
||||||
|
let closure_layout = Layout::FunctionPointer(arg_layouts, ret_layout);
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
ListMap | ListKeepIf | ListKeepOks | ListKeepErrs => {
|
ListMap | ListKeepIf | ListKeepOks | ListKeepErrs => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// own the list if the function wants to own the element
|
// own the list if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
|
@ -417,7 +422,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ListMapWithIndex => {
|
ListMapWithIndex => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// own the list if the function wants to own the element
|
// own the list if the function wants to own the element
|
||||||
if !function_ps[1].borrow {
|
if !function_ps[1].borrow {
|
||||||
|
@ -432,7 +437,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ListMap2 => match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
ListMap2 => match self.param_map.get_symbol(arguments[2], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// own the lists if the function wants to own the element
|
// own the lists if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
|
@ -450,7 +455,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
},
|
},
|
||||||
ListMap3 => match self.param_map.get_symbol(arguments[3], *closure_layout) {
|
ListMap3 => match self.param_map.get_symbol(arguments[3], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// own the lists if the function wants to own the element
|
// own the lists if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
|
@ -471,7 +476,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
},
|
},
|
||||||
ListSortWith => {
|
ListSortWith => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// always own the input list
|
// always own the input list
|
||||||
self.own_var(arguments[0]);
|
self.own_var(arguments[0]);
|
||||||
|
@ -485,7 +490,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => {
|
ListWalk | ListWalkUntil | ListWalkBackwards | DictWalk => {
|
||||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
match self.param_map.get_symbol(arguments[2], closure_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// own the data structure if the function wants to own the element
|
// own the data structure if the function wants to own the element
|
||||||
if !function_ps[0].borrow {
|
if !function_ps[0].borrow {
|
||||||
|
@ -618,7 +623,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
id: j,
|
id: j,
|
||||||
parameters: ys,
|
parameters: ys,
|
||||||
remainder: v,
|
remainder: v,
|
||||||
continuation: b,
|
body: b,
|
||||||
} => {
|
} => {
|
||||||
let old = self.param_set.clone();
|
let old = self.param_set.clone();
|
||||||
self.update_param_set(ys);
|
self.update_param_set(ys);
|
||||||
|
|
|
@ -597,7 +597,7 @@ fn to_relevant_branch_help<'a>(
|
||||||
start.extend(end);
|
start.extend(end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Wrapped::RecordOrSingleTagUnion => {
|
Wrapped::RecordOrSingleTagUnion | Wrapped::LikeARoseTree => {
|
||||||
let sub_positions = arguments.into_iter().enumerate().map(
|
let sub_positions = arguments.into_iter().enumerate().map(
|
||||||
|(index, (pattern, _))| {
|
|(index, (pattern, _))| {
|
||||||
let mut new_path = path.to_vec();
|
let mut new_path = path.to_vec();
|
||||||
|
@ -956,7 +956,7 @@ pub fn optimize_when<'a>(
|
||||||
stmt = Stmt::Join {
|
stmt = Stmt::Join {
|
||||||
id,
|
id,
|
||||||
parameters: &[],
|
parameters: &[],
|
||||||
continuation: env.arena.alloc(body),
|
body: env.arena.alloc(body),
|
||||||
remainder: env.arena.alloc(stmt),
|
remainder: env.arena.alloc(stmt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1329,7 +1329,7 @@ fn compile_guard<'a>(
|
||||||
id,
|
id,
|
||||||
parameters: arena.alloc([param]),
|
parameters: arena.alloc([param]),
|
||||||
remainder: stmt,
|
remainder: stmt,
|
||||||
continuation: arena.alloc(cond),
|
body: arena.alloc(cond),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1622,7 +1622,7 @@ fn decide_to_branching<'a>(
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
id: fail_jp_id,
|
id: fail_jp_id,
|
||||||
parameters: &[],
|
parameters: &[],
|
||||||
continuation: fail,
|
body: fail,
|
||||||
remainder: arena.alloc(test_stmt),
|
remainder: arena.alloc(test_stmt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,7 +622,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let continuation = expand_and_cancel(env, continuation);
|
let continuation = expand_and_cancel(env, continuation);
|
||||||
|
@ -631,7 +631,7 @@ fn expand_and_cancel<'a>(env: &mut Env<'a, '_>, stmt: &'a Stmt<'a>) -> &'a Stmt<
|
||||||
let stmt = Join {
|
let stmt = Join {
|
||||||
id: *id,
|
id: *id,
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub fn occurring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>)
|
||||||
|
|
||||||
Join {
|
Join {
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
@ -455,7 +455,7 @@ impl<'a> Context<'a> {
|
||||||
|
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
op,
|
op,
|
||||||
closure_layout,
|
closure_env_layout,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -467,7 +467,7 @@ impl<'a> Context<'a> {
|
||||||
call_type: if let Some(OWNED) = $borrows.map(|p| p.borrow) {
|
call_type: if let Some(OWNED) = $borrows.map(|p| p.borrow) {
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
op: *op,
|
op: *op,
|
||||||
closure_layout: *closure_layout,
|
closure_env_layout: *closure_env_layout,
|
||||||
function_owns_closure_data: true,
|
function_owns_closure_data: true,
|
||||||
specialization_id: *specialization_id,
|
specialization_id: *specialization_id,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
|
@ -497,12 +497,14 @@ impl<'a> Context<'a> {
|
||||||
const FUNCTION: bool = BORROWED;
|
const FUNCTION: bool = BORROWED;
|
||||||
const CLOSURE_DATA: bool = BORROWED;
|
const CLOSURE_DATA: bool = BORROWED;
|
||||||
|
|
||||||
|
let function_layout = Layout::FunctionPointer(arg_layouts, ret_layout);
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
roc_module::low_level::LowLevel::ListMap
|
roc_module::low_level::LowLevel::ListMap
|
||||||
| roc_module::low_level::LowLevel::ListKeepIf
|
| roc_module::low_level::LowLevel::ListKeepIf
|
||||||
| roc_module::low_level::LowLevel::ListKeepOks
|
| roc_module::low_level::LowLevel::ListKeepOks
|
||||||
| roc_module::low_level::LowLevel::ListKeepErrs => {
|
| roc_module::low_level::LowLevel::ListKeepErrs => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
let borrows = [function_ps[0].borrow, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
|
@ -524,7 +526,7 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roc_module::low_level::LowLevel::ListMapWithIndex => {
|
roc_module::low_level::LowLevel::ListMapWithIndex => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
|
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
|
@ -545,7 +547,7 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roc_module::low_level::LowLevel::ListMap2 => {
|
roc_module::low_level::LowLevel::ListMap2 => {
|
||||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
match self.param_map.get_symbol(arguments[2], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
let borrows = [
|
let borrows = [
|
||||||
function_ps[0].borrow,
|
function_ps[0].borrow,
|
||||||
|
@ -572,7 +574,7 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roc_module::low_level::LowLevel::ListMap3 => {
|
roc_module::low_level::LowLevel::ListMap3 => {
|
||||||
match self.param_map.get_symbol(arguments[3], *closure_layout) {
|
match self.param_map.get_symbol(arguments[3], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
let borrows = [
|
let borrows = [
|
||||||
function_ps[0].borrow,
|
function_ps[0].borrow,
|
||||||
|
@ -601,7 +603,7 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roc_module::low_level::LowLevel::ListSortWith => {
|
roc_module::low_level::LowLevel::ListSortWith => {
|
||||||
match self.param_map.get_symbol(arguments[1], *closure_layout) {
|
match self.param_map.get_symbol(arguments[1], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
let borrows = [OWNED, FUNCTION, CLOSURE_DATA];
|
let borrows = [OWNED, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
|
@ -623,7 +625,7 @@ impl<'a> Context<'a> {
|
||||||
| roc_module::low_level::LowLevel::ListWalkUntil
|
| roc_module::low_level::LowLevel::ListWalkUntil
|
||||||
| roc_module::low_level::LowLevel::ListWalkBackwards
|
| roc_module::low_level::LowLevel::ListWalkBackwards
|
||||||
| roc_module::low_level::LowLevel::DictWalk => {
|
| roc_module::low_level::LowLevel::DictWalk => {
|
||||||
match self.param_map.get_symbol(arguments[2], *closure_layout) {
|
match self.param_map.get_symbol(arguments[2], function_layout) {
|
||||||
Some(function_ps) => {
|
Some(function_ps) => {
|
||||||
// borrow data structure based on first argument of the folded function
|
// borrow data structure based on first argument of the folded function
|
||||||
// borrow the default based on second argument of the folded function
|
// borrow the default based on second argument of the folded function
|
||||||
|
@ -978,7 +980,7 @@ impl<'a> Context<'a> {
|
||||||
id: j,
|
id: j,
|
||||||
parameters: _,
|
parameters: _,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
continuation: v,
|
body: v,
|
||||||
} => {
|
} => {
|
||||||
// get the parameters with borrow signature
|
// get the parameters with borrow signature
|
||||||
let xs = self.param_map.get_join_point(*j);
|
let xs = self.param_map.get_join_point(*j);
|
||||||
|
@ -1000,7 +1002,7 @@ impl<'a> Context<'a> {
|
||||||
id: *j,
|
id: *j,
|
||||||
parameters: xs,
|
parameters: xs,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
continuation: v,
|
body: v,
|
||||||
}),
|
}),
|
||||||
b_live_vars,
|
b_live_vars,
|
||||||
)
|
)
|
||||||
|
@ -1143,7 +1145,7 @@ pub fn collect_stmt(
|
||||||
id: j,
|
id: j,
|
||||||
parameters,
|
parameters,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
continuation: v,
|
body: v,
|
||||||
} => {
|
} => {
|
||||||
let mut j_live_vars = collect_stmt(v, jp_live_vars, MutSet::default());
|
let mut j_live_vars = collect_stmt(v, jp_live_vars, MutSet::default());
|
||||||
for param in parameters.iter() {
|
for param in parameters.iter() {
|
||||||
|
|
|
@ -890,9 +890,10 @@ pub enum Stmt<'a> {
|
||||||
Join {
|
Join {
|
||||||
id: JoinPointId,
|
id: JoinPointId,
|
||||||
parameters: &'a [Param<'a>],
|
parameters: &'a [Param<'a>],
|
||||||
/// does not contain jumps to this id
|
/// body of the join point
|
||||||
continuation: &'a Stmt<'a>,
|
/// what happens after _jumping to_ the join point
|
||||||
/// the "body" of the join point, contains the jumps to this id
|
body: &'a Stmt<'a>,
|
||||||
|
/// what happens after _defining_ the join point
|
||||||
remainder: &'a Stmt<'a>,
|
remainder: &'a Stmt<'a>,
|
||||||
},
|
},
|
||||||
Jump(JoinPointId, &'a [Symbol]),
|
Jump(JoinPointId, &'a [Symbol]),
|
||||||
|
@ -1013,6 +1014,8 @@ pub enum Wrapped {
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
SingleElementRecord,
|
SingleElementRecord,
|
||||||
RecordOrSingleTagUnion,
|
RecordOrSingleTagUnion,
|
||||||
|
/// Like a rose tree; recursive, but only one tag
|
||||||
|
LikeARoseTree,
|
||||||
MultiTagUnion,
|
MultiTagUnion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1045,7 +1048,7 @@ impl Wrapped {
|
||||||
},
|
},
|
||||||
_ => Some(Wrapped::MultiTagUnion),
|
_ => Some(Wrapped::MultiTagUnion),
|
||||||
},
|
},
|
||||||
NonNullableUnwrapped(_) => Some(Wrapped::RecordOrSingleTagUnion),
|
NonNullableUnwrapped(_) => Some(Wrapped::LikeARoseTree),
|
||||||
|
|
||||||
NullableWrapped { .. } | NullableUnwrapped { .. } => {
|
NullableWrapped { .. } | NullableUnwrapped { .. } => {
|
||||||
Some(Wrapped::MultiTagUnion)
|
Some(Wrapped::MultiTagUnion)
|
||||||
|
@ -1151,7 +1154,7 @@ pub enum CallType<'a> {
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
/// the layout of the closure argument, if any
|
/// the layout of the closure argument, if any
|
||||||
closure_layout: Layout<'a>,
|
closure_env_layout: Option<Layout<'a>>,
|
||||||
/// specialization id of the function argument
|
/// specialization id of the function argument
|
||||||
specialization_id: CallSpecId,
|
specialization_id: CallSpecId,
|
||||||
/// does the function need to own the closure data
|
/// does the function need to own the closure data
|
||||||
|
@ -1476,7 +1479,7 @@ impl<'a> Stmt<'a> {
|
||||||
Join {
|
Join {
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let it = parameters.iter().map(|p| symbol_to_doc(alloc, p.symbol));
|
let it = parameters.iter().map(|p| symbol_to_doc(alloc, p.symbol));
|
||||||
|
@ -2712,8 +2715,6 @@ macro_rules! match_on_closure_argument {
|
||||||
|
|
||||||
let arena = $env.arena;
|
let arena = $env.arena;
|
||||||
|
|
||||||
let function_layout = arena.alloc(top_level).full();
|
|
||||||
|
|
||||||
let arg_layouts = top_level.arguments;
|
let arg_layouts = top_level.arguments;
|
||||||
let ret_layout = top_level.result;
|
let ret_layout = top_level.result;
|
||||||
|
|
||||||
|
@ -2723,10 +2724,10 @@ macro_rules! match_on_closure_argument {
|
||||||
$env,
|
$env,
|
||||||
lambda_set,
|
lambda_set,
|
||||||
$closure_data_symbol,
|
$closure_data_symbol,
|
||||||
|top_level_function, closure_data, function_layout, specialization_id| self::Call {
|
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
||||||
call_type: CallType::HigherOrderLowLevel {
|
call_type: CallType::HigherOrderLowLevel {
|
||||||
op: $op,
|
op: $op,
|
||||||
closure_layout: function_layout,
|
closure_env_layout,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
function_owns_closure_data: false,
|
function_owns_closure_data: false,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
|
@ -2734,7 +2735,6 @@ macro_rules! match_on_closure_argument {
|
||||||
},
|
},
|
||||||
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
|
arguments: arena.alloc([$($x,)* top_level_function, closure_data]),
|
||||||
},
|
},
|
||||||
function_layout,
|
|
||||||
$layout,
|
$layout,
|
||||||
$assigned,
|
$assigned,
|
||||||
$hole,
|
$hole,
|
||||||
|
@ -3328,7 +3328,7 @@ pub fn with_hole<'a>(
|
||||||
id,
|
id,
|
||||||
parameters: env.arena.alloc([param]),
|
parameters: env.arena.alloc([param]),
|
||||||
remainder: env.arena.alloc(stmt),
|
remainder: env.arena.alloc(stmt),
|
||||||
continuation: hole,
|
body: hole,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3381,7 +3381,7 @@ pub fn with_hole<'a>(
|
||||||
id,
|
id,
|
||||||
parameters: env.arena.alloc([param]),
|
parameters: env.arena.alloc([param]),
|
||||||
remainder: env.arena.alloc(stmt),
|
remainder: env.arena.alloc(stmt),
|
||||||
continuation: env.arena.alloc(hole),
|
body: env.arena.alloc(hole),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4275,7 +4275,10 @@ fn convert_tag_union<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Unwrapped(_, field_layouts) => {
|
Unwrapped {
|
||||||
|
arguments: field_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
||||||
|
|
||||||
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
|
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
|
||||||
|
@ -5327,7 +5330,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
remainder,
|
remainder,
|
||||||
continuation,
|
body: continuation,
|
||||||
} => {
|
} => {
|
||||||
let opt_remainder = substitute_in_stmt_help(arena, remainder, subs);
|
let opt_remainder = substitute_in_stmt_help(arena, remainder, subs);
|
||||||
let opt_continuation = substitute_in_stmt_help(arena, continuation, subs);
|
let opt_continuation = substitute_in_stmt_help(arena, continuation, subs);
|
||||||
|
@ -5340,7 +5343,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
id: *id,
|
id: *id,
|
||||||
parameters,
|
parameters,
|
||||||
remainder,
|
remainder,
|
||||||
continuation,
|
body: continuation,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -7013,7 +7016,10 @@ fn from_can_pattern_help<'a>(
|
||||||
union,
|
union,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Unwrapped(_, field_layouts) => {
|
Unwrapped {
|
||||||
|
arguments: field_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let union = crate::exhaustive::Union {
|
let union = crate::exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: vec![Ctor {
|
alternatives: vec![Ctor {
|
||||||
|
@ -7708,13 +7714,12 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||||
lambda_set: LambdaSet<'a>,
|
lambda_set: LambdaSet<'a>,
|
||||||
closure_data_symbol: Symbol,
|
closure_data_symbol: Symbol,
|
||||||
to_lowlevel_call: ToLowLevelCall,
|
to_lowlevel_call: ToLowLevelCall,
|
||||||
function_layout: Layout<'a>,
|
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
match lambda_set.runtime_representation() {
|
match lambda_set.runtime_representation() {
|
||||||
Layout::Union(_) => {
|
Layout::Union(_) => {
|
||||||
|
@ -7726,8 +7731,8 @@ where
|
||||||
closure_tag_id_symbol,
|
closure_tag_id_symbol,
|
||||||
Layout::Builtin(crate::layout::TAG_SIZE),
|
Layout::Builtin(crate::layout::TAG_SIZE),
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
|
lambda_set.is_represented(),
|
||||||
to_lowlevel_call,
|
to_lowlevel_call,
|
||||||
function_layout,
|
|
||||||
return_layout,
|
return_layout,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
|
@ -7755,7 +7760,7 @@ where
|
||||||
let call = to_lowlevel_call(
|
let call = to_lowlevel_call(
|
||||||
function_symbol,
|
function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
function_layout,
|
lambda_set.is_represented(),
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7770,8 +7775,8 @@ where
|
||||||
closure_tag_id_symbol,
|
closure_tag_id_symbol,
|
||||||
Layout::Builtin(Builtin::Int1),
|
Layout::Builtin(Builtin::Int1),
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
|
lambda_set.is_represented(),
|
||||||
to_lowlevel_call,
|
to_lowlevel_call,
|
||||||
function_layout,
|
|
||||||
return_layout,
|
return_layout,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
|
@ -7786,8 +7791,8 @@ where
|
||||||
closure_tag_id_symbol,
|
closure_tag_id_symbol,
|
||||||
Layout::Builtin(Builtin::Int8),
|
Layout::Builtin(Builtin::Int8),
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
|
lambda_set.is_represented(),
|
||||||
to_lowlevel_call,
|
to_lowlevel_call,
|
||||||
function_layout,
|
|
||||||
return_layout,
|
return_layout,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
|
@ -7804,14 +7809,14 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
closure_tag_id_symbol: Symbol,
|
closure_tag_id_symbol: Symbol,
|
||||||
closure_tag_id_layout: Layout<'a>,
|
closure_tag_id_layout: Layout<'a>,
|
||||||
closure_data_symbol: Symbol,
|
closure_data_symbol: Symbol,
|
||||||
|
closure_env_layout: Option<Layout<'a>>,
|
||||||
to_lowlevel_call: ToLowLevelCall,
|
to_lowlevel_call: ToLowLevelCall,
|
||||||
function_layout: Layout<'a>,
|
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -7828,7 +7833,7 @@ where
|
||||||
let call = to_lowlevel_call(
|
let call = to_lowlevel_call(
|
||||||
*function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
function_layout,
|
closure_env_layout,
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
);
|
||||||
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
||||||
|
@ -7859,7 +7864,7 @@ where
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
id: join_point_id,
|
id: join_point_id,
|
||||||
parameters: &*env.arena.alloc([param]),
|
parameters: &*env.arena.alloc([param]),
|
||||||
continuation: hole,
|
body: hole,
|
||||||
remainder: env.arena.alloc(switch),
|
remainder: env.arena.alloc(switch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8017,7 +8022,7 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
id: join_point_id,
|
id: join_point_id,
|
||||||
parameters: &*env.arena.alloc([param]),
|
parameters: &*env.arena.alloc([param]),
|
||||||
continuation: hole,
|
body: hole,
|
||||||
remainder: env.arena.alloc(switch),
|
remainder: env.arena.alloc(switch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8161,7 +8166,7 @@ fn enum_lambda_set_to_switch<'a>(
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
id: join_point_id,
|
id: join_point_id,
|
||||||
parameters: &*env.arena.alloc([param]),
|
parameters: &*env.arena.alloc([param]),
|
||||||
continuation: hole,
|
body: hole,
|
||||||
remainder: env.arena.alloc(switch),
|
remainder: env.arena.alloc(switch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8229,14 +8234,14 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
closure_tag_id_symbol: Symbol,
|
closure_tag_id_symbol: Symbol,
|
||||||
closure_tag_id_layout: Layout<'a>,
|
closure_tag_id_layout: Layout<'a>,
|
||||||
closure_data_symbol: Symbol,
|
closure_data_symbol: Symbol,
|
||||||
|
closure_env_layout: Option<Layout<'a>>,
|
||||||
to_lowlevel_call: ToLowLevelCall,
|
to_lowlevel_call: ToLowLevelCall,
|
||||||
function_layout: Layout<'a>,
|
|
||||||
return_layout: Layout<'a>,
|
return_layout: Layout<'a>,
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Layout<'a>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -8253,7 +8258,7 @@ where
|
||||||
let call = to_lowlevel_call(
|
let call = to_lowlevel_call(
|
||||||
*function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
function_layout,
|
closure_env_layout,
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
);
|
||||||
let stmt = build_call(
|
let stmt = build_call(
|
||||||
|
@ -8290,7 +8295,7 @@ where
|
||||||
Stmt::Join {
|
Stmt::Join {
|
||||||
id: join_point_id,
|
id: join_point_id,
|
||||||
parameters: &*env.arena.alloc([param]),
|
parameters: &*env.arena.alloc([param]),
|
||||||
continuation: hole,
|
body: hole,
|
||||||
remainder: env.arena.alloc(switch),
|
remainder: env.arena.alloc(switch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,14 @@ impl<'a> LambdaSet<'a> {
|
||||||
*self.representation
|
*self.representation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_represented(&self) -> Option<Layout<'a>> {
|
||||||
|
if let Layout::Struct(&[]) = self.representation {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(*self.representation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> {
|
pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.set.iter().any(|(s, _)| *s == function_symbol),
|
self.set.iter().any(|(s, _)| *s == function_symbol),
|
||||||
|
@ -281,7 +289,9 @@ impl<'a> LambdaSet<'a> {
|
||||||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||||
Unwrapped(_tag_name, layouts) => Layout::Struct(layouts.into_bump_slice()),
|
Unwrapped {
|
||||||
|
arguments: layouts, ..
|
||||||
|
} => Layout::Struct(layouts.into_bump_slice()),
|
||||||
Wrapped(variant) => {
|
Wrapped(variant) => {
|
||||||
use WrappedVariant::*;
|
use WrappedVariant::*;
|
||||||
|
|
||||||
|
@ -1266,9 +1276,15 @@ pub enum UnionVariant<'a> {
|
||||||
Never,
|
Never,
|
||||||
Unit,
|
Unit,
|
||||||
UnitWithArguments,
|
UnitWithArguments,
|
||||||
BoolUnion { ttrue: TagName, ffalse: TagName },
|
BoolUnion {
|
||||||
|
ttrue: TagName,
|
||||||
|
ffalse: TagName,
|
||||||
|
},
|
||||||
ByteUnion(Vec<'a, TagName>),
|
ByteUnion(Vec<'a, TagName>),
|
||||||
Unwrapped(TagName, Vec<'a, Layout<'a>>),
|
Unwrapped {
|
||||||
|
tag_name: TagName,
|
||||||
|
arguments: Vec<'a, Layout<'a>>,
|
||||||
|
},
|
||||||
Wrapped(WrappedVariant<'a>),
|
Wrapped(WrappedVariant<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1486,7 +1502,10 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
fields: layouts.into_bump_slice(),
|
fields: layouts.into_bump_slice(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
UnionVariant::Unwrapped(tag_name, layouts)
|
UnionVariant::Unwrapped {
|
||||||
|
tag_name,
|
||||||
|
arguments: layouts,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
num_tags => {
|
num_tags => {
|
||||||
|
@ -1638,7 +1657,10 @@ pub fn layout_from_tag_union<'a>(
|
||||||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||||
Unwrapped(_, mut field_layouts) => {
|
Unwrapped {
|
||||||
|
arguments: mut field_layouts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if field_layouts.len() == 1 {
|
if field_layouts.len() == 1 {
|
||||||
field_layouts.pop().unwrap()
|
field_layouts.pop().unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub fn make_tail_recursive<'a>(
|
||||||
id,
|
id,
|
||||||
remainder: jump,
|
remainder: jump,
|
||||||
parameters: params,
|
parameters: params,
|
||||||
continuation: new,
|
body: new,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ fn insert_jumps<'a>(
|
||||||
id,
|
id,
|
||||||
parameters,
|
parameters,
|
||||||
remainder,
|
remainder,
|
||||||
continuation,
|
body: continuation,
|
||||||
} => {
|
} => {
|
||||||
let opt_remainder = insert_jumps(arena, remainder, goal_id, needle);
|
let opt_remainder = insert_jumps(arena, remainder, goal_id, needle);
|
||||||
let opt_continuation = insert_jumps(arena, continuation, goal_id, needle);
|
let opt_continuation = insert_jumps(arena, continuation, goal_id, needle);
|
||||||
|
@ -173,7 +173,7 @@ fn insert_jumps<'a>(
|
||||||
id: *id,
|
id: *id,
|
||||||
parameters,
|
parameters,
|
||||||
remainder,
|
remainder,
|
||||||
continuation,
|
body: continuation,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -6,7 +6,5 @@ platform examples/quicksort
|
||||||
provides [ mainForHost ]
|
provides [ mainForHost ]
|
||||||
effects fx.Effect {}
|
effects fx.Effect {}
|
||||||
|
|
||||||
update : Model -> Model
|
|
||||||
|
|
||||||
mainForHost : List I64 -> List I64
|
mainForHost : List I64 -> List I64
|
||||||
mainForHost = \list -> quicksort list
|
mainForHost = \list -> quicksort list
|
||||||
|
|
5
vendor/morphic_lib/src/api.rs
vendored
5
vendor/morphic_lib/src/api.rs
vendored
|
@ -2,6 +2,7 @@ use sha2::{digest::Digest, Sha256};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::{btree_map::Entry, BTreeMap};
|
use std::collections::{btree_map::Entry, BTreeMap};
|
||||||
|
|
||||||
|
use crate::preprocess;
|
||||||
use crate::render_api_ir;
|
use crate::render_api_ir;
|
||||||
use crate::util::blocks::Blocks;
|
use crate::util::blocks::Blocks;
|
||||||
use crate::util::id_bi_map::IdBiMap;
|
use crate::util::id_bi_map::IdBiMap;
|
||||||
|
@ -57,6 +58,8 @@ enum ErrorKind {
|
||||||
ModNotFound(ModNameBuf),
|
ModNotFound(ModNameBuf),
|
||||||
#[error("entry point {0:?} not found in program")]
|
#[error("entry point {0:?} not found in program")]
|
||||||
EntryPointNotFound(EntryPointNameBuf),
|
EntryPointNotFound(EntryPointNameBuf),
|
||||||
|
#[error("{0}")]
|
||||||
|
PreprocessError(preprocess::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, thiserror::Error, Debug)]
|
#[derive(Clone, thiserror::Error, Debug)]
|
||||||
|
@ -1550,6 +1553,8 @@ fn populate_specs(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(program: Program) -> Result<Solutions> {
|
pub fn solve(program: Program) -> Result<Solutions> {
|
||||||
|
preprocess::preprocess(&program).map_err(ErrorKind::PreprocessError)?;
|
||||||
|
|
||||||
Ok(Solutions {
|
Ok(Solutions {
|
||||||
mods: program
|
mods: program
|
||||||
.mods
|
.mods
|
||||||
|
|
303
vendor/morphic_lib/src/ir.rs
vendored
Normal file
303
vendor/morphic_lib/src/ir.rs
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
//! The core IR used for analysis.
|
||||||
|
//!
|
||||||
|
//! The IR uses a variant of SSA in which control-dependent values are represented using block
|
||||||
|
//! parameters instead of phi nodes.
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::api::{CalleeSpecVarId, UpdateModeVarId};
|
||||||
|
use crate::name_cache::{ConstId, FuncId};
|
||||||
|
use crate::type_cache::TypeId;
|
||||||
|
use crate::util::blocks::Blocks;
|
||||||
|
use crate::util::flat_slices::{FlatSlices, Slice};
|
||||||
|
use crate::util::op_graph::OpGraph;
|
||||||
|
use crate::util::strongly_connected::{strongly_connected, SccKind};
|
||||||
|
|
||||||
|
/// The "core payload" of a node in the op graph, determining its semantics.
|
||||||
|
///
|
||||||
|
/// The `OpKind` of a node is *not* sufficient to uniquely determine either the ids or the types of
|
||||||
|
/// its inputs or output. This information is stored separately in the graph.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) enum OpKind {
|
||||||
|
// Variadic inputs
|
||||||
|
// Inputs may have any type
|
||||||
|
// Output may have any type
|
||||||
|
UnknownWith,
|
||||||
|
// 1 input
|
||||||
|
// Input type must match argument type of callee
|
||||||
|
// Output type must match return type of callee
|
||||||
|
Call {
|
||||||
|
callee_spec_var: CalleeSpecVarId,
|
||||||
|
callee: FuncId,
|
||||||
|
},
|
||||||
|
// 0 inputs
|
||||||
|
// Output type must match type of const
|
||||||
|
ConstRef {
|
||||||
|
const_: ConstId,
|
||||||
|
},
|
||||||
|
// 0 inputs
|
||||||
|
// Output type is heap cell
|
||||||
|
NewHeapCell,
|
||||||
|
// 1 input
|
||||||
|
// Input may have any type
|
||||||
|
// Output type is unit
|
||||||
|
RecursiveTouch,
|
||||||
|
// 1 input
|
||||||
|
// Input type is heap cell
|
||||||
|
// Output type is unit
|
||||||
|
UpdateWriteOnly {
|
||||||
|
update_mode_var: UpdateModeVarId,
|
||||||
|
},
|
||||||
|
// 0 inputs
|
||||||
|
// Output type is `Bag<T>` for some `T`
|
||||||
|
EmptyBag,
|
||||||
|
// 2 inputs: `(bag, to_insert)`
|
||||||
|
// `bag` has type `Bag<T>`
|
||||||
|
// `to_insert` has type `T`
|
||||||
|
// Output type is `Bag<T>`
|
||||||
|
BagInsert,
|
||||||
|
// 1 input
|
||||||
|
// Input type is `Bag<T>`
|
||||||
|
// Output type is `T`
|
||||||
|
BagGet,
|
||||||
|
// 1 input
|
||||||
|
// Input type is `Bag<T>`
|
||||||
|
// Output type is `(Bag<T>, T)`
|
||||||
|
BagRemove,
|
||||||
|
// Variadic inputs
|
||||||
|
// Input types are `T_1, ..., T_N`
|
||||||
|
// Output type is `(T_1, ..., T_N)`
|
||||||
|
MakeTuple,
|
||||||
|
// 1 input
|
||||||
|
// Input type is `(T_1, ..., T_N)`
|
||||||
|
// Output type is `T_(field_idx)`
|
||||||
|
GetTupleField {
|
||||||
|
field_idx: u32,
|
||||||
|
},
|
||||||
|
// 1 input
|
||||||
|
// Input type is `T_(variant_idx)`
|
||||||
|
// Output type is `union { T_1, ..., T_N }`
|
||||||
|
MakeUnion {
|
||||||
|
variant_idx: u32,
|
||||||
|
},
|
||||||
|
// 1 input
|
||||||
|
// Input type is `union { T_1, ..., T_N }`
|
||||||
|
// Output type is `T_(variant_idx)`
|
||||||
|
UnwrapUnion {
|
||||||
|
variant_idx: u32,
|
||||||
|
},
|
||||||
|
// 1 input
|
||||||
|
// Input type is content type of a named type `T`
|
||||||
|
// Output type is `T`
|
||||||
|
MakeNamed,
|
||||||
|
// 1 input
|
||||||
|
// Input type is a named type `T`
|
||||||
|
// Output type is the content type of `T`
|
||||||
|
UnwrapNamed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) enum ValueKind {
|
||||||
|
Op(OpKind),
|
||||||
|
// 0 inputs
|
||||||
|
BlockParam,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) struct ValueInfo {
|
||||||
|
pub(crate) kind: ValueKind,
|
||||||
|
pub(crate) result_type: TypeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub(crate) ValueId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub(crate) BlockId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) enum JumpTarget {
|
||||||
|
Block(BlockId),
|
||||||
|
Ret,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const JUMP_TARGETS_INLINE_COUNT: usize = 8;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) struct BlockInfo {
|
||||||
|
/// A block may optionally have a single parameter, which serves the same role as a phi node in
|
||||||
|
/// SSA.
|
||||||
|
///
|
||||||
|
/// Invariant: If `param` is `Some`, it must point to a `BlockParam` value, not an `Op`.
|
||||||
|
pub(crate) param: Option<ValueId>,
|
||||||
|
/// List of zero or more jump targets to nondeterministically choose from.
|
||||||
|
pub(crate) jump_targets: SmallVec<[JumpTarget; JUMP_TARGETS_INLINE_COUNT]>,
|
||||||
|
/// Optional argument which will be passed to the chosen jump target.
|
||||||
|
pub(crate) target_arg: Option<ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
/// Identifies a strongly connected component in the control flow graph.
|
||||||
|
///
|
||||||
|
/// https://en.wikipedia.org/wiki/Strongly_connected_component
|
||||||
|
pub(crate) SccId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct GraphBuilder {
|
||||||
|
values: OpGraph<ValueId, ValueInfo>,
|
||||||
|
blocks: Blocks<BlockId, ValueId, BlockInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphBuilder {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
GraphBuilder {
|
||||||
|
values: OpGraph::new(),
|
||||||
|
blocks: Blocks::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_op(
|
||||||
|
&mut self,
|
||||||
|
block: BlockId,
|
||||||
|
op_kind: OpKind,
|
||||||
|
inputs: &[ValueId],
|
||||||
|
result_type: TypeId,
|
||||||
|
) -> ValueId {
|
||||||
|
let val_id = self.values.add_node(
|
||||||
|
ValueInfo {
|
||||||
|
kind: ValueKind::Op(op_kind),
|
||||||
|
result_type,
|
||||||
|
},
|
||||||
|
inputs,
|
||||||
|
);
|
||||||
|
self.blocks.add_value(block, val_id);
|
||||||
|
val_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_block(&mut self) -> BlockId {
|
||||||
|
self.blocks.add_block(
|
||||||
|
self.values.count().0,
|
||||||
|
BlockInfo {
|
||||||
|
param: None,
|
||||||
|
jump_targets: SmallVec::new(),
|
||||||
|
target_arg: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_block_with_param(&mut self, param_type: TypeId) -> (BlockId, ValueId) {
|
||||||
|
let param_id = self.values.add_node(
|
||||||
|
ValueInfo {
|
||||||
|
kind: ValueKind::BlockParam,
|
||||||
|
result_type: param_type,
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
let block_id = self.blocks.add_block(
|
||||||
|
self.values.count().0,
|
||||||
|
BlockInfo {
|
||||||
|
param: Some(param_id),
|
||||||
|
jump_targets: SmallVec::new(),
|
||||||
|
target_arg: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
(block_id, param_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_jump_targets(
|
||||||
|
&mut self,
|
||||||
|
block: BlockId,
|
||||||
|
target_arg: Option<ValueId>,
|
||||||
|
jump_targets: SmallVec<[JumpTarget; JUMP_TARGETS_INLINE_COUNT]>,
|
||||||
|
) {
|
||||||
|
let info = self.blocks.block_info_mut(block);
|
||||||
|
info.target_arg = target_arg;
|
||||||
|
info.jump_targets = jump_targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn values(&self) -> &OpGraph<ValueId, ValueInfo> {
|
||||||
|
&self.values
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn blocks(&self) -> &Blocks<BlockId, ValueId, BlockInfo> {
|
||||||
|
&self.blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build(self, entry_block: BlockId) -> Graph {
|
||||||
|
debug_assert!(entry_block < self.blocks.block_count());
|
||||||
|
let rev_sccs = strongly_connected(self.blocks.block_count(), |block| {
|
||||||
|
self.blocks
|
||||||
|
.block_info(block)
|
||||||
|
.jump_targets
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&jump_target| match jump_target {
|
||||||
|
JumpTarget::Ret => None,
|
||||||
|
JumpTarget::Block(target) => Some(target),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
Graph {
|
||||||
|
values: self.values,
|
||||||
|
blocks: self.blocks,
|
||||||
|
entry_block,
|
||||||
|
rev_sccs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct Graph {
|
||||||
|
values: OpGraph<ValueId, ValueInfo>,
|
||||||
|
blocks: Blocks<BlockId, ValueId, BlockInfo>,
|
||||||
|
entry_block: BlockId,
|
||||||
|
|
||||||
|
// Invariant: `rev_sccs` must be stored in *reverse* topological order. If an SCC 'A' can jump
|
||||||
|
// to an SCC 'B', then 'A' must appear *after* 'B' in `rev_sccs`.
|
||||||
|
//
|
||||||
|
// We don't store the SCCs in topological order because control flow graph edges point from
|
||||||
|
// *source block* to *target block*, so running Tarjan's algorithm on the control flow graph
|
||||||
|
// gives us a reverse topological sort rather than a topological sort.
|
||||||
|
rev_sccs: FlatSlices<SccId, SccKind, BlockId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Graph {
|
||||||
|
pub(crate) fn values(&self) -> &OpGraph<ValueId, ValueInfo> {
|
||||||
|
&self.values
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn blocks(&self) -> &Blocks<BlockId, ValueId, BlockInfo> {
|
||||||
|
&self.blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn entry_block(&self) -> BlockId {
|
||||||
|
self.entry_block
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn rev_sccs(&self) -> &FlatSlices<SccId, SccKind, BlockId> {
|
||||||
|
&self.rev_sccs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over sccs in topological order.
|
||||||
|
///
|
||||||
|
/// IF an SCC 'A' can jump to an SCC 'B', then 'A' is guaranteed to appear *before* 'B' in the
|
||||||
|
/// returned iterator.
|
||||||
|
pub(crate) fn iter_sccs(&self) -> impl Iterator<Item = Slice<'_, SccKind, BlockId>> + '_ {
|
||||||
|
self.rev_sccs
|
||||||
|
.count()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(move |scc_id| self.rev_sccs.get(scc_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct FuncDef {
|
||||||
|
pub(crate) graph: Graph,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ConstDef {
|
||||||
|
pub(crate) graph: Graph,
|
||||||
|
}
|
5
vendor/morphic_lib/src/lib.rs
vendored
5
vendor/morphic_lib/src/lib.rs
vendored
|
@ -1,10 +1,15 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
mod ir;
|
||||||
|
mod name_cache;
|
||||||
|
mod preprocess;
|
||||||
mod render_api_ir;
|
mod render_api_ir;
|
||||||
|
mod type_cache;
|
||||||
|
|
||||||
pub use api::*;
|
pub use api::*;
|
||||||
|
|
26
vendor/morphic_lib/src/name_cache.rs
vendored
Normal file
26
vendor/morphic_lib/src/name_cache.rs
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::api;
|
||||||
|
use crate::util::id_bi_map::IdBiMap;
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub NamedTypeId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub FuncId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub ConstId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub EntryPointId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub(crate) struct NameCache {
|
||||||
|
pub(crate) named_types: IdBiMap<NamedTypeId, (api::ModNameBuf, api::TypeNameBuf)>,
|
||||||
|
pub(crate) funcs: IdBiMap<FuncId, (api::ModNameBuf, api::FuncNameBuf)>,
|
||||||
|
pub(crate) consts: IdBiMap<ConstId, (api::ModNameBuf, api::ConstNameBuf)>,
|
||||||
|
pub(crate) entry_points: IdBiMap<EntryPointId, api::EntryPointNameBuf>,
|
||||||
|
}
|
1204
vendor/morphic_lib/src/preprocess.rs
vendored
Normal file
1204
vendor/morphic_lib/src/preprocess.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
22
vendor/morphic_lib/src/type_cache.rs
vendored
Normal file
22
vendor/morphic_lib/src/type_cache.rs
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::name_cache::NamedTypeId;
|
||||||
|
use crate::util::id_bi_map::IdBiMap;
|
||||||
|
|
||||||
|
id_type! {
|
||||||
|
pub TypeId(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum TypeData {
|
||||||
|
Named { named: NamedTypeId },
|
||||||
|
Tuple { fields: SmallVec<[TypeId; 10]> },
|
||||||
|
Union { variants: SmallVec<[TypeId; 10]> },
|
||||||
|
HeapCell,
|
||||||
|
Bag { item: TypeId },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct TypeCache {
|
||||||
|
pub types: IdBiMap<TypeId, TypeData>,
|
||||||
|
}
|
4
vendor/morphic_lib/src/util/blocks.rs
vendored
4
vendor/morphic_lib/src/util/blocks.rs
vendored
|
@ -75,8 +75,8 @@ impl<BlockId: Id, ValId: Id, BlockInfo> Blocks<BlockId, ValId, BlockInfo> {
|
||||||
tail_frag.max_val = next_val;
|
tail_frag.max_val = next_val;
|
||||||
} else {
|
} else {
|
||||||
let new_tail = BlockFrag {
|
let new_tail = BlockFrag {
|
||||||
min_val: val_id,
|
min_val: val_id.clone(),
|
||||||
max_val: next_val,
|
max_val: ValId::from_index_or_panic(val_id.to_index() + 1),
|
||||||
next: None,
|
next: None,
|
||||||
};
|
};
|
||||||
let new_tail_id = self.frags.push(new_tail);
|
let new_tail_id = self.frags.push(new_tail);
|
||||||
|
|
32
vendor/morphic_lib/src/util/bytes_id.rs
vendored
32
vendor/morphic_lib/src/util/bytes_id.rs
vendored
|
@ -4,15 +4,15 @@ macro_rules! bytes_id {
|
||||||
$(#[$annot_borrowed:meta])* $borrowed_vis:vis $borrowed:ident;
|
$(#[$annot_borrowed:meta])* $borrowed_vis:vis $borrowed:ident;
|
||||||
$(#[$annot_owned:meta])* $owned_vis:vis $owned:ident;
|
$(#[$annot_owned:meta])* $owned_vis:vis $owned:ident;
|
||||||
) => {
|
) => {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
$(#[$annot_borrowed])*
|
$(#[$annot_borrowed])*
|
||||||
$borrowed_vis struct $borrowed<'a>($borrowed_vis &'a [u8]);
|
$borrowed_vis struct $borrowed<'a>($borrowed_vis &'a [u8]);
|
||||||
|
|
||||||
// The stack-allocated array portion of a `SmallVec` shares a union with two `usize`s, so on
|
// On 64-bit platforms we can store up to `23` bytes inline in a `SmallVec` with no space
|
||||||
// 64-bit platforms we can make the array up to 16 bytes long with no space penalty.
|
// penalty.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
$(#[$annot_owned])*
|
$(#[$annot_owned])*
|
||||||
$owned_vis struct $owned($owned_vis ::smallvec::SmallVec<[u8; 16]>);
|
$owned_vis struct $owned($owned_vis ::smallvec::SmallVec<[u8; 23]>);
|
||||||
|
|
||||||
impl $owned {
|
impl $owned {
|
||||||
fn borrowed<'a>(&'a self) -> $borrowed<'a> {
|
fn borrowed<'a>(&'a self) -> $borrowed<'a> {
|
||||||
|
@ -25,5 +25,27 @@ macro_rules! bytes_id {
|
||||||
$owned(::smallvec::SmallVec::from_slice(&borrowed.0))
|
$owned(::smallvec::SmallVec::from_slice(&borrowed.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::fmt::Debug for $borrowed<'a> {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
// TODO: Consolidate this with render_api_ir.rs
|
||||||
|
write!(f, "{}(\"", stringify!($borrowed))?;
|
||||||
|
for &byte in self.0 {
|
||||||
|
write!(f, "{}", ::std::ascii::escape_default(byte))?;
|
||||||
|
}
|
||||||
|
write!(f, "\")")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Debug for $owned {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
// We intentionally use the name of $borrowed to render values of type $owned,
|
||||||
|
// because only the borrowed version of each bytestring-based identifier type are
|
||||||
|
// exposed/documented in the public API, and we use this debug formatting logic to
|
||||||
|
// render public-facing error messages.
|
||||||
|
write!(f, "{:?}", self.borrowed())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
112
vendor/morphic_lib/src/util/flat_slices.rs
vendored
Normal file
112
vendor/morphic_lib/src/util/flat_slices.rs
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::util::id_type::{self, Count, Id};
|
||||||
|
use crate::util::id_vec::IdVec;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
struct SliceData<SliceInfo> {
|
||||||
|
info: SliceInfo,
|
||||||
|
|
||||||
|
/// Determines which slice of the `flat_data` buffer contains the items of this slice.
|
||||||
|
///
|
||||||
|
/// If the *previous* slice has `slice_end_idx == a`, and this slice has `inputs_end_idx == b`,
|
||||||
|
/// then the items of this slice are given by `flat_data[a..b]`.
|
||||||
|
slice_end_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Conceptually represents a collection of the form `IdVec<SliceId, (SliceInfo, Vec<T>)>`.
|
||||||
|
///
|
||||||
|
/// The notional `Vec<T>` values are actually stored in a single contiguous buffer to reduce the
|
||||||
|
/// number of heap allocations. Because these values are all different slices of the same
|
||||||
|
/// underlying buffer, we refer to each tuple `(SliceInfo, Vec<T>)` as a "slice" for the purposes of
|
||||||
|
/// this data structure's API.
|
||||||
|
///
|
||||||
|
/// The `SliceInfo` parameter should be regarded as optional, and for some purposes it is perfectly
|
||||||
|
/// fine (stylistically speaking) to set `SliceInfo = ()`.
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct FlatSlices<SliceId: Id, SliceInfo, T> {
|
||||||
|
flat_data: Vec<T>,
|
||||||
|
slices: IdVec<SliceId, SliceData<SliceInfo>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Slice<'a, SliceInfo, T> {
|
||||||
|
pub info: &'a SliceInfo,
|
||||||
|
pub items: &'a [T],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct SliceMut<'a, SliceInfo, T> {
|
||||||
|
pub info: &'a mut SliceInfo,
|
||||||
|
pub items: &'a mut [T],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SliceId: Id, SliceInfo, T> Default for FlatSlices<SliceId, SliceInfo, T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SliceId: Id, SliceInfo, T> FlatSlices<SliceId, SliceInfo, T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
flat_data: Vec::new(),
|
||||||
|
slices: IdVec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.slices.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> Count<SliceId> {
|
||||||
|
self.slices.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_slice(&mut self, info: SliceInfo, slice: &[T]) -> SliceId
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
self.flat_data.extend_from_slice(slice);
|
||||||
|
self.slices.push(SliceData {
|
||||||
|
info,
|
||||||
|
slice_end_idx: self.flat_data.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_range(&self, idx: SliceId) -> (usize, usize) {
|
||||||
|
let start = match id_type::decrement(idx.clone()) {
|
||||||
|
None => 0,
|
||||||
|
Some(prev_idx) => self.slices[prev_idx].slice_end_idx,
|
||||||
|
};
|
||||||
|
let end = self.slices[idx].slice_end_idx;
|
||||||
|
(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<I: Borrow<SliceId>>(&self, idx: I) -> Slice<SliceInfo, T> {
|
||||||
|
let (start, end) = self.data_range(idx.borrow().clone());
|
||||||
|
Slice {
|
||||||
|
info: &self.slices[idx].info,
|
||||||
|
items: &self.flat_data[start..end],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut<I: Borrow<SliceId>>(&mut self, idx: I) -> SliceMut<SliceInfo, T> {
|
||||||
|
let (start, end) = self.data_range(idx.borrow().clone());
|
||||||
|
SliceMut {
|
||||||
|
info: &mut self.slices[idx].info,
|
||||||
|
items: &mut self.flat_data[start..end],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SliceId: Id + fmt::Debug, SliceInfo: fmt::Debug, T: fmt::Debug> fmt::Debug
|
||||||
|
for FlatSlices<SliceId, SliceInfo, T>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_map()
|
||||||
|
.entries(self.count().iter().map(|i| (i.clone(), self.get(i))))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
12
vendor/morphic_lib/src/util/id_bi_map.rs
vendored
12
vendor/morphic_lib/src/util/id_bi_map.rs
vendored
|
@ -52,6 +52,18 @@ impl<K: Id, V: Hash + Eq + Clone> IdBiMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a value into the bi-map, or get its key if it is already present.
|
||||||
|
pub fn get_or_insert(&mut self, val: V) -> K {
|
||||||
|
match self.val_to_key.entry(val) {
|
||||||
|
Entry::Occupied(occupied) => occupied.get().clone(),
|
||||||
|
Entry::Vacant(vacant) => {
|
||||||
|
let new_index = self.key_to_val.push(vacant.key().clone());
|
||||||
|
vacant.insert(new_index.clone());
|
||||||
|
new_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_by_val(&self, val: &V) -> Option<K> {
|
pub fn get_by_val(&self, val: &V) -> Option<K> {
|
||||||
self.val_to_key.get(val).cloned()
|
self.val_to_key.get(val).cloned()
|
||||||
}
|
}
|
||||||
|
|
9
vendor/morphic_lib/src/util/id_type.rs
vendored
9
vendor/morphic_lib/src/util/id_type.rs
vendored
|
@ -98,7 +98,14 @@ impl<T: PartialOrd> PartialOrd<T> for Count<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Id> Count<T> {
|
impl<T: Id> Count<T> {
|
||||||
pub fn iter(&self) -> impl Iterator<Item = T> {
|
pub fn iter(&self) -> impl DoubleEndedIterator<Item = T> {
|
||||||
(0..self.0.to_index()).map(T::from_index_unchecked)
|
(0..self.0.to_index()).map(T::from_index_unchecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decrement<T: Id>(id: T) -> Option<T> {
|
||||||
|
match id.to_index() {
|
||||||
|
0 => None,
|
||||||
|
index => Some(T::from_index_unchecked(index - 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
vendor/morphic_lib/src/util/id_vec.rs
vendored
7
vendor/morphic_lib/src/util/id_vec.rs
vendored
|
@ -107,6 +107,13 @@ impl<K: Id, V> IdVec<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
IdVec {
|
||||||
|
key: PhantomData,
|
||||||
|
items: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_items(items: Vec<V>) -> Self {
|
pub fn from_items(items: Vec<V>) -> Self {
|
||||||
K::assert_in_range(items.len());
|
K::assert_in_range(items.len());
|
||||||
IdVec {
|
IdVec {
|
||||||
|
|
2
vendor/morphic_lib/src/util/mod.rs
vendored
2
vendor/morphic_lib/src/util/mod.rs
vendored
|
@ -8,7 +8,9 @@ pub mod bytes_id;
|
||||||
pub mod forward_trait;
|
pub mod forward_trait;
|
||||||
|
|
||||||
pub mod blocks;
|
pub mod blocks;
|
||||||
|
pub mod flat_slices;
|
||||||
pub mod id_bi_map;
|
pub mod id_bi_map;
|
||||||
pub mod id_vec;
|
pub mod id_vec;
|
||||||
pub mod op_graph;
|
pub mod op_graph;
|
||||||
pub mod replace_none;
|
pub mod replace_none;
|
||||||
|
pub mod strongly_connected;
|
||||||
|
|
51
vendor/morphic_lib/src/util/op_graph.rs
vendored
51
vendor/morphic_lib/src/util/op_graph.rs
vendored
|
@ -1,18 +1,7 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use crate::util::flat_slices::{FlatSlices, Slice};
|
||||||
use crate::util::id_type::{Count, Id};
|
use crate::util::id_type::{Count, Id};
|
||||||
use crate::util::id_vec::IdVec;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
struct NodeData<Op> {
|
|
||||||
op: Op,
|
|
||||||
|
|
||||||
/// Determines which slice of the `flat_inputs` buffer contains the inputs to this node.
|
|
||||||
///
|
|
||||||
/// If the *previous* op has `inputs_end_idx == a`, and this node has `inputs_end_idx == b`,
|
|
||||||
/// then the inputs to this node are given by `flat_inputs[a..b]`.
|
|
||||||
inputs_end_idx: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Node<'a, K, Op> {
|
pub struct Node<'a, K, Op> {
|
||||||
|
@ -27,47 +16,45 @@ pub struct Node<'a, K, Op> {
|
||||||
///
|
///
|
||||||
/// The input lists are actually stored in a single contiguous buffer to reduce the number of heap
|
/// The input lists are actually stored in a single contiguous buffer to reduce the number of heap
|
||||||
/// allocations.
|
/// allocations.
|
||||||
|
///
|
||||||
|
/// This is essentially just a newtype wrapper around `FlatSlices`. We use this wrapper to
|
||||||
|
/// represent op graphs (instead of using `FlatSlices` directly) just so that the names of types,
|
||||||
|
/// functions, and fields in the API are more meaningful for this use case.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct OpGraph<K: Id, Op> {
|
pub struct OpGraph<K: Id, Op> {
|
||||||
flat_inputs: Vec<K>,
|
inner: FlatSlices<K, Op, K>,
|
||||||
nodes: IdVec<K, NodeData<Op>>,
|
}
|
||||||
|
|
||||||
|
impl<K: Id, Op> Default for OpGraph<K, Op> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Id, Op> OpGraph<K, Op> {
|
impl<K: Id, Op> OpGraph<K, Op> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
flat_inputs: Vec::new(),
|
inner: FlatSlices::new(),
|
||||||
nodes: IdVec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.nodes.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> Count<K> {
|
pub fn count(&self) -> Count<K> {
|
||||||
self.nodes.count()
|
self.inner.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_node(&mut self, op: Op, inputs: &[K]) -> K {
|
pub fn add_node(&mut self, op: Op, inputs: &[K]) -> K {
|
||||||
self.flat_inputs.extend_from_slice(inputs);
|
self.inner.push_slice(op, inputs)
|
||||||
let node = NodeData {
|
|
||||||
op,
|
|
||||||
inputs_end_idx: self.flat_inputs.len(),
|
|
||||||
};
|
|
||||||
self.nodes.push(node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node<I: Borrow<K>>(&self, idx: I) -> Node<K, Op> {
|
pub fn node<I: Borrow<K>>(&self, idx: I) -> Node<K, Op> {
|
||||||
let node = &self.nodes[idx.borrow()];
|
let Slice { info, items } = self.inner.get(idx);
|
||||||
let inputs_start_idx = if idx.borrow().to_index() == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.nodes[K::from_index_unchecked(idx.borrow().to_index() - 1)].inputs_end_idx
|
|
||||||
};
|
|
||||||
Node {
|
Node {
|
||||||
op: &node.op,
|
op: info,
|
||||||
inputs: &self.flat_inputs[inputs_start_idx..node.inputs_end_idx],
|
inputs: items,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
153
vendor/morphic_lib/src/util/strongly_connected.rs
vendored
Normal file
153
vendor/morphic_lib/src/util/strongly_connected.rs
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
use crate::util::flat_slices::FlatSlices;
|
||||||
|
use crate::util::id_type::{Count, Id};
|
||||||
|
use crate::util::id_vec::IdVec;
|
||||||
|
use crate::util::replace_none::replace_none;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum SccKind {
|
||||||
|
Acyclic,
|
||||||
|
Cyclic,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn strongly_connected<SccId, NodeId, NodeSuccessors>(
|
||||||
|
node_count: Count<NodeId>,
|
||||||
|
mut node_successors: impl FnMut(NodeId) -> NodeSuccessors,
|
||||||
|
) -> FlatSlices<SccId, SccKind, NodeId>
|
||||||
|
where
|
||||||
|
SccId: Id,
|
||||||
|
NodeId: Id + Eq + Copy,
|
||||||
|
NodeSuccessors: Iterator<Item = NodeId>,
|
||||||
|
{
|
||||||
|
// We use Tarjan's algorithm, performing the depth-first search using an explicit Vec-based
|
||||||
|
// stack instead of recursion to avoid stack overflows on large graphs.
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum NodeState {
|
||||||
|
Unvisited,
|
||||||
|
OnSearchStack { index: u32, low_link: u32 },
|
||||||
|
OnSccStack { index: u32 },
|
||||||
|
Complete,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Action<NodeId> {
|
||||||
|
TryVisit {
|
||||||
|
parent: Option<NodeId>,
|
||||||
|
node: NodeId,
|
||||||
|
},
|
||||||
|
FinishVisit {
|
||||||
|
parent: Option<NodeId>,
|
||||||
|
node: NodeId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sccs = FlatSlices::new();
|
||||||
|
|
||||||
|
let mut node_states = IdVec::filled_with(node_count, || NodeState::Unvisited);
|
||||||
|
let mut node_self_loops = IdVec::filled_with(node_count, || None);
|
||||||
|
let mut scc_stack = Vec::new();
|
||||||
|
let mut search_stack = Vec::new();
|
||||||
|
let mut next_index = 0;
|
||||||
|
|
||||||
|
for search_root in node_count.iter() {
|
||||||
|
search_stack.push(Action::TryVisit {
|
||||||
|
parent: None,
|
||||||
|
node: search_root,
|
||||||
|
});
|
||||||
|
while let Some(action) = search_stack.pop() {
|
||||||
|
match action {
|
||||||
|
Action::TryVisit { parent, node } => match node_states[node] {
|
||||||
|
NodeState::Unvisited => {
|
||||||
|
node_states[node] = NodeState::OnSearchStack {
|
||||||
|
index: next_index,
|
||||||
|
low_link: next_index,
|
||||||
|
};
|
||||||
|
next_index += 1;
|
||||||
|
scc_stack.push(node);
|
||||||
|
|
||||||
|
search_stack.push(Action::FinishVisit { parent, node });
|
||||||
|
// We need to explicitly track self-loops so that when we obtain a size-1
|
||||||
|
// SCC we can determine if it's cyclic or acyclic.
|
||||||
|
let mut has_self_loop = false;
|
||||||
|
for successor in node_successors(node) {
|
||||||
|
if successor == node {
|
||||||
|
has_self_loop = true;
|
||||||
|
}
|
||||||
|
search_stack.push(Action::TryVisit {
|
||||||
|
parent: Some(node),
|
||||||
|
node: successor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
replace_none(&mut node_self_loops[node], has_self_loop).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeState::OnSearchStack { index, low_link: _ }
|
||||||
|
| NodeState::OnSccStack { index } => {
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
if let NodeState::OnSearchStack {
|
||||||
|
index: _,
|
||||||
|
low_link: parent_low_link,
|
||||||
|
} = &mut node_states[parent]
|
||||||
|
{
|
||||||
|
*parent_low_link = (*parent_low_link).min(index);
|
||||||
|
} else {
|
||||||
|
unreachable!("parent should be on search stack");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeState::Complete => {}
|
||||||
|
},
|
||||||
|
|
||||||
|
Action::FinishVisit { parent, node } => {
|
||||||
|
let (index, low_link) =
|
||||||
|
if let NodeState::OnSearchStack { index, low_link } = node_states[node] {
|
||||||
|
(index, low_link)
|
||||||
|
} else {
|
||||||
|
unreachable!("node should be on search stack");
|
||||||
|
};
|
||||||
|
|
||||||
|
node_states[node] = NodeState::OnSccStack { index };
|
||||||
|
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
if let NodeState::OnSearchStack {
|
||||||
|
index: _,
|
||||||
|
low_link: parent_low_link,
|
||||||
|
} = &mut node_states[parent]
|
||||||
|
{
|
||||||
|
*parent_low_link = (*parent_low_link).min(low_link);
|
||||||
|
} else {
|
||||||
|
unreachable!("parent should be on search stack")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if low_link == index {
|
||||||
|
let mut scc_start = scc_stack.len();
|
||||||
|
loop {
|
||||||
|
scc_start -= 1;
|
||||||
|
let scc_node = scc_stack[scc_start];
|
||||||
|
debug_assert!(matches!(
|
||||||
|
node_states[scc_node],
|
||||||
|
NodeState::OnSccStack { .. }
|
||||||
|
));
|
||||||
|
node_states[scc_node] = NodeState::Complete;
|
||||||
|
if scc_node == node {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let scc_slice = &scc_stack[scc_start..];
|
||||||
|
let scc_kind = if scc_slice.len() == 1 && !node_self_loops[node].unwrap() {
|
||||||
|
SccKind::Acyclic
|
||||||
|
} else {
|
||||||
|
SccKind::Cyclic
|
||||||
|
};
|
||||||
|
sccs.push_slice(scc_kind, scc_slice);
|
||||||
|
scc_stack.truncate(scc_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sccs
|
||||||
|
}
|
77
vendor/morphic_lib/tests/basic.rs
vendored
Normal file
77
vendor/morphic_lib/tests/basic.rs
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use morphic_lib::{
|
||||||
|
BlockExpr, CalleeSpecVar, EntryPointName, Error, ExprContext, FuncDefBuilder, FuncName,
|
||||||
|
ModDefBuilder, ModName, ProgramBuilder, TypeContext, UpdateModeVar,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic() {
|
||||||
|
fn run() -> Result<(), Error> {
|
||||||
|
let func1_def = {
|
||||||
|
let mut f = FuncDefBuilder::new();
|
||||||
|
let b = f.add_block();
|
||||||
|
f.add_update(b, UpdateModeVar(b"var1"), f.get_argument())?;
|
||||||
|
let ret = f.add_make_tuple(b, &[])?;
|
||||||
|
let arg_type = f.add_heap_cell_type();
|
||||||
|
let ret_type = f.add_tuple_type(&[])?;
|
||||||
|
f.build(arg_type, ret_type, BlockExpr(b, ret))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let func2_def = {
|
||||||
|
let mut f = FuncDefBuilder::new();
|
||||||
|
let b = f.add_block();
|
||||||
|
let cell = f.add_new_heap_cell(b)?;
|
||||||
|
let ret = f.add_call(
|
||||||
|
b,
|
||||||
|
CalleeSpecVar(b"var2"),
|
||||||
|
ModName(b"main_mod"),
|
||||||
|
FuncName(b"func1"),
|
||||||
|
cell,
|
||||||
|
)?;
|
||||||
|
let unit_type = f.add_tuple_type(&[])?;
|
||||||
|
f.build(unit_type, unit_type, BlockExpr(b, ret))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let main_mod = {
|
||||||
|
let mut m = ModDefBuilder::new();
|
||||||
|
m.add_func(FuncName(b"func1"), func1_def)?;
|
||||||
|
m.add_func(FuncName(b"func2"), func2_def)?;
|
||||||
|
m.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let program = {
|
||||||
|
let mut p = ProgramBuilder::new();
|
||||||
|
p.add_mod(ModName(b"main_mod"), main_mod)?;
|
||||||
|
p.add_entry_point(
|
||||||
|
EntryPointName(b"main"),
|
||||||
|
ModName(b"main_mod"),
|
||||||
|
FuncName(b"func2"),
|
||||||
|
)?;
|
||||||
|
p.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let program_sol = morphic_lib::solve(program)?;
|
||||||
|
|
||||||
|
let (_, _, func2_spec) = program_sol.entry_point_solution(EntryPointName(b"main"))?;
|
||||||
|
|
||||||
|
let main_mod_sol = program_sol.mod_solutions(ModName(b"main_mod"))?;
|
||||||
|
|
||||||
|
let func2_sol = main_mod_sol
|
||||||
|
.func_solutions(FuncName(b"func2"))?
|
||||||
|
.spec(&func2_spec)?;
|
||||||
|
|
||||||
|
let func1_spec = func2_sol.callee_spec(CalleeSpecVar(b"var2"))?;
|
||||||
|
|
||||||
|
let func1_sol = main_mod_sol
|
||||||
|
.func_solutions(FuncName(b"func1"))?
|
||||||
|
.spec(&func1_spec)?;
|
||||||
|
|
||||||
|
let _update_mode = func1_sol.update_mode(UpdateModeVar(b"var1"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = run();
|
||||||
|
if let Err(err) = result {
|
||||||
|
panic!("error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
95
vendor/morphic_lib/tests/recursive.rs
vendored
Normal file
95
vendor/morphic_lib/tests/recursive.rs
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use morphic_lib::{
|
||||||
|
BlockExpr, CalleeSpecVar, EntryPointName, Error, ExprContext, FuncDefBuilder, FuncName,
|
||||||
|
ModDefBuilder, ModName, ProgramBuilder, TypeContext, UpdateModeVar,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recursive() {
|
||||||
|
fn run() -> Result<(), Error> {
|
||||||
|
let rec_def = {
|
||||||
|
let mut f = FuncDefBuilder::new();
|
||||||
|
let b = f.add_block();
|
||||||
|
let arg = f.get_argument();
|
||||||
|
let case1 = {
|
||||||
|
let b = f.add_block();
|
||||||
|
BlockExpr(b, arg)
|
||||||
|
};
|
||||||
|
let case2 = {
|
||||||
|
let b = f.add_block();
|
||||||
|
f.add_update(b, UpdateModeVar(b"mode"), arg)?;
|
||||||
|
let new = f.add_new_heap_cell(b)?;
|
||||||
|
let result = f.add_call(
|
||||||
|
b,
|
||||||
|
CalleeSpecVar(b"call"),
|
||||||
|
ModName(b"main"),
|
||||||
|
FuncName(b"rec"),
|
||||||
|
new,
|
||||||
|
)?;
|
||||||
|
BlockExpr(b, result)
|
||||||
|
};
|
||||||
|
let result = f.add_choice(b, &[case1, case2])?;
|
||||||
|
let heap_cell_type = f.add_heap_cell_type();
|
||||||
|
f.build(heap_cell_type, heap_cell_type, BlockExpr(b, result))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let main_def = {
|
||||||
|
let mut f = FuncDefBuilder::new();
|
||||||
|
let b = f.add_block();
|
||||||
|
let init = f.add_new_heap_cell(b)?;
|
||||||
|
let final_ = f.add_call(
|
||||||
|
b,
|
||||||
|
CalleeSpecVar(b"call"),
|
||||||
|
ModName(b"main"),
|
||||||
|
FuncName(b"rec"),
|
||||||
|
init,
|
||||||
|
)?;
|
||||||
|
f.add_touch(b, final_)?;
|
||||||
|
let result = f.add_make_tuple(b, &[])?;
|
||||||
|
let unit_type = f.add_tuple_type(&[])?;
|
||||||
|
f.build(unit_type, unit_type, BlockExpr(b, result))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let mod_ = {
|
||||||
|
let mut m = ModDefBuilder::new();
|
||||||
|
m.add_func(FuncName(b"rec"), rec_def)?;
|
||||||
|
m.add_func(FuncName(b"main"), main_def)?;
|
||||||
|
m.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let program = {
|
||||||
|
let mut p = ProgramBuilder::new();
|
||||||
|
p.add_mod(ModName(b"main"), mod_)?;
|
||||||
|
p.add_entry_point(
|
||||||
|
EntryPointName(b"entry"),
|
||||||
|
ModName(b"main"),
|
||||||
|
FuncName(b"main"),
|
||||||
|
)?;
|
||||||
|
p.build()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let program_sol = morphic_lib::solve(program)?;
|
||||||
|
|
||||||
|
let (_, _, main_spec) = program_sol.entry_point_solution(EntryPointName(b"entry"))?;
|
||||||
|
|
||||||
|
let main_mod_sol = program_sol.mod_solutions(ModName(b"main"))?;
|
||||||
|
|
||||||
|
let main_sol = main_mod_sol
|
||||||
|
.func_solutions(FuncName(b"main"))?
|
||||||
|
.spec(&main_spec)?;
|
||||||
|
|
||||||
|
let rec_spec = main_sol.callee_spec(CalleeSpecVar(b"call"))?;
|
||||||
|
|
||||||
|
let rec_sol = main_mod_sol
|
||||||
|
.func_solutions(FuncName(b"rec"))?
|
||||||
|
.spec(&rec_spec)?;
|
||||||
|
|
||||||
|
let _update_mode = rec_sol.update_mode(UpdateModeVar(b"mode"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = run();
|
||||||
|
if let Err(err) = result {
|
||||||
|
panic!("error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue