mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Merge remote-tracking branch 'origin/trunk' into specialize-lowlevel
This commit is contained in:
commit
fbb711b2ca
70 changed files with 3513 additions and 126 deletions
614
compiler/mono/src/alias_analysis.rs
Normal file
614
compiler/mono/src/alias_analysis.rs
Normal file
|
@ -0,0 +1,614 @@
|
|||
use morphic_lib::TypeContext;
|
||||
use morphic_lib::{
|
||||
BlockExpr, BlockId, CalleeSpecVar, EntryPointName, ExprContext, FuncDef, FuncDefBuilder,
|
||||
FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, TypeName, UpdateModeVar,
|
||||
ValueId,
|
||||
};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::ir::{Call, CallType, Expr, Literal, ModifyRc, Proc, Stmt};
|
||||
use crate::layout::{Builtin, Layout, ListLayout, UnionLayout};
|
||||
|
||||
// just using one module for now
|
||||
const MOD_LIST: ModName = ModName(b"UserApp");
|
||||
const MOD_APP: ModName = ModName(b"UserApp");
|
||||
|
||||
pub fn spec_program<'a, I>(procs: I) -> Result<morphic_lib::Solutions>
|
||||
where
|
||||
I: Iterator<Item = &'a Proc<'a>>,
|
||||
{
|
||||
let mut main_function = None;
|
||||
let main_module = {
|
||||
let mut m = ModDefBuilder::new();
|
||||
|
||||
for proc in procs {
|
||||
let spec = proc_spec(proc)?;
|
||||
|
||||
m.add_func(FuncName(&proc.name.to_ne_bytes()), spec)?;
|
||||
|
||||
if format!("{:?}", proc.name).contains("mainForHost") {
|
||||
main_function = Some(proc.name);
|
||||
}
|
||||
}
|
||||
|
||||
m.build()?
|
||||
};
|
||||
|
||||
let program = {
|
||||
let mut p = ProgramBuilder::new();
|
||||
p.add_mod(MOD_APP, main_module)?;
|
||||
p.add_entry_point(
|
||||
EntryPointName(b"mainForHost"),
|
||||
MOD_APP,
|
||||
FuncName(&main_function.unwrap().to_ne_bytes()),
|
||||
)?;
|
||||
|
||||
p.build()?
|
||||
};
|
||||
|
||||
morphic_lib::solve(program)
|
||||
}
|
||||
|
||||
fn proc_spec(proc: &Proc) -> Result<FuncDef> {
|
||||
let mut builder = FuncDefBuilder::new();
|
||||
let mut env = Env::default();
|
||||
|
||||
let block = builder.add_block();
|
||||
|
||||
// introduce the arguments
|
||||
let mut argument_layouts = Vec::new();
|
||||
for (i, (layout, symbol)) in proc.args.iter().enumerate() {
|
||||
let value_id = builder.add_get_tuple_field(block, builder.get_argument(), i as u32)?;
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
|
||||
argument_layouts.push(*layout);
|
||||
}
|
||||
|
||||
let value_id = stmt_spec(&mut builder, &mut env, block, &proc.ret_layout, &proc.body)?;
|
||||
|
||||
let root = BlockExpr(block, value_id);
|
||||
let arg_type_id = layout_spec(&mut builder, &Layout::Struct(&argument_layouts))?;
|
||||
let ret_type_id = layout_spec(&mut builder, &proc.ret_layout)?;
|
||||
builder.build(arg_type_id, ret_type_id, root)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Env {
|
||||
symbols: MutMap<Symbol, ValueId>,
|
||||
join_points: MutMap<crate::ir::JoinPointId, morphic_lib::JoinPointId>,
|
||||
}
|
||||
|
||||
fn stmt_spec(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &mut Env,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
stmt: &Stmt,
|
||||
) -> Result<ValueId> {
|
||||
use Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, layout, continuation) => {
|
||||
let value_id = expr_spec(builder, env, block, layout, expr)?;
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
let result = stmt_spec(builder, env, block, layout, continuation)?;
|
||||
env.symbols.remove(symbol);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
Invoke {
|
||||
symbol,
|
||||
call,
|
||||
layout: call_layout,
|
||||
pass,
|
||||
fail,
|
||||
} => {
|
||||
// a call that might throw an exception
|
||||
|
||||
let value_id = call_spec(builder, env, block, call_layout, call)?;
|
||||
|
||||
let pass_block = builder.add_block();
|
||||
env.symbols.insert(*symbol, value_id);
|
||||
let pass_value_id = stmt_spec(builder, env, pass_block, layout, pass)?;
|
||||
env.symbols.remove(symbol);
|
||||
let pass_block_expr = BlockExpr(pass_block, pass_value_id);
|
||||
|
||||
let fail_block = builder.add_block();
|
||||
let fail_value_id = stmt_spec(builder, env, fail_block, layout, fail)?;
|
||||
let fail_block_expr = BlockExpr(fail_block, fail_value_id);
|
||||
|
||||
builder.add_choice(block, &[pass_block_expr, fail_block_expr])
|
||||
}
|
||||
Switch {
|
||||
cond_symbol: _,
|
||||
cond_layout: _,
|
||||
branches,
|
||||
default_branch,
|
||||
ret_layout,
|
||||
} => {
|
||||
let mut cases = Vec::with_capacity(branches.len() + 1);
|
||||
|
||||
let it = branches
|
||||
.iter()
|
||||
.map(|(_, _, body)| body)
|
||||
.chain(std::iter::once(default_branch.1));
|
||||
|
||||
for branch in it {
|
||||
let block = builder.add_block();
|
||||
let value_id = stmt_spec(builder, env, block, ret_layout, branch)?;
|
||||
cases.push(BlockExpr(block, value_id));
|
||||
}
|
||||
|
||||
builder.add_choice(block, &cases)
|
||||
}
|
||||
Ret(symbol) => Ok(env.symbols[symbol]),
|
||||
Refcounting(modify_rc, continuation) => match modify_rc {
|
||||
ModifyRc::Inc(symbol, _) | ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => {
|
||||
let result_type = builder.add_tuple_type(&[])?;
|
||||
let argument = env.symbols[symbol];
|
||||
|
||||
// this is how RC is modelled; it recursively touches all heap cells
|
||||
builder.add_unknown_with(block, &[argument], result_type)?;
|
||||
|
||||
stmt_spec(builder, env, block, layout, continuation)
|
||||
}
|
||||
},
|
||||
Join {
|
||||
id,
|
||||
parameters,
|
||||
continuation,
|
||||
remainder,
|
||||
} => {
|
||||
let mut type_ids = Vec::new();
|
||||
|
||||
for p in parameters.iter() {
|
||||
type_ids.push(layout_spec(builder, &p.layout)?);
|
||||
}
|
||||
|
||||
let ret_type_id = layout_spec(builder, layout)?;
|
||||
|
||||
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
|
||||
|
||||
let (jpid, jp_argument) =
|
||||
builder.declare_join_point(block, jp_arg_type_id, ret_type_id)?;
|
||||
|
||||
let join_body_sub_block = {
|
||||
env.join_points.insert(*id, jpid);
|
||||
let jp_body_block = builder.add_block();
|
||||
|
||||
// unpack the argument
|
||||
for (i, p) in parameters.iter().enumerate() {
|
||||
let value_id =
|
||||
builder.add_get_tuple_field(jp_body_block, jp_argument, i as u32)?;
|
||||
env.symbols.insert(p.symbol, value_id);
|
||||
}
|
||||
|
||||
let jp_body_value_id = stmt_spec(builder, env, jp_body_block, layout, remainder)?;
|
||||
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);
|
||||
builder.define_join_point(jpid, join_body_sub_block)?;
|
||||
|
||||
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
|
||||
}
|
||||
Jump(id, symbols) => {
|
||||
let ret_type_id = layout_spec(builder, layout)?;
|
||||
let argument = build_tuple_value(builder, env, block, symbols)?;
|
||||
|
||||
let jpid = env.join_points[id];
|
||||
builder.add_jump(block, jpid, argument, ret_type_id)
|
||||
}
|
||||
Rethrow | RuntimeError(_) => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_tuple_value(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &Env,
|
||||
block: BlockId,
|
||||
symbols: &[Symbol],
|
||||
) -> Result<ValueId> {
|
||||
let mut value_ids = Vec::new();
|
||||
|
||||
for field in symbols.iter() {
|
||||
let value_id = match env.symbols.get(field) {
|
||||
None => panic!(
|
||||
"Symbol {:?} is not defined in environment {:?}",
|
||||
field, &env.symbols
|
||||
),
|
||||
Some(x) => *x,
|
||||
};
|
||||
value_ids.push(value_id);
|
||||
}
|
||||
|
||||
builder.add_make_tuple(block, &value_ids)
|
||||
}
|
||||
|
||||
fn build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result<TypeId> {
|
||||
let mut field_types = Vec::new();
|
||||
|
||||
for field in layouts.iter() {
|
||||
field_types.push(layout_spec(builder, field)?);
|
||||
}
|
||||
|
||||
builder.add_tuple_type(&field_types)
|
||||
}
|
||||
|
||||
fn call_spec(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &Env,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
call: &Call,
|
||||
) -> Result<ValueId> {
|
||||
use CallType::*;
|
||||
|
||||
match &call.call_type {
|
||||
ByName {
|
||||
name: symbol,
|
||||
full_layout: _,
|
||||
ret_layout: _,
|
||||
arg_layouts: _,
|
||||
specialization_id,
|
||||
} => {
|
||||
let array = specialization_id.to_bytes();
|
||||
let spec_var = CalleeSpecVar(&array);
|
||||
|
||||
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
||||
let slice = &symbol.to_ne_bytes();
|
||||
let name = FuncName(slice);
|
||||
let module = MOD_APP;
|
||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
||||
}
|
||||
Foreign {
|
||||
foreign_symbol: _,
|
||||
ret_layout,
|
||||
} => {
|
||||
let arguments: Vec<_> = call
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|symbol| env.symbols[symbol])
|
||||
.collect();
|
||||
|
||||
let result_type = layout_spec(builder, ret_layout)?;
|
||||
|
||||
builder.add_unknown_with(block, &arguments, result_type)
|
||||
}
|
||||
LowLevel { op, update_mode } => lowlevel_spec(
|
||||
builder,
|
||||
env,
|
||||
block,
|
||||
layout,
|
||||
op,
|
||||
*update_mode,
|
||||
call.arguments,
|
||||
),
|
||||
HigherOrderLowLevel { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lowlevel_spec(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &Env,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
op: &LowLevel,
|
||||
update_mode: crate::ir::UpdateModeId,
|
||||
arguments: &[Symbol],
|
||||
) -> Result<ValueId> {
|
||||
use LowLevel::*;
|
||||
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
let mode = update_mode.to_bytes();
|
||||
let update_mode_var = UpdateModeVar(&mode);
|
||||
|
||||
match op {
|
||||
NumAdd | NumSub => {
|
||||
// NOTE these numeric operations panic (e.g. on overflow)
|
||||
|
||||
let pass_block = {
|
||||
let block = builder.add_block();
|
||||
let value = new_num(builder, block)?;
|
||||
BlockExpr(block, value)
|
||||
};
|
||||
|
||||
let fail_block = {
|
||||
let block = builder.add_block();
|
||||
let value = builder.add_terminate(block, type_id)?;
|
||||
BlockExpr(block, value)
|
||||
};
|
||||
|
||||
let sub_block = {
|
||||
let block = builder.add_block();
|
||||
let choice = builder.add_choice(block, &[pass_block, fail_block])?;
|
||||
|
||||
BlockExpr(block, choice)
|
||||
};
|
||||
|
||||
builder.add_sub_block(block, sub_block)
|
||||
}
|
||||
Eq | NotEq => new_bool(builder, block),
|
||||
NumLte | NumLt | NumGt | NumGte => new_order(builder, block),
|
||||
ListLen => {
|
||||
let list = env.symbols[&arguments[0]];
|
||||
|
||||
builder.add_get_tuple_field(block, list, LIST_LEN_INDEX)
|
||||
}
|
||||
ListGetUnsafe => {
|
||||
// NOTE the ListGet lowlevel op is only evaluated if the index is in-bounds
|
||||
let list = env.symbols[&arguments[0]];
|
||||
|
||||
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 _unit = builder.add_touch(block, cell)?;
|
||||
|
||||
builder.add_bag_get(block, bag)
|
||||
}
|
||||
ListSet => {
|
||||
let list = env.symbols[&arguments[0]];
|
||||
let to_insert = env.symbols[&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 _unit = builder.add_update(block, update_mode_var, cell)?;
|
||||
|
||||
builder.add_bag_insert(block, bag, to_insert)?;
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
other => todo!("lowlevel op not implemented: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_variant_types(
|
||||
builder: &mut FuncDefBuilder,
|
||||
layout: &Layout,
|
||||
) -> Option<Result<Vec<TypeId>>> {
|
||||
match layout {
|
||||
Layout::Union(union_layout) => Some(build_variant_types_help(builder, union_layout)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_variant_types_help(
|
||||
builder: &mut FuncDefBuilder,
|
||||
union_layout: &UnionLayout,
|
||||
) -> Result<Vec<TypeId>> {
|
||||
use UnionLayout::*;
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
for tag in tags.iter() {
|
||||
result.push(build_tuple_type(builder, tag)?);
|
||||
}
|
||||
}
|
||||
Recursive(_) => todo!(),
|
||||
NonNullableUnwrapped(_) => todo!(),
|
||||
NullableWrapped {
|
||||
nullable_id: _,
|
||||
other_tags: _,
|
||||
} => todo!(),
|
||||
NullableUnwrapped {
|
||||
nullable_id: _,
|
||||
other_fields: _,
|
||||
} => todo!(),
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn expr_spec(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &Env,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
expr: &Expr,
|
||||
) -> Result<ValueId> {
|
||||
use Expr::*;
|
||||
|
||||
match expr {
|
||||
Literal(literal) => literal_spec(builder, block, literal),
|
||||
Call(call) => call_spec(builder, env, block, layout, call),
|
||||
Tag {
|
||||
tag_layout,
|
||||
tag_name: _,
|
||||
tag_id,
|
||||
union_size: _,
|
||||
arguments,
|
||||
} => {
|
||||
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
let variant_types = build_variant_types(builder, tag_layout).unwrap()?;
|
||||
builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)
|
||||
}
|
||||
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
||||
AccessAtIndex {
|
||||
index,
|
||||
field_layouts: _,
|
||||
structure,
|
||||
wrapped,
|
||||
} => {
|
||||
use crate::ir::Wrapped;
|
||||
|
||||
let value_id = env.symbols[structure];
|
||||
|
||||
match wrapped {
|
||||
Wrapped::EmptyRecord => {
|
||||
// this is a unit value
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
Wrapped::SingleElementRecord => {
|
||||
todo!("do we unwrap single-element records still?")
|
||||
}
|
||||
Wrapped::RecordOrSingleTagUnion => {
|
||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||
}
|
||||
Wrapped::MultiTagUnion => {
|
||||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
Array { elem_layout, elems } => {
|
||||
let type_id = layout_spec(builder, elem_layout)?;
|
||||
|
||||
let list = new_list(builder, block, type_id)?;
|
||||
|
||||
let mut bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
|
||||
for symbol in elems.iter() {
|
||||
let value_id = env.symbols[symbol];
|
||||
|
||||
bag = builder.add_bag_insert(block, bag, value_id)?;
|
||||
}
|
||||
|
||||
Ok(bag)
|
||||
}
|
||||
|
||||
EmptyArray => {
|
||||
use ListLayout::*;
|
||||
|
||||
match ListLayout::try_from(layout) {
|
||||
Ok(EmptyList) => {
|
||||
// just make up an element type
|
||||
let type_id = builder.add_tuple_type(&[])?;
|
||||
new_list(builder, block, type_id)
|
||||
}
|
||||
Ok(List(element_layout)) => {
|
||||
let type_id = layout_spec(builder, element_layout)?;
|
||||
new_list(builder, block, type_id)
|
||||
}
|
||||
Err(()) => unreachable!("empty array does not have a list layout"),
|
||||
}
|
||||
}
|
||||
Reuse { .. } => todo!("currently unused"),
|
||||
Reset(_) => todo!("currently unused"),
|
||||
RuntimeErrorFunction(_) => {
|
||||
let type_id = layout_spec(builder, layout)?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn literal_spec(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
literal: &Literal,
|
||||
) -> Result<ValueId> {
|
||||
use Literal::*;
|
||||
|
||||
match literal {
|
||||
Str(_) => new_static_string(builder, block),
|
||||
Int(_) | Float(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result<TypeId> {
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Builtin(builtin) => builtin_spec(builder, builtin),
|
||||
PhantomEmptyStruct => todo!(),
|
||||
Struct(fields) => build_tuple_type(builder, fields),
|
||||
Union(union_layout) => {
|
||||
let variant_types = build_variant_types_help(builder, union_layout)?;
|
||||
builder.add_union_type(&variant_types)
|
||||
}
|
||||
RecursivePointer => todo!(),
|
||||
FunctionPointer(_, _) => todo!(),
|
||||
Closure(_, _, _) => todo!(),
|
||||
Pointer(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeId> {
|
||||
use Builtin::*;
|
||||
|
||||
match builtin {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize => builder.add_tuple_type(&[]),
|
||||
Float128 => todo!(),
|
||||
Float64 => todo!(),
|
||||
Float32 => todo!(),
|
||||
Float16 => todo!(),
|
||||
Str => todo!(),
|
||||
Dict(_, _) => todo!(),
|
||||
Set(_) => todo!(),
|
||||
List(_, _) => {
|
||||
// TODO should incorporate the element type into the name
|
||||
Ok(builder.add_named_type(MOD_LIST, TypeName(b"List")))
|
||||
}
|
||||
EmptyStr => todo!(),
|
||||
EmptyList => todo!(),
|
||||
EmptyDict => todo!(),
|
||||
EmptySet => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
// const OK_TAG_ID: u8 = 1u8;
|
||||
// const ERR_TAG_ID: u8 = 0u8;
|
||||
|
||||
const LIST_CELL_INDEX: u32 = 0;
|
||||
const LIST_BAG_INDEX: u32 = 1;
|
||||
const LIST_LEN_INDEX: u32 = 2;
|
||||
|
||||
fn new_list(builder: &mut FuncDefBuilder, block: BlockId, element_type: TypeId) -> Result<ValueId> {
|
||||
let cell = builder.add_new_heap_cell(block)?;
|
||||
let bag = builder.add_empty_bag(block, element_type)?;
|
||||
let length = new_usize(builder, block)?;
|
||||
builder.add_make_tuple(block, &[cell, bag, length])
|
||||
}
|
||||
|
||||
fn new_usize(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
new_num(builder, block)
|
||||
}
|
||||
|
||||
fn new_static_string(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
let cell = builder.add_new_heap_cell(block)?;
|
||||
|
||||
// immediately mutate the cell, so any future updates on this value are invalid
|
||||
// updating a static string would cause a crash at runtime
|
||||
let _ = builder.add_update(block, UpdateModeVar(&[]), cell)?;
|
||||
|
||||
let length = new_usize(builder, block)?;
|
||||
builder.add_make_tuple(block, &[cell, length])
|
||||
}
|
||||
|
||||
fn new_order(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
// always generats EQ
|
||||
let tag_id = 0;
|
||||
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
let unit_value = builder.add_make_tuple(block, &[])?;
|
||||
builder.add_make_union(block, &[unit, unit, unit], tag_id, unit_value)
|
||||
}
|
||||
|
||||
fn new_bool(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
// always generats False
|
||||
let tag_id = 0;
|
||||
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
let unit_value = builder.add_make_tuple(block, &[])?;
|
||||
builder.add_make_union(block, &[unit, unit], tag_id, unit_value)
|
||||
}
|
||||
|
||||
fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
||||
// we model all our numbers as unit values
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
|
@ -382,7 +382,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.own_args_using_params(arguments, ps);
|
||||
}
|
||||
|
||||
LowLevel { op } => {
|
||||
LowLevel { op, .. } => {
|
||||
debug_assert!(!op.is_higher_order());
|
||||
|
||||
self.own_var(z);
|
||||
|
@ -747,6 +747,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
// TODO when we have lists with capacity (if ever)
|
||||
// List.append should own its first argument
|
||||
ListAppend => arena.alloc_slice_copy(&[owned, owned]),
|
||||
ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||
|
||||
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
|
||||
|
|
|
@ -1404,8 +1404,12 @@ fn compile_test_help<'a>(
|
|||
default_branch,
|
||||
};
|
||||
|
||||
let op = LowLevel::Eq;
|
||||
let test = Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
|
||||
call_type: crate::ir::CallType::LowLevel {
|
||||
op,
|
||||
update_mode: env.next_update_mode_id(),
|
||||
},
|
||||
arguments: arena.alloc([lhs, rhs]),
|
||||
});
|
||||
|
||||
|
|
|
@ -441,7 +441,7 @@ impl<'a> Context<'a> {
|
|||
use crate::ir::CallType::*;
|
||||
|
||||
match &call_type {
|
||||
LowLevel { op } => {
|
||||
LowLevel { op, .. } => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||
|
||||
|
@ -932,7 +932,7 @@ impl<'a> Context<'a> {
|
|||
|
||||
use crate::ir::CallType;
|
||||
let stmt = match &call.call_type {
|
||||
CallType::LowLevel { op } => {
|
||||
CallType::LowLevel { op, .. } => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||
self.add_dec_after_lowlevel(call.arguments, ps, cont, &invoke_live_vars)
|
||||
}
|
||||
|
|
|
@ -777,6 +777,8 @@ pub struct Env<'a, 'i> {
|
|||
pub home: ModuleId,
|
||||
pub ident_ids: &'i mut IdentIds,
|
||||
pub ptr_bytes: u32,
|
||||
pub update_mode_counter: u64,
|
||||
pub call_specialization_counter: u64,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Env<'a, 'i> {
|
||||
|
@ -788,6 +790,26 @@ impl<'a, 'i> Env<'a, 'i> {
|
|||
Symbol::new(self.home, ident_id)
|
||||
}
|
||||
|
||||
pub fn next_update_mode_id(&mut self) -> UpdateModeId {
|
||||
let id = UpdateModeId {
|
||||
id: self.update_mode_counter,
|
||||
};
|
||||
|
||||
self.update_mode_counter += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn next_call_specialization_id(&mut self) -> CallSpecId {
|
||||
let id = CallSpecId {
|
||||
id: self.call_specialization_counter,
|
||||
};
|
||||
|
||||
self.call_specialization_counter += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn is_imported_symbol(&self, symbol: Symbol) -> bool {
|
||||
symbol.module_id() != self.home && !symbol.is_builtin()
|
||||
}
|
||||
|
@ -1038,7 +1060,7 @@ impl<'a> Call<'a> {
|
|||
|
||||
alloc.text("CallByName ").append(alloc.intersperse(it, " "))
|
||||
}
|
||||
LowLevel { op: lowlevel } => {
|
||||
LowLevel { op: lowlevel, .. } => {
|
||||
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
|
||||
|
||||
alloc
|
||||
|
@ -1065,14 +1087,36 @@ impl<'a> Call<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct CallSpecId {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl CallSpecId {
|
||||
pub fn to_bytes(self) -> [u8; 8] {
|
||||
self.id.to_ne_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct UpdateModeId {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl UpdateModeId {
|
||||
pub fn to_bytes(self) -> [u8; 8] {
|
||||
self.id.to_ne_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum CallType<'a> {
|
||||
ByName {
|
||||
name: Symbol,
|
||||
|
||||
full_layout: Layout<'a>,
|
||||
ret_layout: Layout<'a>,
|
||||
arg_layouts: &'a [Layout<'a>],
|
||||
specialization_id: CallSpecId,
|
||||
},
|
||||
Foreign {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
|
@ -1080,6 +1124,7 @@ pub enum CallType<'a> {
|
|||
},
|
||||
LowLevel {
|
||||
op: LowLevel,
|
||||
update_mode: UpdateModeId,
|
||||
},
|
||||
HigherOrderLowLevel {
|
||||
op: LowLevel,
|
||||
|
@ -4329,7 +4374,10 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
_ => {
|
||||
let call = self::Call {
|
||||
call_type: CallType::LowLevel { op },
|
||||
call_type: CallType::LowLevel {
|
||||
op,
|
||||
update_mode: env.next_update_mode_id(),
|
||||
},
|
||||
arguments: arg_symbols,
|
||||
};
|
||||
|
||||
|
@ -4547,8 +4595,10 @@ pub fn from_can<'a>(
|
|||
let bool_layout = Layout::Builtin(Builtin::Int1);
|
||||
let cond_symbol = env.unique_symbol();
|
||||
|
||||
let op = LowLevel::ExpectTrue;
|
||||
let call_type = CallType::LowLevel {
|
||||
op: LowLevel::ExpectTrue,
|
||||
op,
|
||||
update_mode: env.next_update_mode_id(),
|
||||
};
|
||||
let arguments = env.arena.alloc([cond_symbol]);
|
||||
let call = self::Call {
|
||||
|
@ -5300,11 +5350,13 @@ fn substitute_in_call<'a>(
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
full_layout,
|
||||
specialization_id,
|
||||
} => substitute(subs, *name).map(|new| CallType::ByName {
|
||||
name: new,
|
||||
arg_layouts,
|
||||
ret_layout: *ret_layout,
|
||||
full_layout: *full_layout,
|
||||
specialization_id: *specialization_id,
|
||||
}),
|
||||
CallType::Foreign { .. } => None,
|
||||
CallType::LowLevel { .. } => None,
|
||||
|
@ -5774,6 +5826,7 @@ fn force_thunk<'a>(
|
|||
ret_layout: layout,
|
||||
full_layout,
|
||||
arg_layouts: &[],
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: &[],
|
||||
};
|
||||
|
@ -6248,6 +6301,7 @@ fn call_by_name_help<'a>(
|
|||
ret_layout: *ret_layout,
|
||||
full_layout: function_layout,
|
||||
arg_layouts: argument_layouts,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -6280,6 +6334,7 @@ fn call_by_name_help<'a>(
|
|||
ret_layout: *ret_layout,
|
||||
full_layout: function_layout,
|
||||
arg_layouts: argument_layouts,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -6332,6 +6387,7 @@ fn call_by_name_help<'a>(
|
|||
ret_layout: *ret_layout,
|
||||
full_layout: function_layout,
|
||||
arg_layouts: argument_layouts,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -6582,6 +6638,7 @@ fn call_specialized_proc<'a>(
|
|||
ret_layout: function_layout.result,
|
||||
full_layout: function_layout.full(),
|
||||
arg_layouts: function_layout.arguments,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -6601,6 +6658,7 @@ fn call_specialized_proc<'a>(
|
|||
ret_layout: function_layout.result,
|
||||
full_layout: function_layout.full(),
|
||||
arg_layouts: function_layout.arguments,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -6624,6 +6682,7 @@ fn call_specialized_proc<'a>(
|
|||
ret_layout: function_layout.result,
|
||||
full_layout: function_layout.full(),
|
||||
arg_layouts: function_layout.arguments,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: field_symbols,
|
||||
};
|
||||
|
@ -7907,6 +7966,7 @@ fn union_lambda_set_branch_help<'a>(
|
|||
full_layout,
|
||||
ret_layout: return_layout,
|
||||
arg_layouts: argument_layouts,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: argument_symbols,
|
||||
}),
|
||||
|
@ -8030,6 +8090,7 @@ fn enum_lambda_set_branch<'a>(
|
|||
full_layout,
|
||||
ret_layout: return_layout,
|
||||
arg_layouts: argument_layouts,
|
||||
specialization_id: env.next_call_specialization_id(),
|
||||
},
|
||||
arguments: argument_symbols,
|
||||
}),
|
||||
|
|
|
@ -1915,3 +1915,21 @@ impl<'a> LayoutIds<'a> {
|
|||
LayoutId(answer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ListLayout<'a> {
|
||||
EmptyList,
|
||||
List(&'a Layout<'a>),
|
||||
}
|
||||
|
||||
impl<'a> std::convert::TryFrom<&Layout<'a>> for ListLayout<'a> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Layout<'a>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Layout::Builtin(Builtin::EmptyList) => Ok(ListLayout::EmptyList),
|
||||
Layout::Builtin(Builtin::List(_, element)) => Ok(ListLayout::List(element)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#![warn(clippy::all, clippy::dbg_macro)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)]
|
||||
|
||||
pub mod alias_analysis;
|
||||
pub mod borrow;
|
||||
pub mod expand_rc;
|
||||
pub mod inc_dec;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue