From 6e3a2cd94d960a25f0a5fd6828ccd842ece3d9da Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 2 Jul 2021 10:48:03 +0200 Subject: [PATCH 01/61] remove union_size from Tag expr --- compiler/gen_llvm/src/llvm/build.rs | 6 +++--- compiler/mono/src/alias_analysis.rs | 1 - compiler/mono/src/ir.rs | 14 +++----------- compiler/mono/src/layout.rs | 12 ++++++++++-- compiler/mono/src/lib.rs | 1 + 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 815e636473..9100f2bc64 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -973,10 +973,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( Tag { arguments, tag_layout: union_layout, - union_size, tag_id, .. - } => build_tag(env, scope, union_layout, *union_size, *tag_id, arguments), + } => build_tag(env, scope, union_layout, *tag_id, arguments), Reset(_) => todo!(), Reuse { .. } => todo!(), @@ -1174,12 +1173,13 @@ pub fn build_tag<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, union_layout: &UnionLayout<'a>, - union_size: u8, tag_id: u8, arguments: &[Symbol], ) -> BasicValueEnum<'ctx> { let tag_id_layout = union_layout.tag_id_layout(); + let union_size = union_layout.number_of_tags(); + match union_layout { UnionLayout::NonRecursive(tags) => { debug_assert!(union_size > 1); diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 2b4354d26e..a439cd62e0 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -836,7 +836,6 @@ fn expr_spec( tag_layout, tag_name: _, tag_id, - union_size: _, arguments, } => match tag_layout { UnionLayout::NonRecursive(_) => { diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 94877e4ad0..9c8a498389 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1129,7 +1129,6 @@ pub enum Expr<'a> { tag_layout: UnionLayout<'a>, tag_name: TagName, tag_id: u8, - union_size: u8, arguments: &'a [Symbol], }, Struct(&'a [Symbol]), @@ -1160,6 +1159,9 @@ pub enum Expr<'a> { Reuse { symbol: Symbol, + update_tag_id: bool, + // normal Tag fields + tag_layout: UnionLayout<'a>, tag_name: TagName, tag_id: u8, arguments: &'a [Symbol], @@ -4031,14 +4033,12 @@ fn construct_closure_data<'a>( ClosureRepresentation::Union { tag_id, tag_layout: _, - union_size, tag_name, union_layout, } => { let expr = Expr::Tag { tag_id, tag_layout: union_layout, - union_size, tag_name, arguments: symbols, }; @@ -4167,7 +4167,6 @@ fn convert_tag_union<'a>( assign_to_symbols(env, procs, layout_cache, iter, stmt) } Wrapped(variant) => { - let union_size = variant.number_of_tags() as u8; let (tag_id, _) = variant.tag_name_to_id(&tag_name); let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args); @@ -4203,7 +4202,6 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_name, tag_id: tag_id as u8, - union_size, arguments: field_symbols, }; @@ -4231,7 +4229,6 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_name, tag_id: tag_id as u8, - union_size, arguments: field_symbols, }; @@ -4261,7 +4258,6 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_name, tag_id: tag_id as u8, - union_size, arguments: field_symbols, }; @@ -4298,7 +4294,6 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_name, tag_id: tag_id as u8, - union_size, arguments: field_symbols, }; @@ -4331,7 +4326,6 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_name, tag_id: tag_id as u8, - union_size, arguments: field_symbols, }; @@ -5381,7 +5375,6 @@ fn substitute_in_expr<'a>( tag_layout, tag_name, tag_id, - union_size, arguments: args, } => { let mut did_change = false; @@ -5403,7 +5396,6 @@ fn substitute_in_expr<'a>( tag_layout: *tag_layout, tag_name: tag_name.clone(), tag_id: *tag_id, - union_size: *union_size, arguments, }) } else { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 6152b73304..89ea62d0b2 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -148,6 +148,16 @@ impl<'a> UnionLayout<'a> { } } + pub fn number_of_tags(&'a self) -> usize { + match self { + UnionLayout::NonRecursive(tags) | UnionLayout::Recursive(tags) => tags.len(), + + UnionLayout::NullableWrapped { other_tags, .. } => other_tags.len() + 1, + UnionLayout::NonNullableUnwrapped(_) => 1, + UnionLayout::NullableUnwrapped { .. } => 2, + } + } + fn tag_id_builtin_help(union_size: usize) -> Builtin<'a> { if union_size <= u8::MAX as usize { Builtin::Int8 @@ -204,7 +214,6 @@ pub enum ClosureRepresentation<'a> { tag_layout: &'a [Layout<'a>], tag_name: TagName, tag_id: u8, - union_size: u8, union_layout: UnionLayout<'a>, }, /// the representation is anything but a union @@ -243,7 +252,6 @@ impl<'a> LambdaSet<'a> { .unwrap(); ClosureRepresentation::Union { - union_size: self.set.len() as u8, tag_id: index as u8, tag_layout: tags[index], tag_name: TagName::Closure(function_symbol), diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index 831ff3d8c1..9a79927aec 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -8,6 +8,7 @@ pub mod expand_rc; pub mod inc_dec; pub mod ir; pub mod layout; +// pub mod reset_reuse; pub mod tail_recursion; // Temporary, while we can build up test cases and optimize the exhaustiveness checking. From 3c2ee9e447c0aa71e2f432e0c4ddd0473418d84b Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 2 Jul 2021 20:34:11 +0200 Subject: [PATCH 02/61] WIP --- compiler/mono/src/inc_dec.rs | 4 +- compiler/mono/src/ir.rs | 2 +- compiler/mono/src/lib.rs | 2 +- compiler/mono/src/reset_reuse.rs | 596 +++++++++++++++++++++++++++++++ 4 files changed, 600 insertions(+), 4 deletions(-) create mode 100644 compiler/mono/src/reset_reuse.rs diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index a9583856e4..bff0872656 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -157,8 +157,8 @@ struct VarInfo { } type VarMap = MutMap; -type LiveVarSet = MutSet; -type JPLiveVarMap = MutMap; +pub type LiveVarSet = MutSet; +pub type JPLiveVarMap = MutMap; #[derive(Clone, Debug)] struct Context<'a> { diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9c8a498389..d589571b5f 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -19,7 +19,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index 9a79927aec..d377342bed 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -8,7 +8,7 @@ pub mod expand_rc; pub mod inc_dec; pub mod ir; pub mod layout; -// pub mod reset_reuse; +pub mod reset_reuse; pub mod tail_recursion; // Temporary, while we can build up test cases and optimize the exhaustiveness checking. diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs new file mode 100644 index 0000000000..67f17ed112 --- /dev/null +++ b/compiler/mono/src/reset_reuse.rs @@ -0,0 +1,596 @@ +use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveVarSet}; +use crate::ir::{Call, Expr, Proc, Stmt}; +use crate::layout::{Layout, UnionLayout}; +use bumpalo::collections::Vec; +use bumpalo::Bump; +use roc_collections::all::MutSet; +use roc_module::symbol::{IdentIds, ModuleId, Symbol}; + +pub fn insert_reset_reuse<'a, 'i>( + arena: &'a Bump, + home: ModuleId, + ident_ids: &'i mut IdentIds, + mut proc: Proc<'a>, +) -> Proc<'a> { + let mut env = Env { + arena, + home, + ident_ids, + jp_live_vars: Default::default(), + }; + + let new_body = function_r(&mut env, arena.alloc(proc.body)); + proc.body = new_body.clone(); + + proc +} + +struct CtorInfo<'a> { + id: u8, + layout: UnionLayout<'a>, +} + +fn may_reuse(tag_layout: UnionLayout, tag_id: u8, other: &CtorInfo) -> bool { + if tag_layout != other.layout { + return false; + } + + // if the tag id is represented as NULL, there is no memory to re-use + match tag_layout { + UnionLayout::NonRecursive(_) + | UnionLayout::Recursive(_) + | UnionLayout::NonNullableUnwrapped(_) => true, + UnionLayout::NullableWrapped { nullable_id, .. } => tag_id as i64 != nullable_id, + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => (tag_id != 0) != nullable_id, + } +} + +#[derive(Debug)] +struct Env<'a, 'i> { + arena: &'a Bump, + + /// required for creating new `Symbol`s + home: ModuleId, + ident_ids: &'i mut IdentIds, + + jp_live_vars: JPLiveVarMap, +} + +impl<'a, 'i> Env<'a, 'i> { + fn unique_symbol(&mut self) -> Symbol { + let ident_id = self.ident_ids.gen_unique(); + + self.home.register_debug_idents(&self.ident_ids); + + Symbol::new(self.home, ident_id) + } +} + +fn function_s<'a, 'i>( + env: &mut Env<'a, 'i>, + w: Symbol, + c: &CtorInfo<'a>, + stmt: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + use Stmt::*; + + let arena = env.arena; + + match stmt { + Let(symbol, expr, layout, continuation) => match expr { + Expr::Tag { + tag_layout, + tag_id, + tag_name, + arguments, + } if may_reuse(*tag_layout, *tag_id, c) => { + // for now, always overwrite the tag ID just to be sure + let update_tag_id = true; + + let new_expr = Expr::Reuse { + symbol: w, + update_tag_id, + tag_layout: *tag_layout, + tag_id: *tag_id, + tag_name: tag_name.clone(), + arguments, + }; + let new_stmt = Let(*symbol, new_expr, *layout, continuation); + + arena.alloc(new_stmt) + } + _ => { + let rest = function_s(env, w, c, continuation); + let new_stmt = Let(*symbol, expr.clone(), *layout, rest); + + arena.alloc(new_stmt) + } + }, + Join { + id, + parameters, + body, + remainder, + } => { + let id = *id; + let body: &Stmt = *body; + let new_body = function_s(env, w, c, body); + + let new_join = if std::ptr::eq(body, new_body) || body == new_body { + // the join point body will consume w + Join { + id, + parameters, + body: new_body, + remainder, + } + } else { + let new_remainder = function_s(env, w, c, remainder); + + Join { + id, + parameters, + body, + remainder: new_remainder, + } + }; + + arena.alloc(new_join) + } + Invoke { + symbol, + call, + layout, + pass, + fail, + exception_id, + } => { + let new_pass = function_s(env, w, c, pass); + let new_fail = function_s(env, w, c, fail); + + let new_invoke = Invoke { + symbol: *symbol, + call: call.clone(), + layout: *layout, + pass: new_pass, + fail: new_fail, + exception_id: *exception_id, + }; + + arena.alloc(new_invoke) + } + Switch { + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + let mut new_branches = Vec::with_capacity_in(branches.len(), arena); + new_branches.extend(branches.iter().map(|(tag, info, body)| { + let new_body = function_s(env, w, c, body); + + (*tag, info.clone(), new_body.clone()) + })); + + let new_default = function_s(env, w, c, default_branch.1); + + let new_switch = Switch { + cond_symbol: *cond_symbol, + cond_layout: *cond_layout, + branches: new_branches.into_bump_slice(), + default_branch: (default_branch.0.clone(), new_default), + ret_layout: *ret_layout, + }; + + arena.alloc(new_switch) + } + Refcounting(op, continuation) => { + let continuation: &Stmt = *continuation; + let new_continuation = function_s(env, w, c, continuation); + + if std::ptr::eq(continuation, new_continuation) || continuation == new_continuation { + stmt + } else { + let new_refcounting = Refcounting(*op, new_continuation); + + arena.alloc(new_refcounting) + } + } + Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => stmt, + } +} + +fn try_function_s<'a, 'i>( + env: &mut Env<'a, 'i>, + x: Symbol, + c: &CtorInfo<'a>, + stmt: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + let w = env.unique_symbol(); + + let new_stmt = function_s(env, w, c, stmt); + + if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { + stmt + } else { + let f00 = Stmt::Let(w, Expr::Reset(x), Layout::Union(c.layout), new_stmt); + + env.arena.alloc(f00) + } +} + +fn function_d_finalize<'a, 'i>( + env: &mut Env<'a, 'i>, + x: Symbol, + c: &CtorInfo<'a>, + output: (&'a Stmt<'a>, bool), +) -> &'a Stmt<'a> { + let (stmt, x_live_in_stmt) = output; + if x_live_in_stmt { + stmt + } else { + try_function_s(env, x, c, stmt) + } +} + +fn function_d_main<'a, 'i>( + env: &mut Env<'a, 'i>, + x: Symbol, + c: &CtorInfo<'a>, + stmt: &'a Stmt<'a>, +) -> (&'a Stmt<'a>, bool) { + use Stmt::*; + + let arena = env.arena; + + match stmt { + Let(symbol, expr, layout, continuation) => match expr { + Expr::Tag { arguments, .. } if arguments.iter().any(|s| *s == x) => { + // If the scrutinee `x` (the one that is providing memory) is being + // stored in a constructor, then reuse will probably not be able to reuse memory at runtime. + // It may work only if the new cell is consumed, but we ignore this case. + (stmt, true) + } + _ => { + let (b, found) = function_d_main(env, x, c, continuation); + + let mut result = MutSet::default(); + if found || { + occurring_variables_expr(expr, &mut result); + !result.contains(&x) + } { + let let_stmt = Let(*symbol, expr.clone(), *layout, b); + + (arena.alloc(let_stmt), found) + } else { + let b = try_function_s(env, x, c, b); + let let_stmt = Let(*symbol, expr.clone(), *layout, b); + + (arena.alloc(let_stmt), found) + } + } + }, + Invoke { + symbol, + call, + layout, + pass, + fail, + exception_id, + } => todo!(), + Switch { + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + if has_live_var(&env.jp_live_vars, stmt, x) { + // if `x` is live in `stmt`, we recursively process each branch + let mut new_branches = Vec::with_capacity_in(branches.len(), arena); + + for (tag, info, body) in branches.iter() { + let temp = function_d_main(env, x, c, body); + let new_body = function_d_finalize(env, x, c, temp); + + new_branches.push((*tag, info.clone(), new_body.clone())); + } + + let new_default = { + let (info, body) = default_branch; + let temp = function_d_main(env, x, c, body); + let new_body = function_d_finalize(env, x, c, temp); + + (info.clone(), new_body) + }; + + let new_switch = Switch { + cond_symbol: *cond_symbol, + cond_layout: *cond_layout, + branches: new_branches.into_bump_slice(), + default_branch: new_default, + ret_layout: *ret_layout, + }; + + (arena.alloc(new_switch), true) + } else { + (stmt, false) + } + } + Refcounting(modify_rc, continuation) => { + let (b, found) = function_d_main(env, x, c, continuation); + + if found || modify_rc.get_symbol() != x { + let refcounting = Refcounting(*modify_rc, b); + + (arena.alloc(refcounting), found) + } else { + let b = try_function_s(env, x, c, b); + let refcounting = Refcounting(*modify_rc, b); + + (arena.alloc(refcounting), found) + } + } + Join { + id, + parameters, + body, + remainder, + } => { + env.jp_live_vars.insert(*id, LiveVarSet::default()); + + let body_live_vars = collect_stmt(body, &env.jp_live_vars, LiveVarSet::default()); + + env.jp_live_vars.insert(*id, body_live_vars); + + let (b, found) = function_d_main(env, x, c, remainder); + + let (v, _found) = function_d_main(env, x, c, body); + + env.jp_live_vars.remove(id); + + // If `found' == true`, then `Dmain b` must also have returned `(b, true)` since + // we assume the IR does not have dead join points. So, if `x` is live in `j` (i.e., `v`), + // then it must also live in `b` since `j` is reachable from `b` with a `jmp`. + // On the other hand, `x` may be live in `b` but dead in `j` (i.e., `v`). -/ + let new_join = Join { + id: *id, + parameters, + body: v, + remainder: b, + }; + + (arena.alloc(new_join), found) + } + Ret(_) | Resume(_) | Jump(_, _) | RuntimeError(_) => { + (stmt, has_live_var(&env.jp_live_vars, stmt, x)) + } + } +} + +fn function_d<'a, 'i>( + env: &mut Env<'a, 'i>, + x: Symbol, + c: &CtorInfo<'a>, + stmt: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + let temp = function_d_main(env, x, c, stmt); + + function_d_finalize(env, x, c, temp) +} + +fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> { + use Stmt::*; + + let arena = env.arena; + + match stmt { + Switch { + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + let mut new_branches = Vec::with_capacity_in(branches.len(), arena); + + // TODO for non-recursive unions there is no benefit + let benefits_from_reuse = match cond_layout { + Layout::Union(union_layout) => Some(union_layout), + _ => None, + }; + + for (tag, info, body) in branches.iter() { + let temp = function_r(env, body); + + // TODO the branch info stores the tag ID + let new_body = match benefits_from_reuse { + Some(union_layout) => { + let x = *cond_symbol; + let ctor_info = CtorInfo { + layout: *union_layout, + id: 0, + }; + function_d(env, x, &ctor_info, temp) + } + None => temp, + }; + + new_branches.push((*tag, info.clone(), new_body.clone())); + } + + let new_default = { + let (info, body) = default_branch; + let temp = function_r(env, body); + + let new_body = match benefits_from_reuse { + Some(union_layout) => { + let x = *cond_symbol; + let ctor_info = CtorInfo { + layout: *union_layout, + id: 0, + }; + function_d(env, x, &ctor_info, temp) + } + None => temp, + }; + + (info.clone(), new_body) + }; + + let new_switch = Switch { + cond_symbol: *cond_symbol, + cond_layout: *cond_layout, + branches: new_branches.into_bump_slice(), + default_branch: new_default, + ret_layout: *ret_layout, + }; + + arena.alloc(new_switch) + } + + Join { + id, + parameters, + body, + remainder, + } => { + env.jp_live_vars.insert(*id, LiveVarSet::default()); + + let body_live_vars = collect_stmt(body, &env.jp_live_vars, LiveVarSet::default()); + + env.jp_live_vars.insert(*id, body_live_vars); + + let b = function_r(env, remainder); + + let v = function_r(env, body); + + env.jp_live_vars.remove(id); + + let join = Join { + id: *id, + parameters, + body: v, + remainder: b, + }; + + arena.alloc(join) + } + + Let(symbol, expr, layout, continuation) => { + let b = function_r(env, continuation); + + arena.alloc(Let(*symbol, expr.clone(), *layout, b)) + } + Invoke { + symbol, + call, + layout, + pass, + fail, + exception_id, + } => todo!(), + Refcounting(modify_rc, continuation) => { + let b = function_r(env, continuation); + + arena.alloc(Refcounting(*modify_rc, b)) + } + + Resume(_) | Ret(_) | Jump(_, _) | RuntimeError(_) => { + // terminals + stmt + } + } +} + +fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Symbol) -> bool { + use Stmt::*; + + match stmt { + Let(s, e, _, c) => { + debug_assert_ne!(*s, needle); + has_live_var_expr(e, needle) || has_live_var(jp_live_vars, c, needle) + } + Invoke { + symbol, + call, + pass, + fail, + .. + } => { + debug_assert_ne!(*symbol, needle); + + has_live_var_call(call, needle) + || has_live_var(jp_live_vars, pass, needle) + || has_live_var(jp_live_vars, fail, needle) + } + Switch { cond_symbol, .. } if *cond_symbol == needle => true, + Switch { + branches, + default_branch, + .. + } => { + has_live_var(jp_live_vars, default_branch.1, needle) + || branches + .iter() + .any(|(_, _, body)| has_live_var(jp_live_vars, body, needle)) + } + Ret(s) => *s == needle, + Refcounting(modify_rc, cont) => { + modify_rc.get_symbol() == needle || has_live_var(jp_live_vars, cont, needle) + } + Join { + id, + parameters, + body, + remainder, + } => { + debug_assert!(parameters.iter().all(|p| p.symbol != needle)); + + let mut jp_live_vars = jp_live_vars.clone(); + + jp_live_vars.insert(*id, LiveVarSet::default()); + + let body_live_vars = collect_stmt(body, &jp_live_vars, LiveVarSet::default()); + + if body_live_vars.contains(&needle) { + return true; + } + + jp_live_vars.insert(*id, body_live_vars); + + has_live_var(&jp_live_vars, remainder, needle) + } + Jump(id, arguments) => { + arguments.iter().any(|s| *s == needle) || jp_live_vars[id].contains(&needle) + } + Resume(_) | RuntimeError(_) => false, + } +} + +fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool { + match expr { + Expr::Literal(_) => false, + Expr::Call(_) => todo!(), + Expr::Array { elems: fields, .. } + | Expr::Tag { + arguments: fields, .. + } + | Expr::Struct(fields) => fields.iter().any(|s| *s == needle), + Expr::StructAtIndex { structure, .. } + | Expr::GetTagId { structure, .. } + | Expr::UnionAtIndex { structure, .. } => *structure == needle, + Expr::EmptyArray => false, + Expr::Reuse { .. } => unreachable!("not introduced"), + Expr::Reset(_) => unreachable!("not introduced"), + Expr::RuntimeErrorFunction(_) => false, + } +} + +fn has_live_var_call<'a>(call: &'a Call<'a>, needle: Symbol) -> bool { + call.arguments.iter().any(|s| *s == needle) +} From 5bb3146aa803d6b0cf560acf0ada75760355fd28 Mon Sep 17 00:00:00 2001 From: tarjei Date: Fri, 2 Jul 2021 22:21:21 +0200 Subject: [PATCH 03/61] Enable reset-reuse and fix layout problems --- compiler/load/src/file.rs | 9 ++++- compiler/mono/src/ir.rs | 12 +++++++ compiler/mono/src/reset_reuse.rs | 56 +++++++++++++++++++------------- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index e306b7cbd7..a3f77a67f7 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2042,7 +2042,7 @@ fn update<'a>( } MadeSpecializations { module_id, - ident_ids, + mut ident_ids, subs, procedures, external_specializations_requested, @@ -2065,6 +2065,13 @@ fn update<'a>( && state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations { + Proc::insert_reset_reuse_operations( + arena, + module_id, + &mut ident_ids, + &mut state.procedures, + ); + // display the mono IR of the module, for debug purposes if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS { let procs_string = state diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index d589571b5f..0a485d309e 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -227,6 +227,18 @@ impl<'a> Proc<'a> { } } + pub fn insert_reset_reuse_operations<'i>( + arena: &'a Bump, + home: ModuleId, + ident_ids: &'i mut IdentIds, + procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, + ) { + for (key, proc) in procs.iter_mut() { + let new_proc = crate::reset_reuse::insert_reset_reuse(arena, home, ident_ids, proc.clone()); + *proc = new_proc; + } + } + pub fn optimize_refcount_operations<'i, T>( arena: &'a Bump, home: ModuleId, diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 67f17ed112..506bc6da20 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -1,5 +1,5 @@ use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveVarSet}; -use crate::ir::{Call, Expr, Proc, Stmt}; +use crate::ir::{Call, Expr, Proc, Stmt, BranchInfo}; use crate::layout::{Layout, UnionLayout}; use bumpalo::collections::Vec; use bumpalo::Bump; @@ -407,17 +407,20 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> for (tag, info, body) in branches.iter() { let temp = function_r(env, body); - // TODO the branch info stores the tag ID - let new_body = match benefits_from_reuse { - Some(union_layout) => { - let x = *cond_symbol; - let ctor_info = CtorInfo { - layout: *union_layout, - id: 0, - }; - function_d(env, x, &ctor_info, temp) - } - None => temp, + let new_body = match info { + BranchInfo::None => temp, + BranchInfo::Constructor { scrutinee, layout, tag_id } => { + match layout { + Layout::Union(union_layout) => { + let ctor_info = CtorInfo { + layout: *union_layout, + id: *tag_id, + }; + function_d(env, *scrutinee, &ctor_info, temp) + }, + _ => temp, + } + }, }; new_branches.push((*tag, info.clone(), new_body.clone())); @@ -427,16 +430,20 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let (info, body) = default_branch; let temp = function_r(env, body); - let new_body = match benefits_from_reuse { - Some(union_layout) => { - let x = *cond_symbol; - let ctor_info = CtorInfo { - layout: *union_layout, - id: 0, - }; - function_d(env, x, &ctor_info, temp) - } - None => temp, + let new_body = match info { + BranchInfo::None => temp, + BranchInfo::Constructor { scrutinee, layout, tag_id } => { + match layout { + Layout::Union(union_layout) => { + let ctor_info = CtorInfo { + layout: *union_layout, + id: *tag_id, + }; + function_d(env, *scrutinee, &ctor_info, temp) + }, + _ => temp, + } + }, }; (info.clone(), new_body) @@ -493,7 +500,10 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> pass, fail, exception_id, - } => todo!(), + } => { + // TODO implement this + stmt + } Refcounting(modify_rc, continuation) => { let b = function_r(env, continuation); From ee67ee546aeec5185669c3141675fecae3f21c5c Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 3 Jul 2021 01:13:50 +0200 Subject: [PATCH 04/61] thread reset-reuse through --- compiler/gen_llvm/src/llvm/build.rs | 16 ++++- compiler/load/src/file.rs | 4 +- compiler/mono/src/alias_analysis.rs | 17 ++++- compiler/mono/src/ir.rs | 6 +- compiler/mono/src/reset_reuse.rs | 97 ++++++++++++++++------------- 5 files changed, 85 insertions(+), 55 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 9100f2bc64..cd2a76e585 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -970,15 +970,25 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into() } - Tag { + Reuse { + arguments, + tag_layout: union_layout, + tag_id, + .. + } + | Tag { arguments, tag_layout: union_layout, tag_id, .. } => build_tag(env, scope, union_layout, *tag_id, arguments), - Reset(_) => todo!(), - Reuse { .. } => todo!(), + Reset(_) => { + // 1. fetch refcount + // 2. if rc == 1, reset the value + // TODO + env.context.i64_type().const_zero().into() + } StructAtIndex { index, structure, .. diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index a3f77a67f7..7c1a69c05f 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2072,6 +2072,8 @@ fn update<'a>( &mut state.procedures, ); + Proc::insert_refcount_operations(arena, &mut state.procedures); + // display the mono IR of the module, for debug purposes if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS { let procs_string = state @@ -2085,8 +2087,6 @@ fn update<'a>( println!("{}", result); } - Proc::insert_refcount_operations(arena, &mut state.procedures); - // This is not safe with the new non-recursive RC updates that we do for tag unions // // Proc::optimize_refcount_operations( diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index a439cd62e0..7d3c509d70 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -832,7 +832,14 @@ fn expr_spec( match expr { Literal(literal) => literal_spec(builder, block, literal), Call(call) => call_spec(builder, env, block, layout, call), - Tag { + Reuse { + tag_layout, + tag_name: _, + tag_id, + arguments, + .. + } + | Tag { tag_layout, tag_name: _, tag_id, @@ -914,8 +921,12 @@ fn expr_spec( Err(()) => unreachable!("empty array does not have a list layout"), } } - Reuse { .. } => todo!("currently unused"), - Reset(_) => todo!("currently unused"), + Reset(symbol) => { + let type_id = layout_spec(builder, layout)?; + let value_id = env.symbols[symbol]; + + builder.add_unknown_with(block, &[value_id], type_id) + } RuntimeErrorFunction(_) => { let type_id = layout_spec(builder, layout)?; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0a485d309e..a99f6ee844 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -234,7 +234,8 @@ impl<'a> Proc<'a> { procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, ) { for (key, proc) in procs.iter_mut() { - let new_proc = crate::reset_reuse::insert_reset_reuse(arena, home, ident_ids, proc.clone()); + let new_proc = + crate::reset_reuse::insert_reset_reuse(arena, home, ident_ids, proc.clone()); *proc = new_proc; } } @@ -1287,11 +1288,12 @@ impl<'a> Expr<'a> { alloc .text("Reuse ") .append(symbol_to_doc(alloc, *symbol)) + .append(alloc.space()) .append(doc_tag) .append(alloc.space()) .append(alloc.intersperse(it, " ")) } - Reset(symbol) => alloc.text("Reuse ").append(symbol_to_doc(alloc, *symbol)), + Reset(symbol) => alloc.text("Reset ").append(symbol_to_doc(alloc, *symbol)), Struct(args) => { let it = args.iter().map(|s| symbol_to_doc(alloc, *s)); diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 506bc6da20..39464fadfc 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -1,5 +1,5 @@ use crate::inc_dec::{collect_stmt, occurring_variables_expr, JPLiveVarMap, LiveVarSet}; -use crate::ir::{Call, Expr, Proc, Stmt, BranchInfo}; +use crate::ir::{BranchInfo, Call, Expr, Proc, Stmt}; use crate::layout::{Layout, UnionLayout}; use bumpalo::collections::Vec; use bumpalo::Bump; @@ -25,15 +25,16 @@ pub fn insert_reset_reuse<'a, 'i>( proc } +#[derive(Debug)] struct CtorInfo<'a> { id: u8, layout: UnionLayout<'a>, } fn may_reuse(tag_layout: UnionLayout, tag_id: u8, other: &CtorInfo) -> bool { - if tag_layout != other.layout { - return false; - } + // if tag_layout != other.layout { + // return false; + // } // if the tag id is represented as NULL, there is no memory to re-use match tag_layout { @@ -248,32 +249,34 @@ fn function_d_main<'a, 'i>( let arena = env.arena; match stmt { - Let(symbol, expr, layout, continuation) => match expr { - Expr::Tag { arguments, .. } if arguments.iter().any(|s| *s == x) => { - // If the scrutinee `x` (the one that is providing memory) is being - // stored in a constructor, then reuse will probably not be able to reuse memory at runtime. - // It may work only if the new cell is consumed, but we ignore this case. - (stmt, true) - } - _ => { - let (b, found) = function_d_main(env, x, c, continuation); + Let(symbol, expr, layout, continuation) => { + match expr { + Expr::Tag { arguments, .. } if arguments.iter().any(|s| *s == x) => { + // If the scrutinee `x` (the one that is providing memory) is being + // stored in a constructor, then reuse will probably not be able to reuse memory at runtime. + // It may work only if the new cell is consumed, but we ignore this case. + (stmt, true) + } + _ => { + let (b, found) = function_d_main(env, x, c, continuation); - let mut result = MutSet::default(); - if found || { - occurring_variables_expr(expr, &mut result); - !result.contains(&x) - } { - let let_stmt = Let(*symbol, expr.clone(), *layout, b); + let mut result = MutSet::default(); + if found || { + occurring_variables_expr(expr, &mut result); + !result.contains(&x) + } { + let let_stmt = Let(*symbol, expr.clone(), *layout, b); - (arena.alloc(let_stmt), found) - } else { - let b = try_function_s(env, x, c, b); - let let_stmt = Let(*symbol, expr.clone(), *layout, b); + (arena.alloc(let_stmt), found) + } else { + let b = try_function_s(env, x, c, b); + let let_stmt = Let(*symbol, expr.clone(), *layout, b); - (arena.alloc(let_stmt), found) + (arena.alloc(let_stmt), found) + } } } - }, + } Invoke { symbol, call, @@ -409,17 +412,19 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let new_body = match info { BranchInfo::None => temp, - BranchInfo::Constructor { scrutinee, layout, tag_id } => { - match layout { - Layout::Union(union_layout) => { - let ctor_info = CtorInfo { - layout: *union_layout, - id: *tag_id, - }; - function_d(env, *scrutinee, &ctor_info, temp) - }, - _ => temp, + BranchInfo::Constructor { + scrutinee, + layout, + tag_id, + } => match layout { + Layout::Union(union_layout) => { + let ctor_info = CtorInfo { + layout: *union_layout, + id: *tag_id, + }; + function_d(env, *scrutinee, &ctor_info, temp) } + _ => temp, }, }; @@ -432,17 +437,19 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let new_body = match info { BranchInfo::None => temp, - BranchInfo::Constructor { scrutinee, layout, tag_id } => { - match layout { - Layout::Union(union_layout) => { - let ctor_info = CtorInfo { - layout: *union_layout, - id: *tag_id, - }; - function_d(env, *scrutinee, &ctor_info, temp) - }, - _ => temp, + BranchInfo::Constructor { + scrutinee, + layout, + tag_id, + } => match layout { + Layout::Union(union_layout) => { + let ctor_info = CtorInfo { + layout: *union_layout, + id: *tag_id, + }; + function_d(env, *scrutinee, &ctor_info, temp) } + _ => temp, }, }; From 5250e930aa77f59ef3ed7a37f10a78fd5a84908d Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 3 Jul 2021 13:43:04 +0200 Subject: [PATCH 05/61] don't unroll recursive layouts --- compiler/mono/src/layout.rs | 20 +++++++++++++++++--- compiler/mono/src/reset_reuse.rs | 22 +++++++++++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 89ea62d0b2..9b7e6de623 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1508,6 +1508,18 @@ fn get_recursion_var(subs: &Subs, var: Variable) -> Option { } } +fn is_recursive_tag_union(layout: &Layout) -> bool { + match layout { + Layout::Union( + UnionLayout::NullableUnwrapped { .. } + | UnionLayout::Recursive(_) + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NonNullableUnwrapped { .. }, + ) => true, + _ => false, + } +} + pub fn union_sorted_tags_help<'a>( arena: &'a Bump, mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec)>, @@ -1623,10 +1635,12 @@ pub fn union_sorted_tags_help<'a>( for var in arguments { match Layout::from_var(&mut env, var) { Ok(layout) => { - // Drop any zero-sized arguments like {} - if !layout.is_dropped_because_empty() { - has_any_arguments = true; + has_any_arguments = true; + // make sure to not unroll recursive types! + if opt_rec_var.is_some() && is_recursive_tag_union(&layout) { + arg_layouts.push(Layout::RecursivePointer); + } else { arg_layouts.push(layout); } } diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 39464fadfc..5ec9fa8244 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -32,20 +32,24 @@ struct CtorInfo<'a> { } fn may_reuse(tag_layout: UnionLayout, tag_id: u8, other: &CtorInfo) -> bool { - // if tag_layout != other.layout { - // return false; - // } + if tag_layout != other.layout { + return false; + } - // if the tag id is represented as NULL, there is no memory to re-use match tag_layout { UnionLayout::NonRecursive(_) | UnionLayout::Recursive(_) | UnionLayout::NonNullableUnwrapped(_) => true, - UnionLayout::NullableWrapped { nullable_id, .. } => tag_id as i64 != nullable_id, - UnionLayout::NullableUnwrapped { - nullable_id, - other_fields, - } => (tag_id != 0) != nullable_id, + UnionLayout::NullableWrapped { nullable_id, .. } => { + // if the source tag id is represented as NULL, there is no memory to re-use + // if the current tag id is represented as NULL, then we don't need to re-use the + // memory here and can use it somewhere else + other.id as i64 != nullable_id && tag_id as i64 != nullable_id + } + UnionLayout::NullableUnwrapped { nullable_id, .. } => { + // idem + (other.id != 0) != nullable_id && (tag_id != 0) != nullable_id + } } } From 6f48c9762b58c07f80d01282e3ebfc959e102444 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 3 Jul 2021 14:15:05 +0200 Subject: [PATCH 06/61] consider tags that are represented as NULL --- compiler/mono/src/layout.rs | 10 ++++++++++ compiler/mono/src/reset_reuse.rs | 24 +++++++----------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 9b7e6de623..a4a7876771 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -196,6 +196,16 @@ impl<'a> UnionLayout<'a> { UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, } } + + pub fn tag_is_null(&self, tag_id: u8) -> bool { + match self { + UnionLayout::NonRecursive(_) + | UnionLayout::NonNullableUnwrapped(_) + | UnionLayout::Recursive(_) => false, + UnionLayout::NullableWrapped { nullable_id, .. } => *nullable_id == tag_id as i64, + UnionLayout::NullableUnwrapped { nullable_id, .. } => *nullable_id == (tag_id != 0), + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 5ec9fa8244..6a236316e9 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -36,21 +36,11 @@ fn may_reuse(tag_layout: UnionLayout, tag_id: u8, other: &CtorInfo) -> bool { return false; } - match tag_layout { - UnionLayout::NonRecursive(_) - | UnionLayout::Recursive(_) - | UnionLayout::NonNullableUnwrapped(_) => true, - UnionLayout::NullableWrapped { nullable_id, .. } => { - // if the source tag id is represented as NULL, there is no memory to re-use - // if the current tag id is represented as NULL, then we don't need to re-use the - // memory here and can use it somewhere else - other.id as i64 != nullable_id && tag_id as i64 != nullable_id - } - UnionLayout::NullableUnwrapped { nullable_id, .. } => { - // idem - (other.id != 0) != nullable_id && (tag_id != 0) != nullable_id - } - } + // we should not get here if the tag we matched on is represented as NULL + debug_assert!(!tag_layout.tag_is_null(other.id)); + + // furthermore, we can only use the memory if the tag we're creating is non-NULL + !tag_layout.tag_is_null(tag_id) } #[derive(Debug)] @@ -421,7 +411,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { - Layout::Union(union_layout) => { + Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, id: *tag_id, @@ -446,7 +436,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { - Layout::Union(union_layout) => { + Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, id: *tag_id, From bb0d1522f2705cb256aeb0164a067f0af3f2af78 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 3 Jul 2021 16:05:47 +0200 Subject: [PATCH 07/61] llvm preparations --- compiler/gen_llvm/src/llvm/build.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index cd2a76e585..63649123b2 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -974,20 +974,33 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( arguments, tag_layout: union_layout, tag_id, + symbol, .. + } => { + let reset = load_symbol(scope, symbol); + build_tag(env, scope, union_layout, *tag_id, arguments) } - | Tag { + + Tag { arguments, tag_layout: union_layout, tag_id, .. } => build_tag(env, scope, union_layout, *tag_id, arguments), - Reset(_) => { + Reset(symbol) => { // 1. fetch refcount - // 2. if rc == 1, reset the value - // TODO - env.context.i64_type().const_zero().into() + // 2. if rc == 1, decrement fields, return pointer as opaque pointer + // 3. otherwise, return NULL + let tag_ptr = load_symbol(scope, symbol); + + // return the pointer as an opaque pointer + // env.builder.build_bitcast( + // tag_ptr, + // env.context.i8_type().ptr_type(AddressSpace::Generic), + // "to_opaque", + // ) + tag_ptr } StructAtIndex { From 6d566eaa64d869e30594d3807973e22f81e107b6 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 3 Jul 2021 10:26:09 -0400 Subject: [PATCH 08/61] impl --- compiler/gen_llvm/src/llvm/build.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 63649123b2..8d3a28a119 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -977,8 +977,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( symbol, .. } => { - let reset = load_symbol(scope, symbol); - build_tag(env, scope, union_layout, *tag_id, arguments) + let reset = load_symbol(scope, symbol).into_pointer_value(); + build_tag(env, scope, union_layout, *tag_id, arguments, Some(reset)) } Tag { @@ -986,7 +986,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( tag_layout: union_layout, tag_id, .. - } => build_tag(env, scope, union_layout, *tag_id, arguments), + } => build_tag(env, scope, union_layout, *tag_id, arguments, None), Reset(symbol) => { // 1. fetch refcount @@ -1198,6 +1198,7 @@ pub fn build_tag<'a, 'ctx, 'env>( union_layout: &UnionLayout<'a>, tag_id: u8, arguments: &[Symbol], + reuse_allocation: Option>, ) -> BasicValueEnum<'ctx> { let tag_id_layout = union_layout.tag_id_layout(); @@ -1588,8 +1589,14 @@ pub fn build_tag<'a, 'ctx, 'env>( } // Create the struct_type - let data_ptr = - reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[other_fields]); + let data_ptr = match reuse_allocation { + Some(ptr) => ptr, + None => reserve_with_refcount_union_as_block_of_memory( + env, + *union_layout, + &[other_fields], + ), + }; let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); let struct_ptr = env From 5bba490dd4ad9186e94b8d749a357453b0e309f8 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 3 Jul 2021 10:58:05 -0400 Subject: [PATCH 09/61] implementation cont --- compiler/gen_llvm/src/llvm/build.rs | 85 +++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 11 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 8d3a28a119..263b589475 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -978,7 +978,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( .. } => { let reset = load_symbol(scope, symbol).into_pointer_value(); - build_tag(env, scope, union_layout, *tag_id, arguments, Some(reset)) + build_tag( + env, + scope, + union_layout, + *tag_id, + arguments, + Some(reset), + parent, + ) } Tag { @@ -986,21 +994,41 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( tag_layout: union_layout, tag_id, .. - } => build_tag(env, scope, union_layout, *tag_id, arguments, None), + } => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent), Reset(symbol) => { // 1. fetch refcount // 2. if rc == 1, decrement fields, return pointer as opaque pointer // 3. otherwise, return NULL - let tag_ptr = load_symbol(scope, symbol); + let tag_ptr = load_symbol(scope, symbol).into_pointer_value(); + let null_ptr = tag_ptr.get_type().const_null(); + let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); + let is_unique = refcount_ptr.is_1(env); - // return the pointer as an opaque pointer - // env.builder.build_bitcast( - // tag_ptr, - // env.context.i8_type().ptr_type(AddressSpace::Generic), - // "to_opaque", - // ) - tag_ptr + let ctx = env.context; + let then_block = ctx.append_basic_block(parent, "then"); + let else_block = ctx.append_basic_block(parent, "else"); + let cont_block = ctx.append_basic_block(parent, "cont"); + + env.builder + .build_conditional_branch(is_unique, then_block, else_block); + + { + env.builder.position_at_end(then_block); + env.builder.build_unconditional_branch(cont_block); + } + { + env.builder.position_at_end(else_block); + env.builder.build_unconditional_branch(cont_block); + } + { + env.builder.position_at_end(cont_block); + let phi = env.builder.build_phi(tag_ptr.get_type(), "branch"); + + phi.add_incoming(&[(&tag_ptr, then_block), (&null_ptr, else_block)]); + + phi.as_basic_value() + } } StructAtIndex { @@ -1199,6 +1227,7 @@ pub fn build_tag<'a, 'ctx, 'env>( tag_id: u8, arguments: &[Symbol], reuse_allocation: Option>, + parent: FunctionValue<'ctx>, ) -> BasicValueEnum<'ctx> { let tag_id_layout = union_layout.tag_id_layout(); @@ -1590,7 +1619,41 @@ pub fn build_tag<'a, 'ctx, 'env>( // Create the struct_type let data_ptr = match reuse_allocation { - Some(ptr) => ptr, + Some(ptr) => { + // check if its a null pointer + let is_null_ptr = env.builder.build_is_null(ptr, "is_null_ptr"); + let ctx = env.context; + let then_block = ctx.append_basic_block(parent, "then"); + let else_block = ctx.append_basic_block(parent, "else"); + let cont_block = ctx.append_basic_block(parent, "cont"); + + env.builder + .build_conditional_branch(is_null_ptr, then_block, else_block); + + let raw_ptr = { + env.builder.position_at_end(then_block); + let raw_ptr = reserve_with_refcount_union_as_block_of_memory( + env, + *union_layout, + &[other_fields], + ); + env.builder.build_unconditional_branch(cont_block); + raw_ptr + }; + let reuse_ptr = { + env.builder.position_at_end(else_block); + env.builder.build_unconditional_branch(cont_block); + ptr + }; + { + env.builder.position_at_end(cont_block); + let phi = env.builder.build_phi(raw_ptr.get_type(), "branch"); + + phi.add_incoming(&[(&raw_ptr, then_block), (&reuse_ptr, else_block)]); + + phi.as_basic_value().into_pointer_value() + } + } None => reserve_with_refcount_union_as_block_of_memory( env, *union_layout, From ee52ce19ad77f2c46844f6d15087bca2cbfc17a2 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 3 Jul 2021 11:53:02 -0400 Subject: [PATCH 10/61] fix reset_reuse --- compiler/mono/src/reset_reuse.rs | 33 ++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 6a236316e9..19bd2d2e2b 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -278,7 +278,30 @@ fn function_d_main<'a, 'i>( pass, fail, exception_id, - } => todo!(), + } => { + if has_live_var(&env.jp_live_vars, stmt, x) { + let new_pass = { + let temp = function_d_main(env, x, c, pass); + function_d_finalize(env, x, c, temp) + }; + let new_fail = { + let temp = function_d_main(env, x, c, fail); + function_d_finalize(env, x, c, temp) + }; + let new_switch = Invoke { + symbol: *symbol, + call: call.clone(), + layout: *layout, + pass: new_pass, + fail: new_fail, + exception_id: *exception_id, + }; + + (arena.alloc(new_switch), true) + } else { + (stmt, false) + } + } Switch { cond_symbol, cond_layout, @@ -586,7 +609,7 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool { match expr { Expr::Literal(_) => false, - Expr::Call(_) => todo!(), + Expr::Call(call) => has_live_var_call(call, needle), Expr::Array { elems: fields, .. } | Expr::Tag { arguments: fields, .. @@ -596,8 +619,10 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool { | Expr::GetTagId { structure, .. } | Expr::UnionAtIndex { structure, .. } => *structure == needle, Expr::EmptyArray => false, - Expr::Reuse { .. } => unreachable!("not introduced"), - Expr::Reset(_) => unreachable!("not introduced"), + Expr::Reuse { + symbol, arguments, .. + } => needle == *symbol || arguments.iter().any(|s| *s == needle), + Expr::Reset(symbol) => needle == *symbol, Expr::RuntimeErrorFunction(_) => false, } } From 048326f9a381184a8e1cb4f7181ccf3427690763 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 4 Jul 2021 14:20:44 +0200 Subject: [PATCH 11/61] build reset WIP --- compiler/gen_llvm/src/llvm/build.rs | 27 ++- compiler/gen_llvm/src/llvm/refcounting.rs | 228 +++++++++++++++++++++- examples/benchmarks/CFold.roc | 43 +++- 3 files changed, 287 insertions(+), 11 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 263b589475..868ec56059 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -23,7 +23,7 @@ use crate::llvm::convert::{ ptr_int, }; use crate::llvm::refcounting::{ - decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, + build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, }; use bumpalo::collections::Vec; use bumpalo::Bump; @@ -1000,7 +1000,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( // 1. fetch refcount // 2. if rc == 1, decrement fields, return pointer as opaque pointer // 3. otherwise, return NULL - let tag_ptr = load_symbol(scope, symbol).into_pointer_value(); + let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol); + let tag_ptr = tag_ptr.into_pointer_value(); let null_ptr = tag_ptr.get_type().const_null(); let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); let is_unique = refcount_ptr.is_1(env); @@ -1013,12 +1014,34 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env.builder .build_conditional_branch(is_unique, then_block, else_block); + let union_layout = match layout { + Layout::Union(ul) => ul, + _ => unreachable!(), + }; + { env.builder.position_at_end(then_block); + match build_reset(env, layout_ids, union_layout) { + Some(reset_function) => { + let call = + env.builder + .build_call(reset_function, &[tag_ptr.into()], "call_reuse"); + + call.set_call_convention(FAST_CALL_CONV); + + let result = call.try_as_basic_value(); + + dbg!(result); + } + None => { + panic!("") + } + } env.builder.build_unconditional_branch(cont_block); } { env.builder.position_at_end(else_block); + refcount_ptr.decrement(env, layout); env.builder.build_unconditional_branch(cont_block); } { diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 4dd072f622..32b5ad8050 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1353,12 +1353,18 @@ fn build_rec_union_help<'a, 'ctx, 'env>( value_ptr, refcount_ptr, do_recurse_block, + DecOrReuse::Dec, ) } } } } +enum DecOrReuse { + Dec, + Reuse, +} + #[allow(clippy::too_many_arguments)] fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -1371,6 +1377,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( value_ptr: PointerValue<'ctx>, refcount_ptr: PointerToRefcount<'ctx>, match_block: BasicBlock<'ctx>, + decrement_or_reuse: DecOrReuse, ) { let mode = Mode::Dec; let call_mode = mode_to_call_mode(decrement_fn, mode); @@ -1486,7 +1493,12 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( // lists. To achieve it, we must first load all fields that we want to inc/dec (done above) // and store them on the stack, then modify (and potentially free) the current cell, then // actually inc/dec the fields. - refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + match decrement_or_reuse { + DecOrReuse::Reuse => {} + DecOrReuse::Dec => { + refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + } + } for (field, field_layout) in deferred_nonrec { modify_refcount_layout_help( @@ -1543,6 +1555,220 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( } } +pub fn build_reset<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + union_layout: &UnionLayout<'a>, +) -> Option> { + use UnionLayout::*; + + match union_layout { + NullableWrapped { + other_tags: tags, .. + } => { + let function = build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + tags, + true, + ); + + Some(function) + } + + NullableUnwrapped { other_fields, .. } => { + let function = build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + env.arena.alloc([*other_fields]), + true, + ); + + Some(function) + } + + NonNullableUnwrapped(fields) => { + let function = build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + &*env.arena.alloc([*fields]), + true, + ); + Some(function) + } + + Recursive(tags) => { + let function = build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + tags, + false, + ); + Some(function) + } + + NonRecursive(_) => { + unreachable!("non-recursive tags cannot be reused") + } + } +} + +fn build_reuse_rec_union<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + when_recursive: &WhenRecursive<'a>, + union_layout: UnionLayout<'a>, + tags: &'a [&'a [Layout<'a>]], + is_nullable: bool, +) -> FunctionValue<'ctx> { + let mode = Mode::Dec; + + let layout_id = layout_ids.get(Symbol::DEC, &Layout::Union(union_layout)); + let fn_name = layout_id.to_symbol_string(Symbol::DEC, &env.interns); + let fn_name = format!("{}_reset", fn_name); + + let dec_function = build_rec_union( + env, + layout_ids, + Mode::Dec, + when_recursive, + union_layout, + tags, + is_nullable, + ); + + let function = match env.module.get_function(fn_name.as_str()) { + Some(function_value) => function_value, + None => { + let block = env.builder.get_insert_block().expect("to be in a function"); + let di_location = env.builder.get_current_debug_location().unwrap(); + + let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout)); + let function_value = build_header(env, basic_type, mode, &fn_name); + + build_reuse_rec_union_help( + env, + layout_ids, + when_recursive, + union_layout, + tags, + function_value, + dec_function, + is_nullable, + ); + + env.builder.position_at_end(block); + env.builder + .set_current_debug_location(env.context, di_location); + + function_value + } + }; + + function +} + +#[allow(clippy::too_many_arguments)] +fn build_reuse_rec_union_help<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + when_recursive: &WhenRecursive<'a>, + union_layout: UnionLayout<'a>, + tags: &'a [&'a [roc_mono::layout::Layout<'a>]], + reset_function: FunctionValue<'ctx>, + dec_function: FunctionValue<'ctx>, + is_nullable: bool, +) { + debug_assert!(!tags.is_empty()); + + let context = &env.context; + let builder = env.builder; + + // Add a basic block for the entry point + let entry = context.append_basic_block(reset_function, "entry"); + + builder.position_at_end(entry); + + debug_info_init!(env, reset_function); + + // Add args to scope + let arg_symbol = Symbol::ARG_1; + + let arg_val = reset_function.get_param_iter().next().unwrap(); + + arg_val.set_name(arg_symbol.ident_string(&env.interns)); + + let parent = reset_function; + + debug_assert!(arg_val.is_pointer_value()); + let value_ptr = arg_val.into_pointer_value(); + + // to increment/decrement the cons-cell itself + let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr); + let call_mode = CallMode::Dec; + + let should_recurse_block = env.context.append_basic_block(parent, "should_recurse"); + + let ctx = env.context; + if is_nullable { + let is_null = env.builder.build_is_null(value_ptr, "is_null"); + + let then_block = ctx.append_basic_block(parent, "then"); + + env.builder + .build_conditional_branch(is_null, then_block, should_recurse_block); + + { + env.builder.position_at_end(then_block); + env.builder.build_return(None); + } + } else { + env.builder.build_unconditional_branch(should_recurse_block); + } + + env.builder.position_at_end(should_recurse_block); + + let layout = Layout::Union(union_layout); + + let do_recurse_block = env.context.append_basic_block(parent, "do_recurse"); + let no_recurse_block = env.context.append_basic_block(parent, "no_recurse"); + + builder.build_conditional_branch(refcount_ptr.is_1(env), do_recurse_block, no_recurse_block); + + { + env.builder.position_at_end(no_recurse_block); + + refcount_ptr.modify(call_mode, &layout, env); + env.builder.build_return(None); + } + + { + env.builder.position_at_end(do_recurse_block); + + build_rec_union_recursive_decrement( + env, + layout_ids, + when_recursive, + parent, + dec_function, + union_layout, + tags, + value_ptr, + refcount_ptr, + do_recurse_block, + DecOrReuse::Reuse, + ) + } +} + fn function_name_from_mode<'a>( layout_ids: &mut LayoutIds<'a>, interns: &Interns, diff --git a/examples/benchmarks/CFold.roc b/examples/benchmarks/CFold.roc index fb4fa4eeca..fbc37a73da 100644 --- a/examples/benchmarks/CFold.roc +++ b/examples/benchmarks/CFold.roc @@ -8,15 +8,42 @@ app "cfold" main : Task.Task {} [] main = Task.after Task.getInt \n -> - e = mkExpr n 1 # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20) - unoptimized = eval e - optimized = eval (constFolding (reassoc e)) + e : Expr + e = (Add (Add ( Val 3) ( Val 1)) (Add ( Val 1) ( Var 1))) + # e = mkExpr n 1 # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20) - unoptimized - |> Str.fromInt - |> Str.concat " & " - |> Str.concat (Str.fromInt optimized) - |> Task.putLine + # unoptimized = eval e + # optimized = eval (constFolding (reassoc e)) + + Task.putLine (show e) + +# unoptimized +# |> Str.fromInt +# |> Str.concat " & " +# |> Str.concat (Str.fromInt optimized) +# |> Task.putLine + +show : Expr -> Str +show = \expr -> + when expr is + Var v -> + "( Var " + |> Str.concat (Str.fromInt v) + |> Str.concat ")" + Val v -> + "( Val " + |> Str.concat (Str.fromInt v) + |> Str.concat ")" + Add l r -> "(Add " + |> Str.concat (show l) + |> Str.concat " " + |> Str.concat (show r) + |> Str.concat ")" + Mul l r -> "(Mul " + |> Str.concat (show l) + |> Str.concat " " + |> Str.concat (show r) + |> Str.concat ")" Expr : [ Add Expr Expr, From 7be0349eee6c5adab334b4e6a5ab9010c73cde2c Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 12 Jul 2021 15:19:26 +0200 Subject: [PATCH 12/61] refactor, insert reset more intelligently --- compiler/gen_llvm/src/llvm/build.rs | 25 +++++++++------- compiler/mono/src/reset_reuse.rs | 45 +++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 868ec56059..215ceafc80 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -997,29 +997,29 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent), Reset(symbol) => { - // 1. fetch refcount - // 2. if rc == 1, decrement fields, return pointer as opaque pointer - // 3. otherwise, return NULL let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol); let tag_ptr = tag_ptr.into_pointer_value(); - let null_ptr = tag_ptr.get_type().const_null(); - let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); - let is_unique = refcount_ptr.is_1(env); + + // reset is only generated for union values + let union_layout = match layout { + Layout::Union(ul) => ul, + _ => unreachable!(), + }; let ctx = env.context; let then_block = ctx.append_basic_block(parent, "then"); let else_block = ctx.append_basic_block(parent, "else"); let cont_block = ctx.append_basic_block(parent, "cont"); + let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); + let is_unique = refcount_ptr.is_1(env); + env.builder .build_conditional_branch(is_unique, then_block, else_block); - let union_layout = match layout { - Layout::Union(ul) => ul, - _ => unreachable!(), - }; - { + // reset, when used on a unique reference, eagerly decrements the components of the + // referenced value, and returns the location of the now-invalid cell env.builder.position_at_end(then_block); match build_reset(env, layout_ids, union_layout) { Some(reset_function) => { @@ -1040,6 +1040,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(cont_block); } { + // If reset is used on a shared, non-reusable reference, it behaves + // like dec and returns , which instructs reuse to behave like ctor env.builder.position_at_end(else_block); refcount_ptr.decrement(env, layout); env.builder.build_unconditional_branch(cont_block); @@ -1048,6 +1050,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env.builder.position_at_end(cont_block); let phi = env.builder.build_phi(tag_ptr.get_type(), "branch"); + let null_ptr = tag_ptr.get_type().const_null(); phi.add_incoming(&[(&tag_ptr, then_block), (&null_ptr, else_block)]); phi.as_basic_value() diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 19bd2d2e2b..f607b89b75 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -212,12 +212,51 @@ fn try_function_s<'a, 'i>( if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { stmt } else { - let f00 = Stmt::Let(w, Expr::Reset(x), Layout::Union(c.layout), new_stmt); - - env.arena.alloc(f00) + insert_reset(env, w, x, Layout::Union(c.layout), new_stmt) } } +fn insert_reset<'a>( + env: &mut Env<'a, '_>, + w: Symbol, + x: Symbol, + layout: Layout<'a>, + mut stmt: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + use crate::ir::Expr::*; + + let mut stack = vec![]; + + while let Stmt::Let(symbol, expr, expr_layout, rest) = stmt { + match &expr { + StructAtIndex { .. } | GetTagId { .. } | UnionAtIndex { .. } => { + stack.push((symbol, expr, expr_layout)); + stmt = rest; + } + Literal(_) + | Call(_) + | Tag { .. } + | Struct(_) + | Array { .. } + | EmptyArray + | Reuse { .. } + | Reset(_) + | RuntimeErrorFunction(_) => break, + } + } + + let reset_expr = Expr::Reset(x); + stmt = env.arena.alloc(Stmt::Let(w, reset_expr, layout, stmt)); + + for (symbol, expr, expr_layout) in stack.into_iter().rev() { + stmt = env + .arena + .alloc(Stmt::Let(*symbol, expr.clone(), *expr_layout, stmt)); + } + + stmt +} + fn function_d_finalize<'a, 'i>( env: &mut Env<'a, 'i>, x: Symbol, From 09f01ba19350cbddfb7c2f84e65f13c09a4c7296 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 12 Jul 2021 17:01:53 +0200 Subject: [PATCH 13/61] Boxed skeleton --- cli/src/repl/eval.rs | 1 + compiler/gen_llvm/src/llvm/build_hash.rs | 1 + compiler/gen_llvm/src/llvm/compare.rs | 2 ++ compiler/gen_llvm/src/llvm/convert.rs | 8 ++++++++ compiler/gen_llvm/src/llvm/refcounting.rs | 4 ++++ compiler/mono/src/alias_analysis.rs | 1 + compiler/mono/src/layout.rs | 8 ++++++++ compiler/mono/src/reset_reuse.rs | 7 ++++++- 8 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index 7d150a4756..b59ddc38b5 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -73,6 +73,7 @@ fn jit_to_ast_help<'a>( content: &Content, ) -> Result, ToAstProblem> { match layout { + Layout::Boxed(_) => todo!(), Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| { bool_to_ast(env, num, content) })), diff --git a/compiler/gen_llvm/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs index 61a74dd232..bdb6700759 100644 --- a/compiler/gen_llvm/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -45,6 +45,7 @@ fn build_hash_layout<'a, 'ctx, 'env>( when_recursive: WhenRecursive<'a>, ) -> IntValue<'ctx> { match layout { + Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive) } diff --git a/compiler/gen_llvm/src/llvm/compare.rs b/compiler/gen_llvm/src/llvm/compare.rs index 2a9d1d3fa3..8d1be9b870 100644 --- a/compiler/gen_llvm/src/llvm/compare.rs +++ b/compiler/gen_llvm/src/llvm/compare.rs @@ -139,6 +139,7 @@ fn build_eq<'a, 'ctx, 'env>( } match lhs_layout { + Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { build_eq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive) } @@ -296,6 +297,7 @@ fn build_neq<'a, 'ctx, 'env>( } match lhs_layout { + Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { build_neq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive) } diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 1cb299c0ae..ebf384d431 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -79,6 +79,14 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( .as_basic_type_enum() } + Boxed(_) => { + // TODO make this dynamic + env.context + .i64_type() + .ptr_type(AddressSpace::Generic) + .as_basic_type_enum() + } + Builtin(builtin) => basic_type_from_builtin(env, builtin), } } diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 32b5ad8050..3c63d69be7 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -752,6 +752,10 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( Some(function) } }, + Layout::Boxed(_) => { + // TODO + todo!() + } } } diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 7d3c509d70..1fbc958b2b 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -972,6 +972,7 @@ fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result } => worst_case_type(builder), }, RecursivePointer => worst_case_type(builder), + Boxed(_) => worst_case_type(builder), Closure(_, lambda_set, _) => layout_spec(builder, &lambda_set.runtime_representation()), } } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index a4a7876771..c72b0f02c4 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -44,6 +44,8 @@ pub enum Layout<'a> { Union(UnionLayout<'a>), RecursivePointer, + Boxed(&'a Layout<'a>), + /// A function. The types of its arguments, then the type of its return value. Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>), } @@ -588,6 +590,7 @@ impl<'a> Layout<'a> { // We cannot memcpy pointers, because then we would have the same pointer in multiple places! false } + Boxed(_) => false, } } @@ -639,6 +642,7 @@ impl<'a> Layout<'a> { } Closure(_, lambda_set, _) => lambda_set.stack_size(pointer_size), RecursivePointer => pointer_size, + Boxed(_) => pointer_size, } } @@ -669,6 +673,7 @@ impl<'a> Layout<'a> { } Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size), Layout::RecursivePointer => pointer_size, + Layout::Boxed(_) => pointer_size, Layout::Closure(_, captured, _) => { pointer_size.max(captured.alignment_bytes(pointer_size)) } @@ -690,6 +695,7 @@ impl<'a> Layout<'a> { } RecursivePointer => true, + Boxed(_) => true, Builtin(List(_)) | Builtin(Str) => true, @@ -722,6 +728,7 @@ impl<'a> Layout<'a> { } } RecursivePointer => true, + Boxed(inner) => inner.contains_refcounted(), Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), } } @@ -746,6 +753,7 @@ impl<'a> Layout<'a> { } Union(union_layout) => union_layout.to_doc(alloc, parens), RecursivePointer => alloc.text("*self"), + Boxed(_) => alloc.text("unbox"), Closure(args, closure_layout, result) => { let args_doc = args.iter().map(|x| x.to_doc(alloc, Parens::InFunction)); diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index f607b89b75..a19b32113a 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -246,7 +246,12 @@ fn insert_reset<'a>( } let reset_expr = Expr::Reset(x); - stmt = env.arena.alloc(Stmt::Let(w, reset_expr, layout, stmt)); + + const I64: Layout<'static> = Layout::Builtin(crate::layout::Builtin::Int64); + + stmt = env + .arena + .alloc(Stmt::Let(w, reset_expr, Layout::Boxed(&I64), stmt)); for (symbol, expr, expr_layout) in stack.into_iter().rev() { stmt = env From 5cfc4b01c9e31e4524b6fa4a67b1b28cd2b49f3c Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 12 Jul 2021 22:04:04 +0200 Subject: [PATCH 14/61] don't decrement when reusing --- compiler/gen_llvm/src/llvm/refcounting.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 3c63d69be7..06b550e429 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1552,7 +1552,9 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( env.builder.position_at_end(merge_block); // increment/decrement the cons-cell itself - refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + if let DecOrReuse::Dec = decrement_or_reuse { + refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + } // this function returns void builder.build_return(None); From 63e1374aaade10872b3f6f2fecd085ca61b6ff14 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 00:40:55 +0200 Subject: [PATCH 15/61] factor allocating space for tag out --- compiler/gen_llvm/src/llvm/build.rs | 94 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 215ceafc80..60492a1e65 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1384,8 +1384,7 @@ pub fn build_tag<'a, 'ctx, 'env>( } // Create the struct_type - let raw_data_ptr = - reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags); + let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); let tag_id_ptr = builder .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") @@ -1547,8 +1546,7 @@ pub fn build_tag<'a, 'ctx, 'env>( } // Create the struct_type - let raw_data_ptr = - reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags); + let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); let tag_id_ptr = builder .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") @@ -1644,48 +1642,8 @@ pub fn build_tag<'a, 'ctx, 'env>( } // Create the struct_type - let data_ptr = match reuse_allocation { - Some(ptr) => { - // check if its a null pointer - let is_null_ptr = env.builder.build_is_null(ptr, "is_null_ptr"); - let ctx = env.context; - let then_block = ctx.append_basic_block(parent, "then"); - let else_block = ctx.append_basic_block(parent, "else"); - let cont_block = ctx.append_basic_block(parent, "cont"); - - env.builder - .build_conditional_branch(is_null_ptr, then_block, else_block); - - let raw_ptr = { - env.builder.position_at_end(then_block); - let raw_ptr = reserve_with_refcount_union_as_block_of_memory( - env, - *union_layout, - &[other_fields], - ); - env.builder.build_unconditional_branch(cont_block); - raw_ptr - }; - let reuse_ptr = { - env.builder.position_at_end(else_block); - env.builder.build_unconditional_branch(cont_block); - ptr - }; - { - env.builder.position_at_end(cont_block); - let phi = env.builder.build_phi(raw_ptr.get_type(), "branch"); - - phi.add_incoming(&[(&raw_ptr, then_block), (&reuse_ptr, else_block)]); - - phi.as_basic_value().into_pointer_value() - } - } - None => reserve_with_refcount_union_as_block_of_memory( - env, - *union_layout, - &[other_fields], - ), - }; + let data_ptr = + allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]); let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); let struct_ptr = env @@ -1711,6 +1669,50 @@ pub fn build_tag<'a, 'ctx, 'env>( } } +fn allocate_tag<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + parent: FunctionValue<'ctx>, + reuse_allocation: Option>, + union_layout: &UnionLayout<'a>, + tags: &[&[Layout<'a>]], +) -> PointerValue<'ctx> { + match reuse_allocation { + Some(ptr) => { + // check if its a null pointer + let is_null_ptr = env.builder.build_is_null(ptr, "is_null_ptr"); + let ctx = env.context; + let then_block = ctx.append_basic_block(parent, "then_allocate_fresh"); + let else_block = ctx.append_basic_block(parent, "else_reuse"); + let cont_block = ctx.append_basic_block(parent, "cont"); + + env.builder + .build_conditional_branch(is_null_ptr, then_block, else_block); + + let raw_ptr = { + env.builder.position_at_end(then_block); + let raw_ptr = + reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags); + env.builder.build_unconditional_branch(cont_block); + raw_ptr + }; + let reuse_ptr = { + env.builder.position_at_end(else_block); + env.builder.build_unconditional_branch(cont_block); + ptr + }; + { + env.builder.position_at_end(cont_block); + let phi = env.builder.build_phi(raw_ptr.get_type(), "branch"); + + phi.add_incoming(&[(&raw_ptr, then_block), (&reuse_ptr, else_block)]); + + phi.as_basic_value().into_pointer_value() + } + } + None => reserve_with_refcount_union_as_block_of_memory(env, *union_layout, tags), + } +} + pub fn get_tag_id<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, From d80deb37cd168fae375c677f15bad7581b3495cf Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 00:41:46 +0200 Subject: [PATCH 16/61] cleanup --- compiler/gen_llvm/src/llvm/build.rs | 6 ++---- compiler/mono/src/inc_dec.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 60492a1e65..ac21dcb9ec 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1029,9 +1029,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( call.set_call_convention(FAST_CALL_CONV); - let result = call.try_as_basic_value(); - - dbg!(result); + let _ = call.try_as_basic_value(); } None => { panic!("") @@ -1868,7 +1866,7 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>( builder.build_bitcast( result, opaque_wrapper_type.ptr_type(AddressSpace::Generic), - "cast_rec_pointer_lookup_at_index_ptr", + "cast_rec_pointer_lookup_at_index_ptr_new", ) } else { result diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index bff0872656..1ab137d5b9 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -753,12 +753,6 @@ impl<'a> Context<'a> { arguments, }) => self.visit_call(z, call_type, arguments, l, b, b_live_vars), - EmptyArray | Literal(_) | Reset(_) | RuntimeErrorFunction(_) => { - // EmptyArray is always stack-allocated - // function pointers are persistent - self.arena.alloc(Stmt::Let(z, v, l, b)) - } - StructAtIndex { structure: x, .. } => { let b = self.add_dec_if_needed(x, b, b_live_vars); let info_x = self.get_var_info(x); @@ -794,6 +788,12 @@ impl<'a> Context<'a> { self.arena.alloc(Stmt::Let(z, v, l, b)) } + + EmptyArray | Literal(_) | Reset(_) | RuntimeErrorFunction(_) => { + // EmptyArray is always stack-allocated + // function pointers are persistent + self.arena.alloc(Stmt::Let(z, v, l, b)) + } }; (new_b, live_vars) From 3bf46ff662cd792eb1b19e9f78138b3475f03f48 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 00:42:02 +0200 Subject: [PATCH 17/61] restore cfold --- examples/benchmarks/CFold.roc | 44 +++++++---------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/examples/benchmarks/CFold.roc b/examples/benchmarks/CFold.roc index fbc37a73da..7c17abf066 100644 --- a/examples/benchmarks/CFold.roc +++ b/examples/benchmarks/CFold.roc @@ -8,42 +8,15 @@ app "cfold" main : Task.Task {} [] main = Task.after Task.getInt \n -> - e : Expr - e = (Add (Add ( Val 3) ( Val 1)) (Add ( Val 1) ( Var 1))) - # e = mkExpr n 1 # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20) + e = mkExpr n 1 # original koka n = 20 (set `ulimit -s unlimited` to avoid stack overflow for n = 20) + unoptimized = eval e + optimized = eval (constFolding (reassoc e)) - # unoptimized = eval e - # optimized = eval (constFolding (reassoc e)) - - Task.putLine (show e) - -# unoptimized -# |> Str.fromInt -# |> Str.concat " & " -# |> Str.concat (Str.fromInt optimized) -# |> Task.putLine - -show : Expr -> Str -show = \expr -> - when expr is - Var v -> - "( Var " - |> Str.concat (Str.fromInt v) - |> Str.concat ")" - Val v -> - "( Val " - |> Str.concat (Str.fromInt v) - |> Str.concat ")" - Add l r -> "(Add " - |> Str.concat (show l) - |> Str.concat " " - |> Str.concat (show r) - |> Str.concat ")" - Mul l r -> "(Mul " - |> Str.concat (show l) - |> Str.concat " " - |> Str.concat (show r) - |> Str.concat ")" + unoptimized + |> Str.fromInt + |> Str.concat " & " + |> Str.concat (Str.fromInt optimized) + |> Task.putLine Expr : [ Add Expr Expr, @@ -124,4 +97,3 @@ constFolding = \e -> Pair y1 y2 -> Add y1 y2 _ -> e - From 83bea1582f06dc810175a1688c059a670f391fe6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 00:42:21 +0200 Subject: [PATCH 18/61] box fixes --- compiler/gen_llvm/src/llvm/build.rs | 27 ++++++++++++++++++++++++++- compiler/mono/src/layout.rs | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index ac21dcb9ec..6671d2a8b6 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -2489,7 +2489,32 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( Dec(symbol) => { let (value, layout) = load_symbol_and_layout(scope, symbol); - if layout.contains_refcounted() { + if let Layout::Boxed(_) = layout { + if value.is_pointer_value() { + let value_ptr = value.into_pointer_value(); + + let then_block = env.context.append_basic_block(parent, "then"); + let done_block = env.context.append_basic_block(parent, "done"); + + let condition = + env.builder.build_is_not_null(value_ptr, "box_is_not_null"); + env.builder + .build_conditional_branch(condition, then_block, done_block); + + { + env.builder.position_at_end(then_block); + let refcount_ptr = + PointerToRefcount::from_ptr_to_data(env, value_ptr); + refcount_ptr.decrement(env, layout); + + env.builder.build_unconditional_branch(done_block); + } + + env.builder.position_at_end(done_block); + } else { + eprint!("we're likely leaking memory; see issue #985 for details"); + } + } else if layout.contains_refcounted() { decrement_refcount_layout(env, parent, layout_ids, value, layout); } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index c72b0f02c4..6c814579e2 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -728,7 +728,7 @@ impl<'a> Layout<'a> { } } RecursivePointer => true, - Boxed(inner) => inner.contains_refcounted(), + Boxed(inner) => true, Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), } } From b28b6b89c09839dbdf87775c97eb61e59a0e1a86 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 23:40:53 +0200 Subject: [PATCH 19/61] update RBTreeDel.roc --- examples/benchmarks/RBTreeDel.roc | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/examples/benchmarks/RBTreeDel.roc b/examples/benchmarks/RBTreeDel.roc index d9b780fa39..ccb1753042 100644 --- a/examples/benchmarks/RBTreeDel.roc +++ b/examples/benchmarks/RBTreeDel.roc @@ -38,12 +38,12 @@ makeMapHelp = \total, n, m -> n1 = n - 1 powerOf10 = - (n % 10 |> resultWithDefault 0) == 0 + n |> Num.isMultipleOf 10 t1 = insert m n powerOf10 isFrequency = - (n % 4 |> resultWithDefault 0) == 0 + n |> Num.isMultipleOf 4 key = n1 + ((total - n1) // 5 |> resultWithDefault 0) t2 = if isFrequency then delete t1 key else t1 @@ -85,8 +85,6 @@ isRed = \tree -> Node Red _ _ _ _ -> True _ -> False -lt = \x, y -> x < y - ins : Tree I64 Bool, I64, Bool -> Tree I64 Bool ins = \tree, kx, vx -> when tree is @@ -94,19 +92,24 @@ ins = \tree, kx, vx -> Node Red Leaf kx vx Leaf Node Red a ky vy b -> - if lt kx ky then - Node Red (ins a kx vx) ky vy b - else if lt ky kx then - Node Red a ky vy (ins b kx vx) - else - Node Red a ky vy (ins b kx vx) + when Num.compare kx ky is + LT -> Node Red (ins a kx vx) ky vy b + GT -> Node Red a ky vy (ins b kx vx) + EQ -> Node Red a ky vy (ins b kx vx) Node Black a ky vy b -> - if lt kx ky then - (if isRed a then balanceLeft (ins a kx vx) ky vy b else Node Black (ins a kx vx) ky vy b) - else if lt ky kx then - (if isRed b then balanceRight a ky vy (ins b kx vx) else Node Black a ky vy (ins b kx vx)) - else Node Black a kx vx b + when Num.compare kx ky is + LT -> + when isRed a is + True -> balanceLeft (ins a kx vx) ky vy b + False -> Node Black (ins a kx vx) ky vy b + + GT -> + when isRed b is + True -> balanceRight a ky vy (ins b kx vx) + False -> Node Black a ky vy (ins b kx vx) + EQ -> + Node Black a kx vx b balanceLeft : Tree a b, a, b, Tree a b -> Tree a b balanceLeft = \l, k, v, r -> From e0ce2892add81771e5585c52389499191c9dba08 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 13 Jul 2021 23:45:06 +0200 Subject: [PATCH 20/61] improvements to reset reuse --- compiler/mono/src/reset_reuse.rs | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index a19b32113a..bb5d73c168 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -114,23 +114,21 @@ fn function_s<'a, 'i>( let body: &Stmt = *body; let new_body = function_s(env, w, c, body); - let new_join = if std::ptr::eq(body, new_body) || body == new_body { - // the join point body will consume w - Join { - id, - parameters, - body: new_body, - remainder, - } - } else { + let new_join = if body == new_body { let new_remainder = function_s(env, w, c, remainder); - Join { id, parameters, body, remainder: new_remainder, } + } else { + Join { + id, + parameters, + body: new_body, + remainder, + } }; arena.alloc(new_join) @@ -209,7 +207,7 @@ fn try_function_s<'a, 'i>( let new_stmt = function_s(env, w, c, stmt); - if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { + if stmt == new_stmt { stmt } else { insert_reset(env, w, x, Layout::Union(c.layout), new_stmt) @@ -298,11 +296,16 @@ fn function_d_main<'a, 'i>( _ => { let (b, found) = function_d_main(env, x, c, continuation); + // NOTE the &b != continuation is not found in the Lean source, but is required + // otherwise we observe the same symbol being reset twice let mut result = MutSet::default(); - if found || { - occurring_variables_expr(expr, &mut result); - !result.contains(&x) - } { + if found + || { + occurring_variables_expr(expr, &mut result); + !result.contains(&x) + } + || &b != continuation + { let let_stmt = Let(*symbol, expr.clone(), *layout, b); (arena.alloc(let_stmt), found) @@ -413,10 +416,10 @@ fn function_d_main<'a, 'i>( let (b, found) = function_d_main(env, x, c, remainder); - let (v, _found) = function_d_main(env, x, c, body); - env.jp_live_vars.remove(id); + let (v, _found) = function_d_main(env, x, c, body); + // If `found' == true`, then `Dmain b` must also have returned `(b, true)` since // we assume the IR does not have dead join points. So, if `x` is live in `j` (i.e., `v`), // then it must also live in `b` since `j` is reachable from `b` with a `jmp`. @@ -463,10 +466,6 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let mut new_branches = Vec::with_capacity_in(branches.len(), arena); // TODO for non-recursive unions there is no benefit - let benefits_from_reuse = match cond_layout { - Layout::Union(union_layout) => Some(union_layout), - _ => None, - }; for (tag, info, body) in branches.iter() { let temp = function_r(env, body); @@ -478,6 +477,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { + Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, @@ -503,6 +503,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { + Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, @@ -536,14 +537,13 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> } => { env.jp_live_vars.insert(*id, LiveVarSet::default()); - let body_live_vars = collect_stmt(body, &env.jp_live_vars, LiveVarSet::default()); + let v = function_r(env, body); + let body_live_vars = collect_stmt(v, &env.jp_live_vars, LiveVarSet::default()); env.jp_live_vars.insert(*id, body_live_vars); let b = function_r(env, remainder); - let v = function_r(env, body); - env.jp_live_vars.remove(id); let join = Join { From bcf23d569984ade33d202ddcc435b502dc012cd0 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:30:00 +0200 Subject: [PATCH 21/61] Revert "improvements to reset reuse" This reverts commit e0ce2892add81771e5585c52389499191c9dba08. --- compiler/mono/src/reset_reuse.rs | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index bb5d73c168..a19b32113a 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -114,21 +114,23 @@ fn function_s<'a, 'i>( let body: &Stmt = *body; let new_body = function_s(env, w, c, body); - let new_join = if body == new_body { - let new_remainder = function_s(env, w, c, remainder); - Join { - id, - parameters, - body, - remainder: new_remainder, - } - } else { + let new_join = if std::ptr::eq(body, new_body) || body == new_body { + // the join point body will consume w Join { id, parameters, body: new_body, remainder, } + } else { + let new_remainder = function_s(env, w, c, remainder); + + Join { + id, + parameters, + body, + remainder: new_remainder, + } }; arena.alloc(new_join) @@ -207,7 +209,7 @@ fn try_function_s<'a, 'i>( let new_stmt = function_s(env, w, c, stmt); - if stmt == new_stmt { + if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { stmt } else { insert_reset(env, w, x, Layout::Union(c.layout), new_stmt) @@ -296,16 +298,11 @@ fn function_d_main<'a, 'i>( _ => { let (b, found) = function_d_main(env, x, c, continuation); - // NOTE the &b != continuation is not found in the Lean source, but is required - // otherwise we observe the same symbol being reset twice let mut result = MutSet::default(); - if found - || { - occurring_variables_expr(expr, &mut result); - !result.contains(&x) - } - || &b != continuation - { + if found || { + occurring_variables_expr(expr, &mut result); + !result.contains(&x) + } { let let_stmt = Let(*symbol, expr.clone(), *layout, b); (arena.alloc(let_stmt), found) @@ -416,10 +413,10 @@ fn function_d_main<'a, 'i>( let (b, found) = function_d_main(env, x, c, remainder); - env.jp_live_vars.remove(id); - let (v, _found) = function_d_main(env, x, c, body); + env.jp_live_vars.remove(id); + // If `found' == true`, then `Dmain b` must also have returned `(b, true)` since // we assume the IR does not have dead join points. So, if `x` is live in `j` (i.e., `v`), // then it must also live in `b` since `j` is reachable from `b` with a `jmp`. @@ -466,6 +463,10 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let mut new_branches = Vec::with_capacity_in(branches.len(), arena); // TODO for non-recursive unions there is no benefit + let benefits_from_reuse = match cond_layout { + Layout::Union(union_layout) => Some(union_layout), + _ => None, + }; for (tag, info, body) in branches.iter() { let temp = function_r(env, body); @@ -477,7 +478,6 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { - Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, @@ -503,7 +503,6 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { - Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, @@ -537,13 +536,14 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> } => { env.jp_live_vars.insert(*id, LiveVarSet::default()); - let v = function_r(env, body); + let body_live_vars = collect_stmt(body, &env.jp_live_vars, LiveVarSet::default()); - let body_live_vars = collect_stmt(v, &env.jp_live_vars, LiveVarSet::default()); env.jp_live_vars.insert(*id, body_live_vars); let b = function_r(env, remainder); + let v = function_r(env, body); + env.jp_live_vars.remove(id); let join = Join { From 26b05d18f2aede40867a37bf447222e2e0a52fa2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:40:48 +0200 Subject: [PATCH 22/61] improvements to reset reuse --- compiler/mono/src/reset_reuse.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index a19b32113a..a45d014e5a 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -298,11 +298,16 @@ fn function_d_main<'a, 'i>( _ => { let (b, found) = function_d_main(env, x, c, continuation); + // NOTE the &b != continuation is not found in the Lean source, but is required + // otherwise we observe the same symbol being reset twice let mut result = MutSet::default(); - if found || { - occurring_variables_expr(expr, &mut result); - !result.contains(&x) - } { + if found + || { + occurring_variables_expr(expr, &mut result); + !result.contains(&x) + } + || &b != continuation + { let let_stmt = Let(*symbol, expr.clone(), *layout, b); (arena.alloc(let_stmt), found) @@ -478,6 +483,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { + Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, @@ -503,6 +509,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> layout, tag_id, } => match layout { + Layout::Union(UnionLayout::NonRecursive(_)) => temp, Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { let ctor_info = CtorInfo { layout: *union_layout, From c45637d0fd05f089b483e74983b720675ad8e0ec Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:41:49 +0200 Subject: [PATCH 23/61] cleanup in refcounting --- compiler/gen_llvm/src/llvm/refcounting.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 06b550e429..45f0b94542 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1497,6 +1497,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( // lists. To achieve it, we must first load all fields that we want to inc/dec (done above) // and store them on the stack, then modify (and potentially free) the current cell, then // actually inc/dec the fields. + match decrement_or_reuse { DecOrReuse::Reuse => {} DecOrReuse::Dec => { @@ -1543,17 +1544,19 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( // read the tag_id let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into()); - let merge_block = env.context.append_basic_block(parent, "decrement_merge"); + let default_block = env.context.append_basic_block(parent, "switch_default"); // switch on it env.builder - .build_switch(current_tag_id, merge_block, &cases); + .build_switch(current_tag_id, default_block, &cases); - env.builder.position_at_end(merge_block); + { + env.builder.position_at_end(default_block); - // increment/decrement the cons-cell itself - if let DecOrReuse::Dec = decrement_or_reuse { - refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + // increment/decrement the cons-cell itself + if let DecOrReuse::Dec = decrement_or_reuse { + refcount_ptr.modify(call_mode, &Layout::Union(union_layout), env); + } } // this function returns void From 77015964693c6fc3058f8b57a7651fcdbb16beb3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:47:37 +0200 Subject: [PATCH 24/61] small tweaks to llvm code gen --- compiler/gen_llvm/src/llvm/build.rs | 33 +++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index fb8e3139e1..e21b4e0121 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1021,6 +1021,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( // reset, when used on a unique reference, eagerly decrements the components of the // referenced value, and returns the location of the now-invalid cell env.builder.position_at_end(then_block); + match build_reset(env, layout_ids, union_layout) { Some(reset_function) => { let call = @@ -1035,6 +1036,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( panic!("") } } + env.builder.build_unconditional_branch(cont_block); } { @@ -1162,6 +1164,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr2( env, tag_id_type, + union_layout, field_layouts, *index as usize, argument.into_pointer_value(), @@ -1174,11 +1177,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr( env, + union_layout, field_layouts, *index as usize, argument.into_pointer_value(), struct_type.into_struct_type(), - &struct_layout, ) } UnionLayout::NullableWrapped { @@ -1202,6 +1205,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr2( env, tag_id_type, + union_layout, field_layouts, *index as usize, argument.into_pointer_value(), @@ -1221,12 +1225,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr( env, + union_layout, field_layouts, // the tag id is not stored *index as usize, argument.into_pointer_value(), struct_type.into_struct_type(), - &struct_layout, ) } } @@ -1693,11 +1697,15 @@ fn allocate_tag<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(cont_block); raw_ptr }; + let reuse_ptr = { env.builder.position_at_end(else_block); + env.builder.build_unconditional_branch(cont_block); + ptr }; + { env.builder.position_at_end(cont_block); let phi = env.builder.build_phi(raw_ptr.get_type(), "branch"); @@ -1780,11 +1788,11 @@ pub fn get_tag_id<'a, 'ctx, 'env>( fn lookup_at_index_ptr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + union_layout: &UnionLayout<'a>, field_layouts: &[Layout<'_>], index: usize, value: PointerValue<'ctx>, struct_type: StructType<'ctx>, - structure_layout: &Layout<'_>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; @@ -1806,11 +1814,13 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) { // a recursive field is stored as a `i64*`, to use it we must cast it to // a pointer to the block of memory representation + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); + builder.build_bitcast( result, - block_of_memory(env.context, structure_layout, env.ptr_bytes) - .ptr_type(AddressSpace::Generic), - "cast_rec_pointer_lookup_at_index_ptr", + actual_type, + "cast_rec_pointer_lookup_at_index_ptr_old", ) } else { result @@ -1820,6 +1830,7 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( fn lookup_at_index_ptr2<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, tag_id_type: IntType<'ctx>, + union_layout: &UnionLayout<'a>, field_layouts: &[Layout<'_>], index: usize, value: PointerValue<'ctx>, @@ -1856,16 +1867,12 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>( // a recursive field is stored as a `i64*`, to use it we must cast it to // a pointer to the block of memory representation - let tags = &[field_layouts]; - let struct_type = block_of_memory_slices(env.context, tags, env.ptr_bytes); - - let opaque_wrapper_type = env - .context - .struct_type(&[struct_type, tag_id_type.into()], false); + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); builder.build_bitcast( result, - opaque_wrapper_type.ptr_type(AddressSpace::Generic), + actual_type, "cast_rec_pointer_lookup_at_index_ptr_new", ) } else { From 1bea704272058ec9a14827deb0d6aad184c0288e Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:48:10 +0200 Subject: [PATCH 25/61] only expand recursive tag unions if it's a different type (think linked-list of trees) --- compiler/mono/src/ir.rs | 2 +- compiler/mono/src/layout.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9246ea3e16..423b2586a6 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -19,7 +19,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index f1462d73aa..caa6af2259 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1665,7 +1665,12 @@ pub fn union_sorted_tags_help<'a>( has_any_arguments = true; // make sure to not unroll recursive types! - if opt_rec_var.is_some() && is_recursive_tag_union(&layout) { + let self_recursion = opt_rec_var.is_some() + && subs.get_root_key_without_compacting(var) + == subs.get_root_key_without_compacting(opt_rec_var.unwrap()) + && is_recursive_tag_union(&layout); + + if self_recursion { arg_layouts.push(Layout::RecursivePointer); } else { arg_layouts.push(layout); From 7d91cb4cd73debdf71385d4b37dbb16e7845e786 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 00:56:16 +0200 Subject: [PATCH 26/61] clippy --- compiler/gen_llvm/src/llvm/build.rs | 3 +-- compiler/mono/src/ir.rs | 2 +- compiler/mono/src/layout.rs | 21 +++++++++++++-------- compiler/mono/src/reset_reuse.rs | 21 +++++++-------------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index e21b4e0121..39a675c11e 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -19,8 +19,7 @@ use crate::llvm::build_str::{ }; use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::convert::{ - basic_type_from_builtin, basic_type_from_layout, block_of_memory, block_of_memory_slices, - ptr_int, + basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, ptr_int, }; use crate::llvm::refcounting::{ build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 423b2586a6..d02186ad8f 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -233,7 +233,7 @@ impl<'a> Proc<'a> { ident_ids: &'i mut IdentIds, procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, ) { - for (key, proc) in procs.iter_mut() { + for (_, proc) in procs.iter_mut() { let new_proc = crate::reset_reuse::insert_reset_reuse(arena, home, ident_ids, proc.clone()); *proc = new_proc; diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index caa6af2259..3d43f1e61c 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -737,7 +737,12 @@ impl<'a> Layout<'a> { } } RecursivePointer => true, - Boxed(inner) => true, + + Boxed(_) => { + // technically we should look at layout of the box's content + // but refcount insertion needs this to return true + true + } Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), } } @@ -1536,15 +1541,15 @@ fn get_recursion_var(subs: &Subs, var: Variable) -> Option { } fn is_recursive_tag_union(layout: &Layout) -> bool { - match layout { + matches!( + layout, Layout::Union( UnionLayout::NullableUnwrapped { .. } - | UnionLayout::Recursive(_) - | UnionLayout::NullableWrapped { .. } - | UnionLayout::NonNullableUnwrapped { .. }, - ) => true, - _ => false, - } + | UnionLayout::Recursive(_) + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NonNullableUnwrapped { .. }, + ) + ) } pub fn union_sorted_tags_help<'a>( diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index a45d014e5a..2bd8be362c 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -212,7 +212,7 @@ fn try_function_s<'a, 'i>( if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { stmt } else { - insert_reset(env, w, x, Layout::Union(c.layout), new_stmt) + insert_reset(env, w, x, new_stmt) } } @@ -220,7 +220,6 @@ fn insert_reset<'a>( env: &mut Env<'a, '_>, w: Symbol, x: Symbol, - layout: Layout<'a>, mut stmt: &'a Stmt<'a>, ) -> &'a Stmt<'a> { use crate::ir::Expr::*; @@ -467,12 +466,6 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> } => { let mut new_branches = Vec::with_capacity_in(branches.len(), arena); - // TODO for non-recursive unions there is no benefit - let benefits_from_reuse = match cond_layout { - Layout::Union(union_layout) => Some(union_layout), - _ => None, - }; - for (tag, info, body) in branches.iter() { let temp = function_r(env, body); @@ -569,12 +562,12 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> arena.alloc(Let(*symbol, expr.clone(), *layout, b)) } Invoke { - symbol, - call, - layout, - pass, - fail, - exception_id, + symbol: _, + call: _, + layout: _, + pass: _, + fail: _, + exception_id: _, } => { // TODO implement this stmt From 0171fd09594302cdbeebf9be5a354575992d1c49 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 15:33:50 +0200 Subject: [PATCH 27/61] remove Boxed --- cli/src/repl/eval.rs | 1 - compiler/gen_llvm/src/llvm/build.rs | 50 +++++++++-------------- compiler/gen_llvm/src/llvm/build_hash.rs | 1 - compiler/gen_llvm/src/llvm/compare.rs | 2 - compiler/gen_llvm/src/llvm/convert.rs | 8 ---- compiler/gen_llvm/src/llvm/refcounting.rs | 4 -- compiler/mono/src/alias_analysis.rs | 1 - compiler/mono/src/inc_dec.rs | 18 ++++++-- compiler/mono/src/layout.rs | 12 ------ compiler/mono/src/reset_reuse.rs | 11 ++--- 10 files changed, 41 insertions(+), 67 deletions(-) diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index b59ddc38b5..7d150a4756 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -73,7 +73,6 @@ fn jit_to_ast_help<'a>( content: &Content, ) -> Result, ToAstProblem> { match layout { - Layout::Boxed(_) => todo!(), Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| { bool_to_ast(env, num, content) })), diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 39a675c11e..e9eafa5de3 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -2495,32 +2495,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( Dec(symbol) => { let (value, layout) = load_symbol_and_layout(scope, symbol); - if let Layout::Boxed(_) = layout { - if value.is_pointer_value() { - let value_ptr = value.into_pointer_value(); - - let then_block = env.context.append_basic_block(parent, "then"); - let done_block = env.context.append_basic_block(parent, "done"); - - let condition = - env.builder.build_is_not_null(value_ptr, "box_is_not_null"); - env.builder - .build_conditional_branch(condition, then_block, done_block); - - { - env.builder.position_at_end(then_block); - let refcount_ptr = - PointerToRefcount::from_ptr_to_data(env, value_ptr); - refcount_ptr.decrement(env, layout); - - env.builder.build_unconditional_branch(done_block); - } - - env.builder.position_at_end(done_block); - } else { - eprint!("we're likely leaking memory; see issue #985 for details"); - } - } else if layout.contains_refcounted() { + if layout.contains_refcounted() { decrement_refcount_layout(env, parent, layout_ids, value, layout); } @@ -2562,11 +2537,26 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( _ if layout.is_refcounted() => { if value.is_pointer_value() { - // BasicValueEnum::PointerValue(value_ptr) => { let value_ptr = value.into_pointer_value(); - let refcount_ptr = - PointerToRefcount::from_ptr_to_data(env, value_ptr); - refcount_ptr.decrement(env, layout); + + let then_block = env.context.append_basic_block(parent, "then"); + let done_block = env.context.append_basic_block(parent, "done"); + + let condition = + env.builder.build_is_not_null(value_ptr, "box_is_not_null"); + env.builder + .build_conditional_branch(condition, then_block, done_block); + + { + env.builder.position_at_end(then_block); + let refcount_ptr = + PointerToRefcount::from_ptr_to_data(env, value_ptr); + refcount_ptr.decrement(env, layout); + + env.builder.build_unconditional_branch(done_block); + } + + env.builder.position_at_end(done_block); } else { eprint!("we're likely leaking memory; see issue #985 for details"); } diff --git a/compiler/gen_llvm/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs index bdb6700759..61a74dd232 100644 --- a/compiler/gen_llvm/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -45,7 +45,6 @@ fn build_hash_layout<'a, 'ctx, 'env>( when_recursive: WhenRecursive<'a>, ) -> IntValue<'ctx> { match layout { - Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive) } diff --git a/compiler/gen_llvm/src/llvm/compare.rs b/compiler/gen_llvm/src/llvm/compare.rs index 8d1be9b870..2a9d1d3fa3 100644 --- a/compiler/gen_llvm/src/llvm/compare.rs +++ b/compiler/gen_llvm/src/llvm/compare.rs @@ -139,7 +139,6 @@ fn build_eq<'a, 'ctx, 'env>( } match lhs_layout { - Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { build_eq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive) } @@ -297,7 +296,6 @@ fn build_neq<'a, 'ctx, 'env>( } match lhs_layout { - Layout::Boxed(_) => todo!(), Layout::Builtin(builtin) => { build_neq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive) } diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index ebf384d431..1cb299c0ae 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -79,14 +79,6 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( .as_basic_type_enum() } - Boxed(_) => { - // TODO make this dynamic - env.context - .i64_type() - .ptr_type(AddressSpace::Generic) - .as_basic_type_enum() - } - Builtin(builtin) => basic_type_from_builtin(env, builtin), } } diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 45f0b94542..32cd8a22d6 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -752,10 +752,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( Some(function) } }, - Layout::Boxed(_) => { - // TODO - todo!() - } } } diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 1fbc958b2b..7d3c509d70 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -972,7 +972,6 @@ fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result } => worst_case_type(builder), }, RecursivePointer => worst_case_type(builder), - Boxed(_) => worst_case_type(builder), Closure(_, lambda_set, _) => layout_spec(builder, &lambda_set.runtime_representation()), } } diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 1ab137d5b9..9954707853 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -154,6 +154,7 @@ struct VarInfo { reference: bool, // true if the variable may be a reference (aka pointer) at runtime persistent: bool, // true if the variable is statically known to be marked a Persistent at runtime consume: bool, // true if the variable RC must be "consumed" + reset: bool, // true if the variable is the result of a Reset operation } type VarMap = MutMap; @@ -254,6 +255,7 @@ impl<'a> Context<'a> { reference: false, // assume function symbols are global constants persistent: true, // assume function symbols are global constants consume: false, // no need to consume this variable + reset: false, // reset symbols cannot be passed as function arguments }, ); } @@ -310,7 +312,12 @@ impl<'a> Context<'a> { return stmt; } - let modify = ModifyRc::Dec(symbol); + let modify = if info.reset { + ModifyRc::DecRef(symbol) + } else { + ModifyRc::Dec(symbol) + }; + self.arena.alloc(Stmt::Refcounting(modify, stmt)) } @@ -812,7 +819,7 @@ impl<'a> Context<'a> { // must this value be consumed? let consume = consume_call(&self.vars, call); - self.update_var_info_help(symbol, layout, persistent, consume) + self.update_var_info_help(symbol, layout, persistent, consume, false) } fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self { @@ -823,7 +830,9 @@ impl<'a> Context<'a> { // must this value be consumed? let consume = consume_expr(&self.vars, expr); - self.update_var_info_help(symbol, layout, persistent, consume) + let reset = matches!(expr, Expr::Reset(_)); + + self.update_var_info_help(symbol, layout, persistent, consume, reset) } fn update_var_info_help( @@ -832,6 +841,7 @@ impl<'a> Context<'a> { layout: &Layout<'a>, persistent: bool, consume: bool, + reset: bool, ) -> Self { // should we perform incs and decs on this value? let reference = layout.contains_refcounted(); @@ -840,6 +850,7 @@ impl<'a> Context<'a> { reference, persistent, consume, + reset, }; let mut ctx = self.clone(); @@ -857,6 +868,7 @@ impl<'a> Context<'a> { reference: p.layout.contains_refcounted(), consume: !p.borrow, persistent: false, + reset: false, }; ctx.vars.insert(p.symbol, info); } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 3d43f1e61c..4bd851184a 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -44,8 +44,6 @@ pub enum Layout<'a> { Union(UnionLayout<'a>), RecursivePointer, - Boxed(&'a Layout<'a>), - /// A function. The types of its arguments, then the type of its return value. Closure(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>), } @@ -599,7 +597,6 @@ impl<'a> Layout<'a> { // We cannot memcpy pointers, because then we would have the same pointer in multiple places! false } - Boxed(_) => false, } } @@ -651,7 +648,6 @@ impl<'a> Layout<'a> { } Closure(_, lambda_set, _) => lambda_set.stack_size(pointer_size), RecursivePointer => pointer_size, - Boxed(_) => pointer_size, } } @@ -682,7 +678,6 @@ impl<'a> Layout<'a> { } Layout::Builtin(builtin) => builtin.alignment_bytes(pointer_size), Layout::RecursivePointer => pointer_size, - Layout::Boxed(_) => pointer_size, Layout::Closure(_, captured, _) => { pointer_size.max(captured.alignment_bytes(pointer_size)) } @@ -704,7 +699,6 @@ impl<'a> Layout<'a> { } RecursivePointer => true, - Boxed(_) => true, Builtin(List(_)) | Builtin(Str) => true, @@ -738,11 +732,6 @@ impl<'a> Layout<'a> { } RecursivePointer => true, - Boxed(_) => { - // technically we should look at layout of the box's content - // but refcount insertion needs this to return true - true - } Closure(_, closure_layout, _) => closure_layout.contains_refcounted(), } } @@ -767,7 +756,6 @@ impl<'a> Layout<'a> { } Union(union_layout) => union_layout.to_doc(alloc, parens), RecursivePointer => alloc.text("*self"), - Boxed(_) => alloc.text("unbox"), Closure(args, closure_layout, result) => { let args_doc = args.iter().map(|x| x.to_doc(alloc, Parens::InFunction)); diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 2bd8be362c..f508e7353e 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -212,7 +212,7 @@ fn try_function_s<'a, 'i>( if std::ptr::eq(stmt, new_stmt) || stmt == new_stmt { stmt } else { - insert_reset(env, w, x, new_stmt) + insert_reset(env, w, x, c.layout, new_stmt) } } @@ -220,6 +220,7 @@ fn insert_reset<'a>( env: &mut Env<'a, '_>, w: Symbol, x: Symbol, + union_layout: UnionLayout<'a>, mut stmt: &'a Stmt<'a>, ) -> &'a Stmt<'a> { use crate::ir::Expr::*; @@ -246,11 +247,11 @@ fn insert_reset<'a>( let reset_expr = Expr::Reset(x); - const I64: Layout<'static> = Layout::Builtin(crate::layout::Builtin::Int64); + // const I64: Layout<'static> = Layout::Builtin(crate::layout::Builtin::Int64); - stmt = env - .arena - .alloc(Stmt::Let(w, reset_expr, Layout::Boxed(&I64), stmt)); + let layout = Layout::Union(union_layout); + + stmt = env.arena.alloc(Stmt::Let(w, reset_expr, layout, stmt)); for (symbol, expr, expr_layout) in stack.into_iter().rev() { stmt = env From 8a1a643e3bbe004cc2663bff30b93a7a8abc3326 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 16:52:56 +0200 Subject: [PATCH 28/61] refactor function_r branch body --- compiler/mono/src/reset_reuse.rs | 68 ++++++++++++++------------------ 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index f508e7353e..1abecc474e 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -452,6 +452,33 @@ fn function_d<'a, 'i>( function_d_finalize(env, x, c, temp) } +fn function_r_branch_body<'a, 'i>( + env: &mut Env<'a, 'i>, + info: &BranchInfo<'a>, + body: &'a Stmt<'a>, +) -> &'a Stmt<'a> { + let temp = function_r(env, body); + + match info { + BranchInfo::None => temp, + BranchInfo::Constructor { + scrutinee, + layout, + tag_id, + } => match layout { + Layout::Union(UnionLayout::NonRecursive(_)) => temp, + Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { + let ctor_info = CtorInfo { + layout: *union_layout, + id: *tag_id, + }; + function_d(env, *scrutinee, &ctor_info, temp) + } + _ => temp, + }, + } +} + fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> { use Stmt::*; @@ -468,52 +495,15 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> let mut new_branches = Vec::with_capacity_in(branches.len(), arena); for (tag, info, body) in branches.iter() { - let temp = function_r(env, body); - - let new_body = match info { - BranchInfo::None => temp, - BranchInfo::Constructor { - scrutinee, - layout, - tag_id, - } => match layout { - Layout::Union(UnionLayout::NonRecursive(_)) => temp, - Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { - let ctor_info = CtorInfo { - layout: *union_layout, - id: *tag_id, - }; - function_d(env, *scrutinee, &ctor_info, temp) - } - _ => temp, - }, - }; + let new_body = function_r_branch_body(env, info, body); new_branches.push((*tag, info.clone(), new_body.clone())); } let new_default = { let (info, body) = default_branch; - let temp = function_r(env, body); - let new_body = match info { - BranchInfo::None => temp, - BranchInfo::Constructor { - scrutinee, - layout, - tag_id, - } => match layout { - Layout::Union(UnionLayout::NonRecursive(_)) => temp, - Layout::Union(union_layout) if !union_layout.tag_is_null(*tag_id) => { - let ctor_info = CtorInfo { - layout: *union_layout, - id: *tag_id, - }; - function_d(env, *scrutinee, &ctor_info, temp) - } - _ => temp, - }, - }; + let new_body = function_r_branch_body(env, info, body); (info.clone(), new_body) }; From 1beed7f7597ffec047fc256a8f4d1da45c3352be Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 16:58:49 +0200 Subject: [PATCH 29/61] implement function_R invoke --- compiler/mono/src/reset_reuse.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/mono/src/reset_reuse.rs b/compiler/mono/src/reset_reuse.rs index 1abecc474e..3a437e9d50 100644 --- a/compiler/mono/src/reset_reuse.rs +++ b/compiler/mono/src/reset_reuse.rs @@ -553,15 +553,27 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> arena.alloc(Let(*symbol, expr.clone(), *layout, b)) } Invoke { - symbol: _, - call: _, - layout: _, - pass: _, - fail: _, - exception_id: _, + symbol, + call, + layout, + pass, + fail, + exception_id, } => { - // TODO implement this - stmt + let branch_info = BranchInfo::None; + let new_pass = function_r_branch_body(env, &branch_info, pass); + let new_fail = function_r_branch_body(env, &branch_info, fail); + + let invoke = Invoke { + symbol: *symbol, + call: call.clone(), + layout: *layout, + pass: new_pass, + fail: new_fail, + exception_id: *exception_id, + }; + + arena.alloc(invoke) } Refcounting(modify_rc, continuation) => { let b = function_r(env, continuation); From 771599165ba4fdb2f79c518a10eea1f8c904a766 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 17:16:45 +0200 Subject: [PATCH 30/61] cleanup build_reset usage --- compiler/gen_llvm/src/llvm/build.rs | 18 ++--- compiler/gen_llvm/src/llvm/refcounting.rs | 80 ++++++++++------------- compiler/mono/src/ir.rs | 2 +- 3 files changed, 40 insertions(+), 60 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index e9eafa5de3..702329af40 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1021,20 +1021,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( // referenced value, and returns the location of the now-invalid cell env.builder.position_at_end(then_block); - match build_reset(env, layout_ids, union_layout) { - Some(reset_function) => { - let call = - env.builder - .build_call(reset_function, &[tag_ptr.into()], "call_reuse"); + let reset_function = build_reset(env, layout_ids, union_layout); + let call = env + .builder + .build_call(reset_function, &[tag_ptr.into()], "call_reuse"); - call.set_call_convention(FAST_CALL_CONV); + call.set_call_convention(FAST_CALL_CONV); - let _ = call.try_as_basic_value(); - } - None => { - panic!("") - } - } + let _ = call.try_as_basic_value(); env.builder.build_unconditional_branch(cont_block); } diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 32cd8a22d6..c2457bd83a 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1564,61 +1564,47 @@ pub fn build_reset<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, union_layout: &UnionLayout<'a>, -) -> Option> { +) -> FunctionValue<'ctx> { use UnionLayout::*; match union_layout { NullableWrapped { other_tags: tags, .. - } => { - let function = build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - tags, - true, - ); + } => build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + tags, + true, + ), - Some(function) - } + NullableUnwrapped { other_fields, .. } => build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + env.arena.alloc([*other_fields]), + true, + ), - NullableUnwrapped { other_fields, .. } => { - let function = build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - env.arena.alloc([*other_fields]), - true, - ); + NonNullableUnwrapped(fields) => build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + &*env.arena.alloc([*fields]), + true, + ), - Some(function) - } - - NonNullableUnwrapped(fields) => { - let function = build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - &*env.arena.alloc([*fields]), - true, - ); - Some(function) - } - - Recursive(tags) => { - let function = build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - tags, - false, - ); - Some(function) - } + Recursive(tags) => build_reuse_rec_union( + env, + layout_ids, + &WhenRecursive::Loop(*union_layout), + *union_layout, + tags, + false, + ), NonRecursive(_) => { unreachable!("non-recursive tags cannot be reused") diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index d02186ad8f..6a5b9e082c 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -19,7 +19,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { From 2c604f96e01369ba588e5d71f5487e3ff789b2c6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 17:21:48 +0200 Subject: [PATCH 31/61] clarify block names --- compiler/gen_llvm/src/llvm/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 702329af40..f61e341d5a 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1006,8 +1006,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( }; let ctx = env.context; - let then_block = ctx.append_basic_block(parent, "then"); - let else_block = ctx.append_basic_block(parent, "else"); + let then_block = ctx.append_basic_block(parent, "then_reset"); + let else_block = ctx.append_basic_block(parent, "else_decref"); let cont_block = ctx.append_basic_block(parent, "cont"); let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); @@ -1024,7 +1024,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( let reset_function = build_reset(env, layout_ids, union_layout); let call = env .builder - .build_call(reset_function, &[tag_ptr.into()], "call_reuse"); + .build_call(reset_function, &[tag_ptr.into()], "call_reset"); call.set_call_convention(FAST_CALL_CONV); From 568e4e390a204a7503e7238afbb2b3a9e14edb47 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 17:27:11 +0200 Subject: [PATCH 32/61] fix --- compiler/mono/src/ir.rs | 2 +- compiler/test_gen/src/gen_tags.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 6a5b9e082c..d02186ad8f 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -19,7 +19,7 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; -pub const PRETTY_PRINT_IR_SYMBOLS: bool = true; +pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; macro_rules! return_on_layout_error { ($env:expr, $layout_result:expr) => { diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index 40e5403914..048a10a62b 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -97,7 +97,7 @@ fn applied_tag_just_enum() { "# ), (2, 0), - (u8, i64) + (u8, u8) ); } From 15eba1a6870731cace0ba881688b279458269e86 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 17:55:53 +0200 Subject: [PATCH 33/61] give it another go --- compiler/test_gen/src/gen_tags.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index 048a10a62b..1cad68a333 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -97,7 +97,8 @@ fn applied_tag_just_enum() { "# ), (2, 0), - (u8, u8) + (u8, [u8; 7], u8), + |(a, _, c)| (a, c) ); } From 785b4539af8afb7a04596ee3c01c7694c2e2daa8 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 22:21:03 +0200 Subject: [PATCH 34/61] less code --- compiler/gen_llvm/src/llvm/build.rs | 4 +- compiler/gen_llvm/src/llvm/refcounting.rs | 163 +++++----------------- 2 files changed, 34 insertions(+), 133 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index f61e341d5a..3b383f8af5 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1021,7 +1021,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( // referenced value, and returns the location of the now-invalid cell env.builder.position_at_end(then_block); - let reset_function = build_reset(env, layout_ids, union_layout); + let reset_function = build_reset(env, layout_ids, *union_layout); let call = env .builder .build_call(reset_function, &[tag_ptr.into()], "call_reset"); @@ -1034,7 +1034,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } { // If reset is used on a shared, non-reusable reference, it behaves - // like dec and returns , which instructs reuse to behave like ctor + // like dec and returns NULL, which instructs reuse to behave like ctor env.builder.position_at_end(else_block); refcount_ptr.decrement(env, layout); env.builder.build_unconditional_branch(cont_block); diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index c2457bd83a..870e2218fa 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -644,70 +644,21 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( Union(variant) => { use UnionLayout::*; - match variant { - NullableWrapped { - other_tags: tags, .. - } => { - let function = build_rec_union( - env, - layout_ids, - mode, - &WhenRecursive::Loop(*variant), - *variant, - tags, - true, - ); + if let NonRecursive(tags) = variant { + let function = modify_refcount_union(env, layout_ids, mode, when_recursive, tags); - Some(function) - } - - NullableUnwrapped { other_fields, .. } => { - let function = build_rec_union( - env, - layout_ids, - mode, - &WhenRecursive::Loop(*variant), - *variant, - env.arena.alloc([*other_fields]), - true, - ); - - Some(function) - } - - NonNullableUnwrapped(fields) => { - let function = build_rec_union( - env, - layout_ids, - mode, - &WhenRecursive::Loop(*variant), - *variant, - &*env.arena.alloc([*fields]), - true, - ); - Some(function) - } - - Recursive(tags) => { - let function = build_rec_union( - env, - layout_ids, - mode, - &WhenRecursive::Loop(*variant), - *variant, - tags, - false, - ); - Some(function) - } - - NonRecursive(tags) => { - let function = - modify_refcount_union(env, layout_ids, mode, when_recursive, tags); - - Some(function) - } + return Some(function); } + + let function = build_rec_union( + env, + layout_ids, + mode, + &WhenRecursive::Loop(*variant), + *variant, + ); + + Some(function) } Closure(_, lambda_set, _) => { @@ -1208,10 +1159,8 @@ fn build_rec_union<'a, 'ctx, 'env>( mode: Mode, when_recursive: &WhenRecursive<'a>, union_layout: UnionLayout<'a>, - tags: &'a [&'a [Layout<'a>]], - is_nullable: bool, ) -> FunctionValue<'ctx> { - let layout = Layout::Union(UnionLayout::Recursive(tags)); + let layout = Layout::Union(union_layout); let (_, fn_name) = function_name_from_mode( layout_ids, @@ -1228,7 +1177,7 @@ fn build_rec_union<'a, 'ctx, 'env>( let block = env.builder.get_insert_block().expect("to be in a function"); let di_location = env.builder.get_current_debug_location().unwrap(); - let basic_type = basic_type_from_layout(env, &Layout::Union(union_layout)); + let basic_type = basic_type_from_layout(env, &layout); let function_value = build_header(env, basic_type, mode, &fn_name); build_rec_union_help( @@ -1237,9 +1186,7 @@ fn build_rec_union<'a, 'ctx, 'env>( mode, when_recursive, union_layout, - tags, function_value, - is_nullable, ); env.builder.position_at_end(block); @@ -1260,10 +1207,10 @@ fn build_rec_union_help<'a, 'ctx, 'env>( mode: Mode, when_recursive: &WhenRecursive<'a>, union_layout: UnionLayout<'a>, - tags: &'a [&'a [roc_mono::layout::Layout<'a>]], fn_val: FunctionValue<'ctx>, - is_nullable: bool, ) { + let tags = union_layout_tags(env.arena, &union_layout); + let is_nullable = union_layout.is_nullable(); debug_assert!(!tags.is_empty()); let context = &env.context; @@ -1560,65 +1507,27 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( } } -pub fn build_reset<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, +fn union_layout_tags<'a>( + arena: &'a bumpalo::Bump, union_layout: &UnionLayout<'a>, -) -> FunctionValue<'ctx> { +) -> &'a [&'a [Layout<'a>]] { use UnionLayout::*; match union_layout { NullableWrapped { other_tags: tags, .. - } => build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - tags, - true, - ), - - NullableUnwrapped { other_fields, .. } => build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - env.arena.alloc([*other_fields]), - true, - ), - - NonNullableUnwrapped(fields) => build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - &*env.arena.alloc([*fields]), - true, - ), - - Recursive(tags) => build_reuse_rec_union( - env, - layout_ids, - &WhenRecursive::Loop(*union_layout), - *union_layout, - tags, - false, - ), - - NonRecursive(_) => { - unreachable!("non-recursive tags cannot be reused") - } + } => *tags, + NullableUnwrapped { other_fields, .. } => arena.alloc([*other_fields]), + NonNullableUnwrapped(fields) => arena.alloc([*fields]), + Recursive(tags) => tags, + NonRecursive(tags) => tags, } } -fn build_reuse_rec_union<'a, 'ctx, 'env>( +pub fn build_reset<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - when_recursive: &WhenRecursive<'a>, union_layout: UnionLayout<'a>, - tags: &'a [&'a [Layout<'a>]], - is_nullable: bool, ) -> FunctionValue<'ctx> { let mode = Mode::Dec; @@ -1626,15 +1535,8 @@ fn build_reuse_rec_union<'a, 'ctx, 'env>( let fn_name = layout_id.to_symbol_string(Symbol::DEC, &env.interns); let fn_name = format!("{}_reset", fn_name); - let dec_function = build_rec_union( - env, - layout_ids, - Mode::Dec, - when_recursive, - union_layout, - tags, - is_nullable, - ); + let when_recursive = WhenRecursive::Loop(union_layout); + let dec_function = build_rec_union(env, layout_ids, Mode::Dec, &when_recursive, union_layout); let function = match env.module.get_function(fn_name.as_str()) { Some(function_value) => function_value, @@ -1648,12 +1550,10 @@ fn build_reuse_rec_union<'a, 'ctx, 'env>( build_reuse_rec_union_help( env, layout_ids, - when_recursive, + &when_recursive, union_layout, - tags, function_value, dec_function, - is_nullable, ); env.builder.position_at_end(block); @@ -1673,11 +1573,12 @@ fn build_reuse_rec_union_help<'a, 'ctx, 'env>( layout_ids: &mut LayoutIds<'a>, when_recursive: &WhenRecursive<'a>, union_layout: UnionLayout<'a>, - tags: &'a [&'a [roc_mono::layout::Layout<'a>]], reset_function: FunctionValue<'ctx>, dec_function: FunctionValue<'ctx>, - is_nullable: bool, ) { + let tags = union_layout_tags(env.arena, &union_layout); + let is_nullable = union_layout.is_nullable(); + debug_assert!(!tags.is_empty()); let context = &env.context; From d464c005f05c6df4bb889b56a292fce54878c79d Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 14 Jul 2021 23:26:19 +0200 Subject: [PATCH 35/61] encode tag unions as morphic unions --- compiler/mono/src/alias_analysis.rs | 96 +++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 2b4354d26e..f03c8f979b 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -4,7 +4,7 @@ use morphic_lib::{ FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, UpdateModeVar, ValueId, }; -use roc_collections::all::MutMap; +use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; use std::convert::TryFrom; @@ -26,6 +26,20 @@ pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] { const DEBUG: bool = false; const SIZE: usize = if DEBUG { 50 } else { 16 }; +#[derive(Debug, Clone, Copy, Hash)] +struct TagUnionId(u64); + +fn recursive_tag_union_name_bytes(union_layout: &UnionLayout) -> TagUnionId { + use std::collections::hash_map::DefaultHasher; + use std::hash::Hash; + use std::hash::Hasher; + + let mut hasher = DefaultHasher::new(); + union_layout.hash(&mut hasher); + + TagUnionId(hasher.finish()) +} + pub fn func_name_bytes_help<'a, I>( symbol: Symbol, argument_layouts: I, @@ -225,6 +239,7 @@ fn proc_spec(proc: &Proc) -> Result { struct Env { symbols: MutMap, join_points: MutMap, + tag_unions: MutSet, } fn stmt_spec( @@ -792,24 +807,52 @@ fn build_variant_types( ) -> Result> { use UnionLayout::*; - let mut result = Vec::new(); + let mut result; match union_layout { - NonRecursive(tags) => { + NonRecursive(tags) | Recursive(tags) => { + result = Vec::with_capacity(tags.len()); + for tag in tags.iter() { result.push(build_tuple_type(builder, tag)?); } } - Recursive(_) => unreachable!(), - NonNullableUnwrapped(_) => unreachable!(), + NonNullableUnwrapped(fields) => { + result = vec![build_tuple_type(builder, fields)?]; + } NullableWrapped { - nullable_id: _, - other_tags: _, - } => unreachable!(), + nullable_id, + other_tags: tags, + } => { + result = Vec::with_capacity(tags.len() + 1); + + let cutoff = *nullable_id as usize; + + for tag in tags[..cutoff].iter() { + result.push(build_tuple_type(builder, tag)?); + } + + let unit = builder.add_tuple_type(&[])?; + result.push(unit); + + for tag in tags[cutoff..].iter() { + result.push(build_tuple_type(builder, tag)?); + } + } NullableUnwrapped { - nullable_id: _, - other_fields: _, - } => unreachable!(), + nullable_id, + other_fields: fields, + } => { + let unit = builder.add_tuple_type(&[])?; + let other_type = build_tuple_type(builder, fields)?; + + if *nullable_id { + // nullable_id == 1 + result = vec![other_type, unit]; + } else { + result = vec![unit, other_type]; + } + } } Ok(result) @@ -838,21 +881,24 @@ fn expr_spec( tag_id, union_size: _, arguments, - } => match tag_layout { - UnionLayout::NonRecursive(_) => { - let value_id = build_tuple_value(builder, env, block, arguments)?; - let variant_types = build_variant_types(builder, tag_layout)?; - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) + } => { + let variant_types = build_variant_types(builder, tag_layout)?; + + match tag_layout { + UnionLayout::NonRecursive(_) => { + let value_id = build_tuple_value(builder, env, block, arguments)?; + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) + } + UnionLayout::Recursive(_) + | UnionLayout::NonNullableUnwrapped(_) + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NullableUnwrapped { .. } => { + let result_type = worst_case_type(builder)?; + let value_id = build_tuple_value(builder, env, block, arguments)?; + builder.add_unknown_with(block, &[value_id], result_type) + } } - UnionLayout::Recursive(_) - | UnionLayout::NonNullableUnwrapped(_) - | UnionLayout::NullableWrapped { .. } - | UnionLayout::NullableUnwrapped { .. } => { - let result_type = worst_case_type(builder)?; - let value_id = build_tuple_value(builder, env, block, arguments)?; - builder.add_unknown_with(block, &[value_id], result_type) - } - }, + } Struct(fields) => build_tuple_value(builder, env, block, fields), UnionAtIndex { index, From 8e54023d608d248c45fafeb0e28b1a90b07b9d54 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 15 Jul 2021 15:16:09 +0200 Subject: [PATCH 36/61] foundation for encoding recursive types --- compiler/mono/src/alias_analysis.rs | 78 ++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index f03c8f979b..0de38cc176 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -2,7 +2,7 @@ use morphic_lib::TypeContext; use morphic_lib::{ BlockExpr, BlockId, CalleeSpecVar, ConstDefBuilder, ConstName, EntryPointName, ExprContext, FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, - UpdateModeVar, ValueId, + TypeName, UpdateModeVar, ValueId, }; use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; @@ -40,6 +40,12 @@ fn recursive_tag_union_name_bytes(union_layout: &UnionLayout) -> TagUnionId { TagUnionId(hasher.finish()) } +impl TagUnionId { + const fn as_bytes(&self) -> [u8; 8] { + self.0.to_ne_bytes() + } +} + pub fn func_name_bytes_help<'a, I>( symbol: Symbol, argument_layouts: I, @@ -435,6 +441,26 @@ fn build_tuple_value( builder.add_make_tuple(block, &value_ids) } +#[derive(Clone, Debug, PartialEq)] +enum WhenRecursive<'a> { + Unreachable, + Loop(UnionLayout<'a>), +} + +fn build_recursive_tuple_type( + builder: &mut FuncDefBuilder, + layouts: &[Layout], + when_recursive: &WhenRecursive, +) -> Result { + 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 build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result { let mut field_types = Vec::new(); @@ -889,8 +915,19 @@ fn expr_spec( let value_id = build_tuple_value(builder, env, block, arguments)?; builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) } - UnionLayout::Recursive(_) - | UnionLayout::NonNullableUnwrapped(_) + UnionLayout::Recursive(_) => { + let value_id = build_tuple_value(builder, env, block, arguments)?; + let union_id = + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)?; + + let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + let named_id = builder.add_make_named(block, MOD_APP, type_name, union_id)?; + + Ok(named_id) + } + UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableWrapped { .. } | UnionLayout::NullableUnwrapped { .. } => { let result_type = worst_case_type(builder)?; @@ -917,7 +954,8 @@ fn expr_spec( _ => { // for the moment recursive tag unions don't quite work let value_id = env.symbols[structure]; - let result_type = layout_spec(builder, layout)?; + let result_type = + layout_spec_help(builder, layout, &WhenRecursive::Loop(*union_layout))?; builder.add_unknown_with(block, &[value_id], result_type) } }, @@ -986,11 +1024,19 @@ fn literal_spec( } fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result { + layout_spec_help(builder, layout, &WhenRecursive::Unreachable) +} + +fn layout_spec_help( + builder: &mut FuncDefBuilder, + layout: &Layout, + when_recursive: &WhenRecursive, +) -> Result { use Layout::*; match layout { - Builtin(builtin) => builtin_spec(builder, builtin), - Struct(fields) => build_tuple_type(builder, fields), + Builtin(builtin) => builtin_spec(builder, builtin, when_recursive), + Struct(fields) => build_recursive_tuple_type(builder, fields, when_recursive), Union(union_layout) => match union_layout { UnionLayout::NonRecursive(_) => { let variant_types = build_variant_types(builder, union_layout)?; @@ -1008,11 +1054,19 @@ fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result } => worst_case_type(builder), }, RecursivePointer => worst_case_type(builder), - Closure(_, lambda_set, _) => layout_spec(builder, &lambda_set.runtime_representation()), + Closure(_, lambda_set, _) => layout_spec_help( + builder, + &lambda_set.runtime_representation(), + when_recursive, + ), } } -fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result { +fn builtin_spec( + builder: &mut FuncDefBuilder, + builtin: &Builtin, + when_recursive: &WhenRecursive, +) -> Result { use Builtin::*; match builtin { @@ -1020,8 +1074,8 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result builder.add_tuple_type(&[]), Str | EmptyStr => str_type(builder), Dict(key_layout, value_layout) => { - let value_type = layout_spec(builder, value_layout)?; - let key_type = layout_spec(builder, key_layout)?; + let value_type = layout_spec_help(builder, value_layout, when_recursive)?; + let key_type = layout_spec_help(builder, key_layout, when_recursive)?; let element_type = builder.add_tuple_type(&[key_type, value_type])?; let cell = builder.add_heap_cell_type(); @@ -1030,7 +1084,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result { let value_type = builder.add_tuple_type(&[])?; - let key_type = layout_spec(builder, key_layout)?; + let key_type = layout_spec_help(builder, key_layout, when_recursive)?; let element_type = builder.add_tuple_type(&[key_type, value_type])?; let cell = builder.add_heap_cell_type(); @@ -1038,7 +1092,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result { - let element_type = layout_spec(builder, element_layout)?; + let element_type = layout_spec_help(builder, element_layout, when_recursive)?; let cell = builder.add_heap_cell_type(); let bag = builder.add_bag_type(element_type)?; From 0eb0d2457d80caa59a17de376a04c1ff85e4ad6f Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 15 Jul 2021 23:07:10 +0200 Subject: [PATCH 37/61] add helpers --- compiler/gen_llvm/src/llvm/build.rs | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 3b383f8af5..3588f2fa99 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1664,6 +1664,62 @@ pub fn build_tag<'a, 'ctx, 'env>( } } +fn tag_pointer_set_tag_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + tag_id: u8, + pointer: PointerValue<'ctx>, +) -> PointerValue<'ctx> { + // we only have 3 bits, so can encode only 0..7 + debug_assert!(tag_id < 8); + + let ptr_int = env.ptr_int(); + + let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); + + let tag_id_intval = ptr_int.const_int(tag_id as u64, false); + let combined = env.builder.build_or(as_int, tag_id_intval, "store_tag_id"); + + env.builder + .build_int_to_ptr(combined, pointer.get_type(), "to_ptr") +} + +fn tag_pointer_read_tag_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + pointer: PointerValue<'ctx>, +) -> IntValue<'ctx> { + let mask: u64 = 0b0000_0111; + + let ptr_int = env.ptr_int(); + + let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); + let mask_intval = env.ptr_int().const_int(mask, false); + + let masked = env.builder.build_and(as_int, mask_intval, "mask"); + + env.builder + .build_int_cast(masked, env.context.i8_type(), "to_u8") +} + +fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + pointer: PointerValue<'ctx>, +) -> PointerValue<'ctx> { + let ptr_int = env.ptr_int(); + + let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); + + let mask = { + let a = env.ptr_int().const_all_ones(); + let tag_id_bits = env.ptr_int().const_int(3, false); + env.builder.build_left_shift(a, tag_id_bits, "make_mask") + }; + + let masked = env.builder.build_and(as_int, mask, "masked"); + + env.builder + .build_int_to_ptr(masked, pointer.get_type(), "to_ptr") +} + fn allocate_tag<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, From 23867296a45bf10d0533fb67a08be3fb0bb840cd Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 15 Jul 2021 23:44:00 +0200 Subject: [PATCH 38/61] encode tag id in pointer and use it to read tag id --- compiler/gen_llvm/src/llvm/build.rs | 16 +++++++++++----- compiler/gen_llvm/src/llvm/refcounting.rs | 13 +++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 3588f2fa99..07cc897d91 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1154,13 +1154,15 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type(); + let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value()); + lookup_at_index_ptr2( env, tag_id_type, union_layout, field_layouts, *index as usize, - argument.into_pointer_value(), + ptr, ) } UnionLayout::NonNullableUnwrapped(field_layouts) => { @@ -1413,7 +1415,7 @@ pub fn build_tag<'a, 'ctx, 'env>( builder.build_store(field_ptr, field_val); } - raw_data_ptr.into() + tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() } UnionLayout::NonNullableUnwrapped(fields) => { debug_assert_eq!(union_size, 1); @@ -1683,7 +1685,7 @@ fn tag_pointer_set_tag_id<'a, 'ctx, 'env>( .build_int_to_ptr(combined, pointer.get_type(), "to_ptr") } -fn tag_pointer_read_tag_id<'a, 'ctx, 'env>( +pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, pointer: PointerValue<'ctx>, ) -> IntValue<'ctx> { @@ -1700,7 +1702,7 @@ fn tag_pointer_read_tag_id<'a, 'ctx, 'env>( .build_int_cast(masked, env.context.i8_type(), "to_u8") } -fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>( +pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, pointer: PointerValue<'ctx>, ) -> PointerValue<'ctx> { @@ -1785,7 +1787,11 @@ pub fn get_tag_id<'a, 'ctx, 'env>( get_tag_id_non_recursive(env, tag) } - UnionLayout::Recursive(_) => get_tag_id_wrapped(env, argument.into_pointer_value()), + UnionLayout::Recursive(_) => { + // TODO check that all tag ids fit into 2/3 bits + // get_tag_id_wrapped( env, tag_pointer_clear_tag_id(env, argument.into_pointer_value()),) + tag_pointer_read_tag_id(env, argument.into_pointer_value()) + } UnionLayout::NonNullableUnwrapped(_) => tag_id_int_type.const_zero(), UnionLayout::NullableWrapped { nullable_id, .. } => { let argument_ptr = argument.into_pointer_value(); diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 870e2218fa..9b60cf9ad1 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -1,7 +1,7 @@ use crate::debug_info_init; use crate::llvm::build::{ add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive, - Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX, + tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX, }; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::convert::{ @@ -1233,7 +1233,8 @@ fn build_rec_union_help<'a, 'ctx, 'env>( let parent = fn_val; debug_assert!(arg_val.is_pointer_value()); - let value_ptr = arg_val.into_pointer_value(); + let current_tag_id = get_tag_id(env, fn_val, &union_layout, arg_val); + let value_ptr = tag_pointer_clear_tag_id(env, arg_val.into_pointer_value()); // to increment/decrement the cons-cell itself let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr); @@ -1298,6 +1299,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>( union_layout, tags, value_ptr, + current_tag_id, refcount_ptr, do_recurse_block, DecOrReuse::Dec, @@ -1322,6 +1324,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( union_layout: UnionLayout<'a>, tags: &[&[Layout<'a>]], value_ptr: PointerValue<'ctx>, + current_tag_id: IntValue<'ctx>, refcount_ptr: PointerToRefcount<'ctx>, match_block: BasicBlock<'ctx>, decrement_or_reuse: DecOrReuse, @@ -1485,7 +1488,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( env.builder.build_unconditional_branch(only_branch); } else { // read the tag_id - let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into()); + // let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into()); let default_block = env.context.append_basic_block(parent, "switch_default"); @@ -1601,7 +1604,8 @@ fn build_reuse_rec_union_help<'a, 'ctx, 'env>( let parent = reset_function; debug_assert!(arg_val.is_pointer_value()); - let value_ptr = arg_val.into_pointer_value(); + let current_tag_id = get_tag_id(env, reset_function, &union_layout, arg_val); + let value_ptr = tag_pointer_clear_tag_id(env, arg_val.into_pointer_value()); // to increment/decrement the cons-cell itself let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, value_ptr); @@ -1654,6 +1658,7 @@ fn build_reuse_rec_union_help<'a, 'ctx, 'env>( union_layout, tags, value_ptr, + current_tag_id, refcount_ptr, do_recurse_block, DecOrReuse::Reuse, From 2bc3ddae464c2e2a2b4c09f600fb44c4e2110051 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 17:51:41 +0200 Subject: [PATCH 39/61] fix bug in casting recursive pointers --- compiler/gen_llvm/src/llvm/build.rs | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 6e87423b3f..201acf20d6 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -19,8 +19,7 @@ use crate::llvm::build_str::{ }; use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::convert::{ - basic_type_from_builtin, basic_type_from_layout, block_of_memory, block_of_memory_slices, - ptr_int, + basic_type_from_builtin, basic_type_from_layout, block_of_memory_slices, ptr_int, }; use crate::llvm::refcounting::{ decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, @@ -1087,6 +1086,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr2( env, + union_layout, tag_id_type, field_layouts, *index as usize, @@ -1100,11 +1100,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr( env, + union_layout, field_layouts, *index as usize, argument.into_pointer_value(), struct_type.into_struct_type(), - &struct_layout, ) } UnionLayout::NullableWrapped { @@ -1127,6 +1127,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr2( env, + union_layout, tag_id_type, field_layouts, *index as usize, @@ -1147,12 +1148,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( lookup_at_index_ptr( env, + union_layout, field_layouts, // the tag id is not stored *index as usize, argument.into_pointer_value(), struct_type.into_struct_type(), - &struct_layout, ) } } @@ -1661,11 +1662,11 @@ pub fn get_tag_id<'a, 'ctx, 'env>( fn lookup_at_index_ptr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + union_layout: &UnionLayout<'a>, field_layouts: &[Layout<'_>], index: usize, value: PointerValue<'ctx>, struct_type: StructType<'ctx>, - structure_layout: &Layout<'_>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; @@ -1687,11 +1688,13 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( if let Some(Layout::RecursivePointer) = field_layouts.get(index as usize) { // a recursive field is stored as a `i64*`, to use it we must cast it to // a pointer to the block of memory representation + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); + builder.build_bitcast( result, - block_of_memory(env.context, structure_layout, env.ptr_bytes) - .ptr_type(AddressSpace::Generic), - "cast_rec_pointer_lookup_at_index_ptr", + actual_type, + "cast_rec_pointer_lookup_at_index_ptr_old", ) } else { result @@ -1700,6 +1703,7 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>( fn lookup_at_index_ptr2<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + union_layout: &UnionLayout<'a>, tag_id_type: IntType<'ctx>, field_layouts: &[Layout<'_>], index: usize, @@ -1737,17 +1741,13 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>( // a recursive field is stored as a `i64*`, to use it we must cast it to // a pointer to the block of memory representation - let tags = &[field_layouts]; - let struct_type = block_of_memory_slices(env.context, tags, env.ptr_bytes); - - let opaque_wrapper_type = env - .context - .struct_type(&[struct_type, tag_id_type.into()], false); + let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout)); + debug_assert!(actual_type.is_pointer_type()); builder.build_bitcast( result, - opaque_wrapper_type.ptr_type(AddressSpace::Generic), - "cast_rec_pointer_lookup_at_index_ptr", + actual_type, + "cast_rec_pointer_lookup_at_index_ptr_new", ) } else { result From 71672bb7ad8b6221e55c94d04af413223f74ce94 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 17:54:44 +0200 Subject: [PATCH 40/61] prevent unrolling of recursive types --- compiler/mono/src/ir.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index a693052ab9..4f41a292ba 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4201,8 +4201,18 @@ fn convert_tag_union<'a>( layouts.push(arg_layouts); } + // we must derive the union layout from the whole_var, building it up + // from `layouts` would unroll recursive tag unions, and that leads to + // problems down the line because we hash layouts and an unrolled + // version is not the same as the minimal version. debug_assert!(layouts.len() > 1); - let union_layout = UnionLayout::Recursive(layouts.into_bump_slice()); + let union_layout = match return_on_layout_error!( + env, + layout_cache.from_var(env.arena, variant_var, env.subs) + ) { + Layout::Union(ul) => ul, + _ => unreachable!(), + }; let tag = Expr::Tag { tag_layout: union_layout, @@ -7095,6 +7105,7 @@ fn from_can_pattern_help<'a>( temp }; + debug_assert!(layouts.len() > 1); let layout = UnionLayout::NonRecursive(layouts.into_bump_slice()); Pattern::AppliedTag { @@ -7152,8 +7163,16 @@ fn from_can_pattern_help<'a>( temp }; + // we must derive the union layout from the whole_var, building it up + // from `layouts` would unroll recursive tag unions, and that leads to + // problems down the line because we hash layouts and an unrolled + // version is not the same as the minimal version. debug_assert!(layouts.len() > 1); - let layout = UnionLayout::Recursive(layouts.into_bump_slice()); + let layout = + match layout_cache.from_var(env.arena, *whole_var, env.subs) { + Ok(Layout::Union(ul)) => ul, + _ => unreachable!(), + }; Pattern::AppliedTag { tag_name: tag_name.clone(), From 7dd440f6422c7b75e24bb34831bfa4d94917acfd Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 17:55:03 +0200 Subject: [PATCH 41/61] morphic Recursive types --- compiler/mono/src/alias_analysis.rs | 152 +++++++++++++++++++++------- 1 file changed, 114 insertions(+), 38 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 0de38cc176..831bdd8afd 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -1,8 +1,8 @@ use morphic_lib::TypeContext; use morphic_lib::{ BlockExpr, BlockId, CalleeSpecVar, ConstDefBuilder, ConstName, EntryPointName, ExprContext, - FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId, - TypeName, UpdateModeVar, ValueId, + FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, + TypeDefBuilder, TypeId, TypeName, UpdateModeVar, ValueId, }; use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; @@ -154,6 +154,8 @@ where let entry_point_name = FuncName(ENTRY_POINT_NAME); m.add_func(entry_point_name, entry_point_function)?; + let mut type_definitions = MutSet::default(); + // all other functions for proc in procs { let bytes = func_name_bytes(proc); @@ -168,11 +170,27 @@ where ); } - let spec = proc_spec(proc)?; + let (spec, type_names) = proc_spec(proc)?; + + type_definitions.extend(type_names); m.add_func(func_name, spec)?; } + for union_layout in type_definitions { + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + let mut builder = TypeDefBuilder::new(); + + let variant_types = build_variant_types(&mut builder, &union_layout)?; + let root_type = builder.add_union_type(&variant_types)?; + + let type_def = builder.build(root_type)?; + + m.add_named_type(type_name, type_def)?; + } + m.build()? }; @@ -215,7 +233,7 @@ fn build_entry_point(layout: crate::ir::ProcLayout, func_name: FuncName) -> Resu Ok(spec) } -fn proc_spec(proc: &Proc) -> Result { +fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet>)> { let mut builder = FuncDefBuilder::new(); let mut env = Env::default(); @@ -238,22 +256,22 @@ fn proc_spec(proc: &Proc) -> Result { let spec = builder.build(arg_type_id, ret_type_id, root)?; - Ok(spec) + Ok((spec, env.type_names)) } #[derive(Default)] -struct Env { +struct Env<'a> { symbols: MutMap, join_points: MutMap, - tag_unions: MutSet, + type_names: MutSet>, } -fn stmt_spec( +fn stmt_spec<'a>( builder: &mut FuncDefBuilder, - env: &mut Env, + env: &mut Env<'a>, block: BlockId, layout: &Layout, - stmt: &Stmt, + stmt: &Stmt<'a>, ) -> Result { use Stmt::*; @@ -448,20 +466,20 @@ enum WhenRecursive<'a> { } fn build_recursive_tuple_type( - builder: &mut FuncDefBuilder, + builder: &mut impl TypeContext, layouts: &[Layout], when_recursive: &WhenRecursive, ) -> Result { let mut field_types = Vec::new(); for field in layouts.iter() { - field_types.push(layout_spec(builder, field)?); + field_types.push(layout_spec_help(builder, field, when_recursive)?); } builder.add_tuple_type(&field_types) } -fn build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result { +fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Result { let mut field_types = Vec::new(); for field in layouts.iter() { @@ -828,7 +846,7 @@ fn lowlevel_spec( } fn build_variant_types( - builder: &mut FuncDefBuilder, + builder: &mut impl TypeContext, union_layout: &UnionLayout, ) -> Result> { use UnionLayout::*; @@ -836,13 +854,25 @@ fn build_variant_types( let mut result; match union_layout { - NonRecursive(tags) | Recursive(tags) => { + NonRecursive(tags) => { result = Vec::with_capacity(tags.len()); for tag in tags.iter() { result.push(build_tuple_type(builder, tag)?); } } + Recursive(tags) => { + result = Vec::with_capacity(tags.len()); + + let when_recursive = WhenRecursive::Loop(*union_layout); + + for tag in tags.iter() { + let data_id = build_recursive_tuple_type(builder, tag, &when_recursive)?; + let cell_id = builder.add_heap_cell_type(); + let value_id = builder.add_tuple_type(&[cell_id, data_id])?; + result.push(value_id); + } + } NonNullableUnwrapped(fields) => { result = vec![build_tuple_type(builder, fields)?]; } @@ -889,12 +919,12 @@ fn worst_case_type(context: &mut impl TypeContext) -> Result { context.add_bag_type(cell) } -fn expr_spec( +fn expr_spec<'a>( builder: &mut FuncDefBuilder, - env: &mut Env, + env: &mut Env<'a>, block: BlockId, - layout: &Layout, - expr: &Expr, + layout: &Layout<'a>, + expr: &Expr<'a>, ) -> Result { use Expr::*; @@ -916,13 +946,19 @@ fn expr_spec( builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) } UnionLayout::Recursive(_) => { - let value_id = build_tuple_value(builder, env, block, arguments)?; + let data_id = build_tuple_value(builder, env, block, arguments)?; + let cell_id = builder.add_new_heap_cell(block)?; + + let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; + let union_id = builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)?; let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); + env.type_names.insert(*tag_layout); + let named_id = builder.add_make_named(block, MOD_APP, type_name, union_id)?; Ok(named_id) @@ -951,6 +987,24 @@ fn expr_spec( builder.add_get_tuple_field(block, tuple_value_id, index) } + UnionLayout::Recursive(_) => { + let index = (*index) as u32; + let tag_value_id = env.symbols[structure]; + + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?; + let variant_id = builder.add_unwrap_union(block, union_id, *tag_id as u32)?; + + // we're reading from this value, so touch the heap cell + let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?; + builder.add_touch(block, heap_cell)?; + + let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?; + + builder.add_get_tuple_field(block, tuple_value_id, index) + } _ => { // for the moment recursive tag unions don't quite work let value_id = env.symbols[structure]; @@ -1023,12 +1077,12 @@ fn literal_spec( } } -fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result { +fn layout_spec(builder: &mut impl TypeContext, layout: &Layout) -> Result { layout_spec_help(builder, layout, &WhenRecursive::Unreachable) } fn layout_spec_help( - builder: &mut FuncDefBuilder, + builder: &mut impl TypeContext, layout: &Layout, when_recursive: &WhenRecursive, ) -> Result { @@ -1037,23 +1091,45 @@ fn layout_spec_help( match layout { Builtin(builtin) => builtin_spec(builder, builtin, when_recursive), Struct(fields) => build_recursive_tuple_type(builder, fields, when_recursive), - Union(union_layout) => match union_layout { - UnionLayout::NonRecursive(_) => { - let variant_types = build_variant_types(builder, union_layout)?; - builder.add_union_type(&variant_types) + Union(union_layout) => { + let variant_types = build_variant_types(builder, union_layout)?; + + match union_layout { + UnionLayout::NonRecursive(_) => builder.add_union_type(&variant_types), + UnionLayout::Recursive(_) => { + // worst_case_type(builder), + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + Ok(builder.add_named_type(MOD_APP, type_name)) + } + UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), + UnionLayout::NullableWrapped { + nullable_id: _, + other_tags: _, + } => worst_case_type(builder), + UnionLayout::NullableUnwrapped { + nullable_id: _, + other_fields: _, + } => worst_case_type(builder), } - UnionLayout::Recursive(_) => worst_case_type(builder), - UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), - UnionLayout::NullableWrapped { - nullable_id: _, - other_tags: _, - } => worst_case_type(builder), - UnionLayout::NullableUnwrapped { - nullable_id: _, - other_fields: _, - } => worst_case_type(builder), + } + RecursivePointer => match when_recursive { + WhenRecursive::Unreachable => { + // TODO should be unreachable + // unreachable!(), + worst_case_type(builder) + } + WhenRecursive::Loop(union_layout) + if matches!(union_layout, UnionLayout::Recursive(_)) => + { + let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + Ok(builder.add_named_type(MOD_APP, type_name)) + } + WhenRecursive::Loop(_union_layout) => worst_case_type(builder), }, - RecursivePointer => worst_case_type(builder), Closure(_, lambda_set, _) => layout_spec_help( builder, &lambda_set.runtime_representation(), @@ -1063,7 +1139,7 @@ fn layout_spec_help( } fn builtin_spec( - builder: &mut FuncDefBuilder, + builder: &mut impl TypeContext, builtin: &Builtin, when_recursive: &WhenRecursive, ) -> Result { From 97f15bbb496b76c7e9ebed7bcf803f6ee000616a Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 18:10:55 +0200 Subject: [PATCH 42/61] Tag don't unroll recursive types --- compiler/mono/src/ir.rs | 52 ++++++++++++----------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 4f41a292ba..cb8b610fe1 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4180,6 +4180,18 @@ fn convert_tag_union<'a>( let field_symbols; let opt_tag_id_symbol; + // we must derive the union layout from the whole_var, building it up + // from `layouts` would unroll recursive tag unions, and that leads to + // problems down the line because we hash layouts and an unrolled + // version is not the same as the minimal version. + let union_layout = match return_on_layout_error!( + env, + layout_cache.from_var(env.arena, variant_var, env.subs) + ) { + Layout::Union(ul) => ul, + _ => unreachable!(), + }; + use WrappedVariant::*; let (tag, union_layout) = match variant { Recursive { sorted_tag_layouts } => { @@ -4201,19 +4213,6 @@ fn convert_tag_union<'a>( layouts.push(arg_layouts); } - // we must derive the union layout from the whole_var, building it up - // from `layouts` would unroll recursive tag unions, and that leads to - // problems down the line because we hash layouts and an unrolled - // version is not the same as the minimal version. - debug_assert!(layouts.len() > 1); - let union_layout = match return_on_layout_error!( - env, - layout_cache.from_var(env.arena, variant_var, env.subs) - ) { - Layout::Union(ul) => ul, - _ => unreachable!(), - }; - let tag = Expr::Tag { tag_layout: union_layout, tag_name, @@ -4225,8 +4224,8 @@ fn convert_tag_union<'a>( (tag, union_layout) } NonNullableUnwrapped { - fields, tag_name: wrapped_tag_name, + .. } => { debug_assert_eq!(tag_name, wrapped_tag_name); @@ -4240,8 +4239,6 @@ fn convert_tag_union<'a>( temp.into_bump_slice() }; - let union_layout = UnionLayout::NonNullableUnwrapped(fields); - let tag = Expr::Tag { tag_layout: union_layout, tag_name, @@ -4270,8 +4267,6 @@ fn convert_tag_union<'a>( layouts.push(arg_layouts); } - let union_layout = UnionLayout::NonRecursive(layouts.into_bump_slice()); - let tag = Expr::Tag { tag_layout: union_layout, tag_name, @@ -4283,9 +4278,7 @@ fn convert_tag_union<'a>( (tag, union_layout) } NullableWrapped { - nullable_id, - nullable_name: _, - sorted_tag_layouts, + sorted_tag_layouts, .. } => { opt_tag_id_symbol = None; @@ -4304,11 +4297,6 @@ fn convert_tag_union<'a>( layouts.push(arg_layouts); } - let union_layout = UnionLayout::NullableWrapped { - nullable_id, - other_tags: layouts.into_bump_slice(), - }; - let tag = Expr::Tag { tag_layout: union_layout, tag_name, @@ -4319,12 +4307,7 @@ fn convert_tag_union<'a>( (tag, union_layout) } - NullableUnwrapped { - nullable_id, - nullable_name: _, - other_name: _, - other_fields, - } => { + NullableUnwrapped { .. } => { // FIXME drop tag let tag_id_symbol = env.unique_symbol(); opt_tag_id_symbol = Some(tag_id_symbol); @@ -4337,11 +4320,6 @@ fn convert_tag_union<'a>( temp.into_bump_slice() }; - let union_layout = UnionLayout::NullableUnwrapped { - nullable_id, - other_fields, - }; - let tag = Expr::Tag { tag_layout: union_layout, tag_name, From c42a530ec04b2a10b8e4ad74e35db9e9c2e8971d Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 18:15:03 +0200 Subject: [PATCH 43/61] fix reading the wrong memory --- compiler/test_gen/src/gen_tags.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index 40e5403914..1cad68a333 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -97,7 +97,8 @@ fn applied_tag_just_enum() { "# ), (2, 0), - (u8, i64) + (u8, [u8; 7], u8), + |(a, _, c)| (a, c) ); } From 743e8bd86fa3fa60a9fdc55f436eddcc392173f8 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 18:17:21 +0200 Subject: [PATCH 44/61] stop defining the tag id when it is not used --- compiler/mono/src/ir.rs | 28 ++----------------- compiler/test_mono/generated/has_none.txt | 2 -- compiler/test_mono/generated/is_nil.txt | 2 -- .../generated/linked_list_length_twice.txt | 1 - compiler/test_mono/generated/peano.txt | 4 --- compiler/test_mono/generated/peano1.txt | 4 --- compiler/test_mono/generated/peano2.txt | 4 --- 7 files changed, 2 insertions(+), 43 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index cb8b610fe1..e38941b6c4 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4178,7 +4178,6 @@ fn convert_tag_union<'a>( let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args); let field_symbols; - let opt_tag_id_symbol; // we must derive the union layout from the whole_var, building it up // from `layouts` would unroll recursive tag unions, and that leads to @@ -4196,7 +4195,6 @@ fn convert_tag_union<'a>( let (tag, union_layout) = match variant { Recursive { sorted_tag_layouts } => { debug_assert!(sorted_tag_layouts.len() > 1); - opt_tag_id_symbol = None; field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena); @@ -4229,8 +4227,6 @@ fn convert_tag_union<'a>( } => { debug_assert_eq!(tag_name, wrapped_tag_name); - opt_tag_id_symbol = None; - field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena); @@ -4250,8 +4246,6 @@ fn convert_tag_union<'a>( (tag, union_layout) } NonRecursive { sorted_tag_layouts } => { - opt_tag_id_symbol = None; - field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena); @@ -4280,8 +4274,6 @@ fn convert_tag_union<'a>( NullableWrapped { sorted_tag_layouts, .. } => { - opt_tag_id_symbol = None; - field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena); @@ -4308,10 +4300,6 @@ fn convert_tag_union<'a>( (tag, union_layout) } NullableUnwrapped { .. } => { - // FIXME drop tag - let tag_id_symbol = env.unique_symbol(); - opt_tag_id_symbol = Some(tag_id_symbol); - field_symbols = { let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena); @@ -4332,26 +4320,14 @@ fn convert_tag_union<'a>( } }; - let mut stmt = Stmt::Let(assigned, tag, Layout::Union(union_layout), hole); + let stmt = Stmt::Let(assigned, tag, Layout::Union(union_layout), hole); let iter = field_symbols_temp .into_iter() .map(|x| x.2 .0) .rev() .zip(field_symbols.iter().rev()); - stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt); - - if let Some(tag_id_symbol) = opt_tag_id_symbol { - // define the tag id - stmt = Stmt::Let( - tag_id_symbol, - Expr::Literal(Literal::Int(tag_id as i128)), - union_layout.tag_id_layout(), - arena.alloc(stmt), - ); - } - - stmt + assign_to_symbols(env, procs, layout_cache, iter, stmt) } } } diff --git a/compiler/test_mono/generated/has_none.txt b/compiler/test_mono/generated/has_none.txt index cfbfaadfae..49a7106b8f 100644 --- a/compiler/test_mono/generated/has_none.txt +++ b/compiler/test_mono/generated/has_none.txt @@ -21,10 +21,8 @@ procedure Test.3 (Test.4): jump Test.13 Test.4; procedure Test.0 (): - let Test.28 = 0i64; let Test.30 = 3i64; let Test.26 = Just Test.30; - let Test.29 = 1i64; let Test.27 = Nil ; let Test.12 = Cons Test.26 Test.27; let Test.11 = CallByName Test.3 Test.12; diff --git a/compiler/test_mono/generated/is_nil.txt b/compiler/test_mono/generated/is_nil.txt index ec6a558c3a..55329f43fc 100644 --- a/compiler/test_mono/generated/is_nil.txt +++ b/compiler/test_mono/generated/is_nil.txt @@ -10,9 +10,7 @@ procedure Test.2 (Test.3): ret Test.11; procedure Test.0 (): - let Test.17 = 0i64; let Test.15 = 2i64; - let Test.18 = 1i64; let Test.16 = Nil ; let Test.9 = Cons Test.15 Test.16; let Test.8 = CallByName Test.2 Test.9; diff --git a/compiler/test_mono/generated/linked_list_length_twice.txt b/compiler/test_mono/generated/linked_list_length_twice.txt index 92aa6d9c20..38eea70396 100644 --- a/compiler/test_mono/generated/linked_list_length_twice.txt +++ b/compiler/test_mono/generated/linked_list_length_twice.txt @@ -17,7 +17,6 @@ procedure Test.3 (Test.5): ret Test.14; procedure Test.0 (): - let Test.21 = 1i64; let Test.2 = Nil ; let Test.8 = CallByName Test.3 Test.2; let Test.9 = CallByName Test.3 Test.2; diff --git a/compiler/test_mono/generated/peano.txt b/compiler/test_mono/generated/peano.txt index ba743a0f1d..f24478a76d 100644 --- a/compiler/test_mono/generated/peano.txt +++ b/compiler/test_mono/generated/peano.txt @@ -1,8 +1,4 @@ procedure Test.0 (): - let Test.9 = 0i64; - let Test.11 = 0i64; - let Test.13 = 0i64; - let Test.14 = 1i64; let Test.12 = Z ; let Test.10 = S Test.12; let Test.8 = S Test.10; diff --git a/compiler/test_mono/generated/peano1.txt b/compiler/test_mono/generated/peano1.txt index df1d0183c7..7dfd0943f2 100644 --- a/compiler/test_mono/generated/peano1.txt +++ b/compiler/test_mono/generated/peano1.txt @@ -1,8 +1,4 @@ procedure Test.0 (): - let Test.13 = 0i64; - let Test.15 = 0i64; - let Test.17 = 0i64; - let Test.18 = 1i64; let Test.16 = Z ; let Test.14 = S Test.16; let Test.12 = S Test.14; diff --git a/compiler/test_mono/generated/peano2.txt b/compiler/test_mono/generated/peano2.txt index 4a1a5a234f..e52c3eca2d 100644 --- a/compiler/test_mono/generated/peano2.txt +++ b/compiler/test_mono/generated/peano2.txt @@ -1,8 +1,4 @@ procedure Test.0 (): - let Test.19 = 0i64; - let Test.21 = 0i64; - let Test.23 = 0i64; - let Test.24 = 1i64; let Test.22 = Z ; let Test.20 = S Test.22; let Test.18 = S Test.20; From 9b8f8b3c166327996304b170c54146cb9db43666 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 18:23:57 +0200 Subject: [PATCH 45/61] AppliedTag don't unroll recursive layouts --- compiler/mono/src/ir.rs | 65 ++++------------------- compiler/test_mono/generated/has_none.txt | 4 +- compiler/test_mono/generated/peano.txt | 6 +-- compiler/test_mono/generated/peano1.txt | 6 +-- compiler/test_mono/generated/peano2.txt | 6 +-- 5 files changed, 20 insertions(+), 67 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index e38941b6c4..9f8f08efb3 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -7005,6 +7005,15 @@ fn from_can_pattern_help<'a>( temp }; + // we must derive the union layout from the whole_var, building it up + // from `layouts` would unroll recursive tag unions, and that leads to + // problems down the line because we hash layouts and an unrolled + // version is not the same as the minimal version. + let layout = match layout_cache.from_var(env.arena, *whole_var, env.subs) { + Ok(Layout::Union(ul)) => ul, + _ => unreachable!(), + }; + use WrappedVariant::*; match variant { NonRecursive { @@ -7049,19 +7058,6 @@ fn from_can_pattern_help<'a>( )); } - let layouts: Vec<&'a [Layout<'a>]> = { - let mut temp = Vec::with_capacity_in(tags.len(), env.arena); - - for (_, arg_layouts) in tags.into_iter() { - temp.push(*arg_layouts); - } - - temp - }; - - debug_assert!(layouts.len() > 1); - let layout = UnionLayout::NonRecursive(layouts.into_bump_slice()); - Pattern::AppliedTag { tag_name: tag_name.clone(), tag_id: tag_id as u8, @@ -7107,27 +7103,6 @@ fn from_can_pattern_help<'a>( )); } - let layouts: Vec<&'a [Layout<'a>]> = { - let mut temp = Vec::with_capacity_in(tags.len(), env.arena); - - for (_, arg_layouts) in tags.into_iter() { - temp.push(*arg_layouts); - } - - temp - }; - - // we must derive the union layout from the whole_var, building it up - // from `layouts` would unroll recursive tag unions, and that leads to - // problems down the line because we hash layouts and an unrolled - // version is not the same as the minimal version. - debug_assert!(layouts.len() > 1); - let layout = - match layout_cache.from_var(env.arena, *whole_var, env.subs) { - Ok(Layout::Union(ul)) => ul, - _ => unreachable!(), - }; - Pattern::AppliedTag { tag_name: tag_name.clone(), tag_id: tag_id as u8, @@ -7171,8 +7146,6 @@ fn from_can_pattern_help<'a>( )); } - let layout = UnionLayout::NonNullableUnwrapped(fields); - Pattern::AppliedTag { tag_name: tag_name.clone(), tag_id: tag_id as u8, @@ -7246,21 +7219,6 @@ fn from_can_pattern_help<'a>( )); } - let layouts: Vec<&'a [Layout<'a>]> = { - let mut temp = Vec::with_capacity_in(tags.len(), env.arena); - - for (_, arg_layouts) in tags.into_iter() { - temp.push(*arg_layouts); - } - - temp - }; - - let layout = UnionLayout::NullableWrapped { - nullable_id, - other_tags: layouts.into_bump_slice(), - }; - Pattern::AppliedTag { tag_name: tag_name.clone(), tag_id: tag_id as u8, @@ -7317,11 +7275,6 @@ fn from_can_pattern_help<'a>( )); } - let layout = UnionLayout::NullableUnwrapped { - nullable_id, - other_fields, - }; - Pattern::AppliedTag { tag_name: tag_name.clone(), tag_id: tag_id as u8, diff --git a/compiler/test_mono/generated/has_none.txt b/compiler/test_mono/generated/has_none.txt index 49a7106b8f..5341f99624 100644 --- a/compiler/test_mono/generated/has_none.txt +++ b/compiler/test_mono/generated/has_none.txt @@ -21,8 +21,8 @@ procedure Test.3 (Test.4): jump Test.13 Test.4; procedure Test.0 (): - let Test.30 = 3i64; - let Test.26 = Just Test.30; + let Test.28 = 3i64; + let Test.26 = Just Test.28; let Test.27 = Nil ; let Test.12 = Cons Test.26 Test.27; let Test.11 = CallByName Test.3 Test.12; diff --git a/compiler/test_mono/generated/peano.txt b/compiler/test_mono/generated/peano.txt index f24478a76d..49814c63b4 100644 --- a/compiler/test_mono/generated/peano.txt +++ b/compiler/test_mono/generated/peano.txt @@ -1,6 +1,6 @@ procedure Test.0 (): - let Test.12 = Z ; - let Test.10 = S Test.12; - let Test.8 = S Test.10; + let Test.10 = Z ; + let Test.9 = S Test.10; + let Test.8 = S Test.9; let Test.2 = S Test.8; ret Test.2; diff --git a/compiler/test_mono/generated/peano1.txt b/compiler/test_mono/generated/peano1.txt index 7dfd0943f2..bcf0d7a27d 100644 --- a/compiler/test_mono/generated/peano1.txt +++ b/compiler/test_mono/generated/peano1.txt @@ -1,7 +1,7 @@ procedure Test.0 (): - let Test.16 = Z ; - let Test.14 = S Test.16; - let Test.12 = S Test.14; + let Test.14 = Z ; + let Test.13 = S Test.14; + let Test.12 = S Test.13; let Test.2 = S Test.12; let Test.9 = 1i64; let Test.10 = GetTagId Test.2; diff --git a/compiler/test_mono/generated/peano2.txt b/compiler/test_mono/generated/peano2.txt index e52c3eca2d..11ad76dbaf 100644 --- a/compiler/test_mono/generated/peano2.txt +++ b/compiler/test_mono/generated/peano2.txt @@ -1,7 +1,7 @@ procedure Test.0 (): - let Test.22 = Z ; - let Test.20 = S Test.22; - let Test.18 = S Test.20; + let Test.20 = Z ; + let Test.19 = S Test.20; + let Test.18 = S Test.19; let Test.2 = S Test.18; let Test.15 = 0i64; let Test.16 = GetTagId Test.2; From 03422a9907394a289d6940cd059479feff55361d Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 20:16:56 +0200 Subject: [PATCH 46/61] alias analysis for NullableUnwrapped --- compiler/mono/src/alias_analysis.rs | 101 +++++++++++++++++++++------- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 831bdd8afd..2684f2884e 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -845,6 +845,19 @@ fn lowlevel_spec( } } +fn recursive_tag_variant( + builder: &mut impl TypeContext, + union_layout: &UnionLayout, + fields: &[Layout], +) -> Result { + let when_recursive = WhenRecursive::Loop(*union_layout); + + let data_id = build_recursive_tuple_type(builder, fields, &when_recursive)?; + let cell_id = builder.add_heap_cell_type(); + + builder.add_tuple_type(&[cell_id, data_id]) +} + fn build_variant_types( builder: &mut impl TypeContext, union_layout: &UnionLayout, @@ -864,17 +877,12 @@ fn build_variant_types( Recursive(tags) => { result = Vec::with_capacity(tags.len()); - let when_recursive = WhenRecursive::Loop(*union_layout); - for tag in tags.iter() { - let data_id = build_recursive_tuple_type(builder, tag, &when_recursive)?; - let cell_id = builder.add_heap_cell_type(); - let value_id = builder.add_tuple_type(&[cell_id, data_id])?; - result.push(value_id); + result.push(recursive_tag_variant(builder, union_layout, tag)?); } } NonNullableUnwrapped(fields) => { - result = vec![build_tuple_type(builder, fields)?]; + result = vec![recursive_tag_variant(builder, union_layout, fields)?]; } NullableWrapped { nullable_id, @@ -885,14 +893,14 @@ fn build_variant_types( let cutoff = *nullable_id as usize; for tag in tags[..cutoff].iter() { - result.push(build_tuple_type(builder, tag)?); + result.push(recursive_tag_variant(builder, union_layout, tag)?); } let unit = builder.add_tuple_type(&[])?; result.push(unit); for tag in tags[cutoff..].iter() { - result.push(build_tuple_type(builder, tag)?); + result.push(recursive_tag_variant(builder, union_layout, tag)?); } } NullableUnwrapped { @@ -900,7 +908,7 @@ fn build_variant_types( other_fields: fields, } => { let unit = builder.add_tuple_type(&[])?; - let other_type = build_tuple_type(builder, fields)?; + let other_type = recursive_tag_variant(builder, union_layout, fields)?; if *nullable_id { // nullable_id == 1 @@ -959,17 +967,33 @@ fn expr_spec<'a>( env.type_names.insert(*tag_layout); - let named_id = builder.add_make_named(block, MOD_APP, type_name, union_id)?; - - Ok(named_id) + builder.add_make_named(block, MOD_APP, type_name, union_id) } - UnionLayout::NonNullableUnwrapped(_) - | UnionLayout::NullableWrapped { .. } - | UnionLayout::NullableUnwrapped { .. } => { + UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableWrapped { .. } => { let result_type = worst_case_type(builder)?; let value_id = build_tuple_value(builder, env, block, arguments)?; builder.add_unknown_with(block, &[value_id], result_type) } + UnionLayout::NullableUnwrapped { nullable_id, .. } => { + let union_id = if *tag_id == *nullable_id as u8 { + let value_id = builder.add_make_tuple(block, &[])?; + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + } else { + let data_id = build_tuple_value(builder, env, block, arguments)?; + let cell_id = builder.add_new_heap_cell(block)?; + + let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; + + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + }; + + let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + env.type_names.insert(*tag_layout); + + builder.add_make_named(block, MOD_APP, type_name, union_id) + } } } Struct(fields) => build_tuple_value(builder, env, block, fields), @@ -1005,6 +1029,24 @@ fn expr_spec<'a>( builder.add_get_tuple_field(block, tuple_value_id, index) } + UnionLayout::NullableUnwrapped { .. } => { + let index = (*index) as u32; + let tag_value_id = env.symbols[structure]; + + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?; + let variant_id = builder.add_unwrap_union(block, union_id, *tag_id as u32)?; + + // we're reading from this value, so touch the heap cell + let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?; + builder.add_touch(block, heap_cell)?; + + let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?; + + builder.add_get_tuple_field(block, tuple_value_id, index) + } _ => { // for the moment recursive tag unions don't quite work let value_id = env.symbols[structure]; @@ -1097,7 +1139,6 @@ fn layout_spec_help( match union_layout { UnionLayout::NonRecursive(_) => builder.add_union_type(&variant_types), UnionLayout::Recursive(_) => { - // worst_case_type(builder), let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); @@ -1111,7 +1152,12 @@ fn layout_spec_help( UnionLayout::NullableUnwrapped { nullable_id: _, other_fields: _, - } => worst_case_type(builder), + } => { + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + Ok(builder.add_named_type(MOD_APP, type_name)) + } } } RecursivePointer => match when_recursive { @@ -1120,15 +1166,18 @@ fn layout_spec_help( // unreachable!(), worst_case_type(builder) } - WhenRecursive::Loop(union_layout) - if matches!(union_layout, UnionLayout::Recursive(_)) => - { - let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); + WhenRecursive::Loop(union_layout) => match union_layout { + UnionLayout::NonRecursive(_) => unreachable!(), + UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. } => { + let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); - Ok(builder.add_named_type(MOD_APP, type_name)) - } - WhenRecursive::Loop(_union_layout) => worst_case_type(builder), + Ok(builder.add_named_type(MOD_APP, type_name)) + } + UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableWrapped { .. } => { + worst_case_type(builder) + } + }, }, Closure(_, lambda_set, _) => layout_spec_help( builder, From 720440b3f9fe52f4fba790f2f1fbb169b6c8c233 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 20:22:59 +0200 Subject: [PATCH 47/61] alias analysis for NullableWrapped --- compiler/mono/src/alias_analysis.rs | 69 +++++++++++++---------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 2684f2884e..b4e7b8701a 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -969,11 +969,31 @@ fn expr_spec<'a>( builder.add_make_named(block, MOD_APP, type_name, union_id) } - UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableWrapped { .. } => { + UnionLayout::NonNullableUnwrapped(_) => { let result_type = worst_case_type(builder)?; let value_id = build_tuple_value(builder, env, block, arguments)?; builder.add_unknown_with(block, &[value_id], result_type) } + UnionLayout::NullableWrapped { nullable_id, .. } => { + let union_id = if *tag_id == *nullable_id as u8 { + let value_id = builder.add_make_tuple(block, &[])?; + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + } else { + let data_id = build_tuple_value(builder, env, block, arguments)?; + let cell_id = builder.add_new_heap_cell(block)?; + + let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; + + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + }; + + let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + env.type_names.insert(*tag_layout); + + builder.add_make_named(block, MOD_APP, type_name, union_id) + } UnionLayout::NullableUnwrapped { nullable_id, .. } => { let union_id = if *tag_id == *nullable_id as u8 { let value_id = builder.add_make_tuple(block, &[])?; @@ -1011,25 +1031,9 @@ fn expr_spec<'a>( builder.add_get_tuple_field(block, tuple_value_id, index) } - UnionLayout::Recursive(_) => { - let index = (*index) as u32; - let tag_value_id = env.symbols[structure]; - - let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); - - let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?; - let variant_id = builder.add_unwrap_union(block, union_id, *tag_id as u32)?; - - // we're reading from this value, so touch the heap cell - let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?; - builder.add_touch(block, heap_cell)?; - - let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?; - - builder.add_get_tuple_field(block, tuple_value_id, index) - } - UnionLayout::NullableUnwrapped { .. } => { + UnionLayout::Recursive(_) + | UnionLayout::NullableUnwrapped { .. } + | UnionLayout::NullableWrapped { .. } => { let index = (*index) as u32; let tag_value_id = env.symbols[structure]; @@ -1138,26 +1142,15 @@ fn layout_spec_help( match union_layout { UnionLayout::NonRecursive(_) => builder.add_union_type(&variant_types), - UnionLayout::Recursive(_) => { + UnionLayout::Recursive(_) + | UnionLayout::NullableUnwrapped { .. } + | UnionLayout::NullableWrapped { .. } => { let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); Ok(builder.add_named_type(MOD_APP, type_name)) } UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), - UnionLayout::NullableWrapped { - nullable_id: _, - other_tags: _, - } => worst_case_type(builder), - UnionLayout::NullableUnwrapped { - nullable_id: _, - other_fields: _, - } => { - let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); - - Ok(builder.add_named_type(MOD_APP, type_name)) - } } } RecursivePointer => match when_recursive { @@ -1168,15 +1161,15 @@ fn layout_spec_help( } WhenRecursive::Loop(union_layout) => match union_layout { UnionLayout::NonRecursive(_) => unreachable!(), - UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. } => { + UnionLayout::Recursive(_) + | UnionLayout::NullableUnwrapped { .. } + | UnionLayout::NullableWrapped { .. } => { let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); Ok(builder.add_named_type(MOD_APP, type_name)) } - UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableWrapped { .. } => { - worst_case_type(builder) - } + UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), }, }, Closure(_, lambda_set, _) => layout_spec_help( From abbbc67caf7619a20df25d3daf1280297775ecac Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 20:42:33 +0200 Subject: [PATCH 48/61] alias analysis for NonNullableUnwrapped --- compiler/mono/src/alias_analysis.rs | 61 ++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index b4e7b8701a..49c44855cc 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -23,7 +23,7 @@ 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) } -const DEBUG: bool = false; +const DEBUG: bool = true; const SIZE: usize = if DEBUG { 50 } else { 16 }; #[derive(Debug, Clone, Copy, Hash)] @@ -184,7 +184,12 @@ where let mut builder = TypeDefBuilder::new(); let variant_types = build_variant_types(&mut builder, &union_layout)?; - let root_type = builder.add_union_type(&variant_types)?; + let root_type = if let UnionLayout::NonNullableUnwrapped(_) = union_layout { + debug_assert_eq!(variant_types.len(), 1); + variant_types[0] + } else { + builder.add_union_type(&variant_types)? + }; let type_def = builder.build(root_type)?; @@ -922,6 +927,7 @@ fn build_variant_types( Ok(result) } +#[allow(dead_code)] fn worst_case_type(context: &mut impl TypeContext) -> Result { let cell = context.add_heap_cell_type(); context.add_bag_type(cell) @@ -970,9 +976,17 @@ fn expr_spec<'a>( builder.add_make_named(block, MOD_APP, type_name, union_id) } UnionLayout::NonNullableUnwrapped(_) => { - let result_type = worst_case_type(builder)?; - let value_id = build_tuple_value(builder, env, block, arguments)?; - builder.add_unknown_with(block, &[value_id], result_type) + let data_id = build_tuple_value(builder, env, block, arguments)?; + let cell_id = builder.add_new_heap_cell(block)?; + + let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; + + let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + env.type_names.insert(*tag_layout); + + builder.add_make_named(block, MOD_APP, type_name, value_id) } UnionLayout::NullableWrapped { nullable_id, .. } => { let union_id = if *tag_id == *nullable_id as u8 { @@ -1051,12 +1065,25 @@ fn expr_spec<'a>( builder.add_get_tuple_field(block, tuple_value_id, index) } - _ => { - // for the moment recursive tag unions don't quite work - let value_id = env.symbols[structure]; - let result_type = - layout_spec_help(builder, layout, &WhenRecursive::Loop(*union_layout))?; - builder.add_unknown_with(block, &[value_id], result_type) + UnionLayout::NonNullableUnwrapped { .. } => { + let index = (*index) as u32; + debug_assert!(*tag_id == 0); + + let tag_value_id = env.symbols[structure]; + + let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + let variant_id = + builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?; + + // we're reading from this value, so touch the heap cell + let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?; + builder.add_touch(block, heap_cell)?; + + let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?; + + builder.add_get_tuple_field(block, tuple_value_id, index) } }, StructAtIndex { @@ -1144,32 +1171,30 @@ fn layout_spec_help( UnionLayout::NonRecursive(_) => builder.add_union_type(&variant_types), UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. } - | UnionLayout::NullableWrapped { .. } => { + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NonNullableUnwrapped(_) => { let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); Ok(builder.add_named_type(MOD_APP, type_name)) } - UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), } } RecursivePointer => match when_recursive { WhenRecursive::Unreachable => { - // TODO should be unreachable - // unreachable!(), - worst_case_type(builder) + unreachable!() } WhenRecursive::Loop(union_layout) => match union_layout { UnionLayout::NonRecursive(_) => unreachable!(), UnionLayout::Recursive(_) | UnionLayout::NullableUnwrapped { .. } - | UnionLayout::NullableWrapped { .. } => { + | UnionLayout::NullableWrapped { .. } + | UnionLayout::NonNullableUnwrapped(_) => { let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); let type_name = TypeName(&type_name_bytes); Ok(builder.add_named_type(MOD_APP, type_name)) } - UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder), }, }, Closure(_, lambda_set, _) => layout_spec_help( From c9809fff2a46b32c904905e3662671e575e9a6d6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 20:52:47 +0200 Subject: [PATCH 49/61] refactor --- compiler/mono/src/alias_analysis.rs | 83 +++++++++-------------------- 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 49c44855cc..dc679a1f4f 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -954,31 +954,15 @@ fn expr_spec<'a>( } => { let variant_types = build_variant_types(builder, tag_layout)?; - match tag_layout { + let data_id = build_tuple_value(builder, env, block, arguments)?; + let cell_id = builder.add_new_heap_cell(block)?; + + let value_id = match tag_layout { UnionLayout::NonRecursive(_) => { let value_id = build_tuple_value(builder, env, block, arguments)?; - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id) - } - UnionLayout::Recursive(_) => { - let data_id = build_tuple_value(builder, env, block, arguments)?; - let cell_id = builder.add_new_heap_cell(block)?; - - let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; - - let union_id = - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)?; - - let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); - - env.type_names.insert(*tag_layout); - - builder.add_make_named(block, MOD_APP, type_name, union_id) + return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id); } UnionLayout::NonNullableUnwrapped(_) => { - let data_id = build_tuple_value(builder, env, block, arguments)?; - let cell_id = builder.add_new_heap_cell(block)?; - let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); @@ -986,49 +970,34 @@ fn expr_spec<'a>( env.type_names.insert(*tag_layout); - builder.add_make_named(block, MOD_APP, type_name, value_id) + return builder.add_make_named(block, MOD_APP, type_name, value_id); } + UnionLayout::Recursive(_) => builder.add_make_tuple(block, &[cell_id, data_id])?, UnionLayout::NullableWrapped { nullable_id, .. } => { - let union_id = if *tag_id == *nullable_id as u8 { - let value_id = builder.add_make_tuple(block, &[])?; - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + if *tag_id == *nullable_id as u8 { + data_id } else { - let data_id = build_tuple_value(builder, env, block, arguments)?; - let cell_id = builder.add_new_heap_cell(block)?; - - let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; - - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? - }; - - let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); - - env.type_names.insert(*tag_layout); - - builder.add_make_named(block, MOD_APP, type_name, union_id) + builder.add_make_tuple(block, &[cell_id, data_id])? + } } UnionLayout::NullableUnwrapped { nullable_id, .. } => { - let union_id = if *tag_id == *nullable_id as u8 { - let value_id = builder.add_make_tuple(block, &[])?; - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? + if *tag_id == *nullable_id as u8 { + data_id } else { - let data_id = build_tuple_value(builder, env, block, arguments)?; - let cell_id = builder.add_new_heap_cell(block)?; - - let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?; - - builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)? - }; - - let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); - let type_name = TypeName(&type_name_bytes); - - env.type_names.insert(*tag_layout); - - builder.add_make_named(block, MOD_APP, type_name, union_id) + builder.add_make_tuple(block, &[cell_id, data_id])? + } } - } + }; + + let union_id = + builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)?; + + let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + env.type_names.insert(*tag_layout); + + builder.add_make_named(block, MOD_APP, type_name, union_id) } Struct(fields) => build_tuple_value(builder, env, block, fields), UnionAtIndex { From 55c760dd7af466ffbb23ad2c50497233a9382e2d Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 16 Jul 2021 20:56:24 +0200 Subject: [PATCH 50/61] turn debug mode off --- compiler/mono/src/alias_analysis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index dc679a1f4f..0e6626a04b 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -23,7 +23,7 @@ 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) } -const DEBUG: bool = true; +const DEBUG: bool = false; const SIZE: usize = if DEBUG { 50 } else { 16 }; #[derive(Debug, Clone, Copy, Hash)] From 1f1cf180c4c9e3a208b3847af61edf8a692efa41 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 17 Jul 2021 00:07:29 +0200 Subject: [PATCH 51/61] fix merge conflict --- compiler/gen_llvm/src/llvm/build.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index d9e3643e38..76e2ed8b70 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1158,7 +1158,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env, union_layout, tag_id_type, - union_layout, field_layouts, *index as usize, argument.into_pointer_value(), @@ -1200,7 +1199,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( env, union_layout, tag_id_type, - union_layout, field_layouts, *index as usize, argument.into_pointer_value(), @@ -1826,7 +1824,6 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, union_layout: &UnionLayout<'a>, tag_id_type: IntType<'ctx>, - union_layout: &UnionLayout<'a>, field_layouts: &[Layout<'_>], index: usize, value: PointerValue<'ctx>, From bfd189aa196ae71cf8671069e3b5b05329ce97a3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 17 Jul 2021 21:43:40 +0200 Subject: [PATCH 52/61] clear tag id from reused pointer --- compiler/gen_llvm/src/llvm/build.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 07cc897d91..fe19814797 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1010,7 +1010,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( let else_block = ctx.append_basic_block(parent, "else_decref"); let cont_block = ctx.append_basic_block(parent, "cont"); - let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, tag_ptr); + let refcount_ptr = + PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr)); let is_unique = refcount_ptr.is_1(env); env.builder @@ -1752,9 +1753,11 @@ fn allocate_tag<'a, 'ctx, 'env>( let reuse_ptr = { env.builder.position_at_end(else_block); + let cleared = tag_pointer_clear_tag_id(env, ptr); + env.builder.build_unconditional_branch(cont_block); - ptr + cleared }; { From c57913f767b0f430b1aaed251e9fe7a6596ba468 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 17 Jul 2021 22:00:57 +0200 Subject: [PATCH 53/61] fix hash and compare --- compiler/gen_llvm/src/llvm/build.rs | 9 +++++++ compiler/gen_llvm/src/llvm/build_hash.rs | 13 ++++------ compiler/gen_llvm/src/llvm/compare.rs | 30 ++++++++++-------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index fe19814797..4232601908 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -170,6 +170,15 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { self.ptr_bytes * 2 } + pub fn tag_id_bits(&self) -> u32 { + match self.ptr_bytes { + 4 => 2, + 8 => 3, + 16 => 4, + _ => 0, + } + } + pub fn build_intrinsic_call( &self, intrinsic_name: &'static str, diff --git a/compiler/gen_llvm/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs index 61a74dd232..99af6b9e11 100644 --- a/compiler/gen_llvm/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -1,5 +1,6 @@ use crate::debug_info_init; use crate::llvm::bitcode::call_bitcode_fn; +use crate::llvm::build::tag_pointer_clear_tag_id; use crate::llvm::build::Env; use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX}; use crate::llvm::build_str; @@ -493,14 +494,9 @@ fn hash_tag<'a, 'ctx, 'env>( ); // hash the tag data - let answer = hash_ptr_to_struct( - env, - layout_ids, - union_layout, - field_layouts, - seed, - tag.into_pointer_value(), - ); + let tag = tag_pointer_clear_tag_id(env, tag.into_pointer_value()); + let answer = + hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag); merge_phi.add_incoming(&[(&answer, block)]); env.builder.build_unconditional_branch(merge_block); @@ -598,6 +594,7 @@ fn hash_tag<'a, 'ctx, 'env>( ); // hash tag data + let tag = tag_pointer_clear_tag_id(env, tag); let answer = hash_ptr_to_struct( env, layout_ids, diff --git a/compiler/gen_llvm/src/llvm/compare.rs b/compiler/gen_llvm/src/llvm/compare.rs index 2a9d1d3fa3..ffc0b04e99 100644 --- a/compiler/gen_llvm/src/llvm/compare.rs +++ b/compiler/gen_llvm/src/llvm/compare.rs @@ -1,5 +1,5 @@ -use crate::llvm::build::Env; use crate::llvm::build::{cast_block_of_memory_to_tag, get_tag_id, FAST_CALL_CONV}; +use crate::llvm::build::{tag_pointer_clear_tag_id, Env}; use crate::llvm::build_list::{list_len, load_list_ptr}; use crate::llvm::build_str::str_equal; use crate::llvm::convert::basic_type_from_layout; @@ -925,6 +925,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( let id1 = get_tag_id(env, parent, union_layout, tag1); let id2 = get_tag_id(env, parent, union_layout, tag2); + // clear the tag_id so we get a pointer to the actual data + let tag1 = tag_pointer_clear_tag_id(env, tag1.into_pointer_value()); + let tag2 = tag_pointer_clear_tag_id(env, tag2.into_pointer_value()); + let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); let same_tag = @@ -944,14 +948,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( let block = env.context.append_basic_block(parent, "tag_id_modify"); env.builder.position_at_end(block); - let answer = eq_ptr_to_struct( - env, - layout_ids, - union_layout, - field_layouts, - tag1.into_pointer_value(), - tag2.into_pointer_value(), - ); + let answer = + eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2); env.builder.build_return(Some(&answer)); @@ -1073,6 +1071,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( let id1 = get_tag_id(env, parent, union_layout, tag1); let id2 = get_tag_id(env, parent, union_layout, tag2); + // clear the tag_id so we get a pointer to the actual data + let tag1 = tag_pointer_clear_tag_id(env, tag1.into_pointer_value()); + let tag2 = tag_pointer_clear_tag_id(env, tag2.into_pointer_value()); + let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields"); let same_tag = @@ -1093,14 +1095,8 @@ fn build_tag_eq_help<'a, 'ctx, 'env>( let block = env.context.append_basic_block(parent, "tag_id_modify"); env.builder.position_at_end(block); - let answer = eq_ptr_to_struct( - env, - layout_ids, - union_layout, - field_layouts, - tag1.into_pointer_value(), - tag2.into_pointer_value(), - ); + let answer = + eq_ptr_to_struct(env, layout_ids, union_layout, field_layouts, tag1, tag2); env.builder.build_return(Some(&answer)); From e0b9377dcfea26f537023aa431679ed3fc4abd86 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 17 Jul 2021 22:18:33 +0200 Subject: [PATCH 54/61] refactor wrapped tag creation --- compiler/gen_llvm/src/llvm/build.rs | 259 ++++++++++++---------------- 1 file changed, 114 insertions(+), 145 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 4232601908..faac418f23 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1253,6 +1253,98 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } } +fn build_wrapped_tag<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + arguments: &[Symbol], +) -> BasicValueEnum<'ctx> { + todo!() +} + +fn build_wrapped_tag_store_id<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + scope: &Scope<'a, 'ctx>, + union_layout: &UnionLayout<'a>, + tag_id: u8, + arguments: &[Symbol], + tag_field_layouts: &[Layout<'a>], + tags: &[&[Layout<'a>]], + reuse_allocation: Option>, + parent: FunctionValue<'ctx>, +) -> BasicValueEnum<'ctx> { + let ctx = env.context; + let builder = env.builder; + + let tag_id_layout = union_layout.tag_id_layout(); + + // Determine types + let num_fields = arguments.len() + 1; + let mut field_types = Vec::with_capacity_in(num_fields, env.arena); + let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); + + for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { + let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); + + let field_type = basic_type_from_layout(env, tag_field_layout); + + field_types.push(field_type); + + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(val.is_pointer_value()); + + // we store recursive pointers as `i64*` + let ptr = env.builder.build_bitcast( + val, + ctx.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ); + + field_vals.push(ptr); + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + field_vals.push(val); + } + } + + // Create the struct_type + let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); + + let tag_id_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") + .unwrap(); + + let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); + + env.builder + .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); + + let opaque_struct_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") + .unwrap(); + + let struct_type = env.context.struct_type(&field_types, false); + let struct_ptr = env + .builder + .build_bitcast( + opaque_struct_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() +} + pub fn build_tag<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, @@ -1263,7 +1355,6 @@ pub fn build_tag<'a, 'ctx, 'env>( parent: FunctionValue<'ctx>, ) -> BasicValueEnum<'ctx> { let tag_id_layout = union_layout.tag_id_layout(); - let union_size = union_layout.number_of_tags(); match union_layout { @@ -1354,78 +1445,19 @@ pub fn build_tag<'a, 'ctx, 'env>( UnionLayout::Recursive(tags) => { debug_assert!(union_size > 1); - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - let tag_field_layouts = &tags[tag_id as usize]; - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); - - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - - // Create the struct_type - let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); - - let tag_id_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") - .unwrap(); - - let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); - - env.builder - .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); - - let opaque_struct_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") - .unwrap(); - - let struct_type = env.context.struct_type(&field_types, false); - let struct_ptr = env - .builder - .build_bitcast( - opaque_struct_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "struct_ptr", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() + build_wrapped_tag_store_id( + env, + scope, + union_layout, + tag_id, + arguments, + &tag_field_layouts, + tags, + reuse_allocation, + parent, + ) } UnionLayout::NonNullableUnwrapped(fields) => { debug_assert_eq!(union_size, 1); @@ -1505,14 +1537,6 @@ pub fn build_tag<'a, 'ctx, 'env>( debug_assert!(union_size > 1); - let ctx = env.context; - let builder = env.builder; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - let tag_field_layouts = { use std::cmp::Ordering::*; match tag_id.cmp(&(*nullable_id as u8)) { @@ -1522,72 +1546,17 @@ pub fn build_tag<'a, 'ctx, 'env>( } }; - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let val = load_symbol(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } - - // Create the struct_type - let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); - - let tag_id_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") - .unwrap(); - - let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); - - env.builder - .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); - - let opaque_struct_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") - .unwrap(); - - let struct_type = env.context.struct_type(&field_types, false); - let struct_ptr = env - .builder - .build_bitcast( - opaque_struct_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "struct_ptr", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } - - raw_data_ptr.into() + build_wrapped_tag_store_id( + env, + scope, + union_layout, + tag_id, + arguments, + &tag_field_layouts, + tags, + reuse_allocation, + parent, + ) } UnionLayout::NullableUnwrapped { nullable_id, From e19b03f29028c46be67579c346b92e3f5dde9590 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 18 Jul 2021 00:04:10 +0200 Subject: [PATCH 55/61] WIP --- compiler/gen_llvm/src/llvm/build.rs | 195 ++++++++++++---------- compiler/gen_llvm/src/llvm/convert.rs | 52 +++--- compiler/gen_llvm/src/llvm/refcounting.rs | 31 +--- compiler/load/src/file.rs | 2 +- compiler/mono/src/layout.rs | 26 ++- compiler/test_gen/src/gen_primitives.rs | 6 +- compiler/test_gen/src/gen_tags.rs | 4 +- 7 files changed, 157 insertions(+), 159 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index faac418f23..b062d2d125 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -170,15 +170,6 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { self.ptr_bytes * 2 } - pub fn tag_id_bits(&self) -> u32 { - match self.ptr_bytes { - 4 => 2, - 8 => 3, - 16 => 4, - _ => 0, - } - } - pub fn build_intrinsic_call( &self, intrinsic_name: &'static str, @@ -902,7 +893,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>( } } -pub const TAG_ID_INDEX: u32 = 1; +const TAG_ID_INDEX: u32 = 1; pub const TAG_DATA_INDEX: u32 = 0; pub fn struct_from_fields<'a, 'ctx, 'env, I>( @@ -1253,14 +1244,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } } +#[allow(clippy::too_many_arguments)] fn build_wrapped_tag<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - arguments: &[Symbol], -) -> BasicValueEnum<'ctx> { - todo!() -} - -fn build_wrapped_tag_store_id<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, union_layout: &UnionLayout<'a>, @@ -1310,39 +1295,62 @@ fn build_wrapped_tag_store_id<'a, 'ctx, 'env>( // Create the struct_type let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); - let tag_id_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") - .unwrap(); - - let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); - - env.builder - .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); - - let opaque_struct_ptr = builder - .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") - .unwrap(); - - let struct_type = env.context.struct_type(&field_types, false); - let struct_ptr = env - .builder - .build_bitcast( - opaque_struct_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "struct_ptr", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + let tag_id_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index") .unwrap(); - builder.build_store(field_ptr, field_val); - } + let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type(); - tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() + env.builder + .build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false)); + + let opaque_struct_ptr = builder + .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") + .unwrap(); + + let struct_type = env.context.struct_type(&field_types, false); + let struct_ptr = env + .builder + .build_bitcast( + opaque_struct_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + raw_data_ptr.into() + } else { + let struct_type = env.context.struct_type(&field_types, false); + let struct_ptr = env + .builder + .build_bitcast( + raw_data_ptr, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in field_vals.into_iter().enumerate() { + let field_ptr = builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + builder.build_store(field_ptr, field_val); + } + + tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() + } } pub fn build_tag<'a, 'ctx, 'env>( @@ -1447,7 +1455,39 @@ pub fn build_tag<'a, 'ctx, 'env>( let tag_field_layouts = &tags[tag_id as usize]; - build_wrapped_tag_store_id( + build_wrapped_tag( + env, + scope, + union_layout, + tag_id, + arguments, + &tag_field_layouts, + tags, + reuse_allocation, + parent, + ) + } + UnionLayout::NullableWrapped { + nullable_id, + other_tags: tags, + } => { + let tag_field_layouts = { + use std::cmp::Ordering::*; + match tag_id.cmp(&(*nullable_id as u8)) { + Equal => { + let layout = Layout::Union(*union_layout); + + return basic_type_from_layout(env, &layout) + .into_pointer_type() + .const_null() + .into(); + } + Less => &tags[tag_id as usize], + Greater => &tags[tag_id as usize - 1], + } + }; + + build_wrapped_tag( env, scope, union_layout, @@ -1522,42 +1562,6 @@ pub fn build_tag<'a, 'ctx, 'env>( data_ptr.into() } - UnionLayout::NullableWrapped { - nullable_id, - other_tags: tags, - } => { - if tag_id == *nullable_id as u8 { - let layout = Layout::Union(*union_layout); - - return basic_type_from_layout(env, &layout) - .into_pointer_type() - .const_null() - .into(); - } - - debug_assert!(union_size > 1); - - let tag_field_layouts = { - use std::cmp::Ordering::*; - match tag_id.cmp(&(*nullable_id as u8)) { - Equal => unreachable!("early return above"), - Less => &tags[tag_id as usize], - Greater => &tags[tag_id as usize - 1], - } - }; - - build_wrapped_tag_store_id( - env, - scope, - union_layout, - tag_id, - arguments, - &tag_field_layouts, - tags, - reuse_allocation, - parent, - ) - } UnionLayout::NullableUnwrapped { nullable_id, other_fields, @@ -1769,9 +1773,13 @@ pub fn get_tag_id<'a, 'ctx, 'env>( get_tag_id_non_recursive(env, tag) } UnionLayout::Recursive(_) => { - // TODO check that all tag ids fit into 2/3 bits - // get_tag_id_wrapped( env, tag_pointer_clear_tag_id(env, argument.into_pointer_value()),) - tag_pointer_read_tag_id(env, argument.into_pointer_value()) + let argument_ptr = argument.into_pointer_value(); + + if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + get_tag_id_wrapped(env, argument_ptr) + } else { + tag_pointer_read_tag_id(env, argument_ptr) + } } UnionLayout::NonNullableUnwrapped(_) => tag_id_int_type.const_zero(), UnionLayout::NullableWrapped { nullable_id, .. } => { @@ -1797,7 +1805,12 @@ pub fn get_tag_id<'a, 'ctx, 'env>( { env.builder.position_at_end(else_block); - let tag_id = get_tag_id_wrapped(env, argument_ptr); + + let tag_id = if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + get_tag_id_wrapped(env, argument_ptr) + } else { + tag_pointer_read_tag_id(env, argument_ptr) + }; env.builder.build_store(result, tag_id); env.builder.build_unconditional_branch(cont_block); } @@ -1933,9 +1946,11 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>( union_layout: UnionLayout<'a>, fields: &[&[Layout<'a>]], ) -> PointerValue<'ctx> { + let ptr_bytes = env.ptr_bytes; + let block_type = block_of_memory_slices(env.context, fields, env.ptr_bytes); - let basic_type = if union_layout.stores_tag_id() { + let basic_type = if union_layout.stores_tag_id_as_data(ptr_bytes) { let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); env.context @@ -1951,7 +1966,7 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>( .max() .unwrap_or_default(); - if union_layout.stores_tag_id() { + if union_layout.stores_tag_id_as_data(ptr_bytes) { stack_size += union_layout.tag_id_layout().stack_size(env.ptr_bytes); } diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 1cb299c0ae..88e1808ec8 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -19,6 +19,9 @@ fn basic_type_from_record<'a, 'ctx, 'env>( .as_basic_type_enum() } +//pub const fn store_tag_id_in_pointer<'a, 'ctx, 'env>( +// env: &crate::llvm::build::Env<'a, 'ctx, 'env>, + pub fn basic_type_from_layout<'a, 'ctx, 'env>( env: &crate::llvm::build::Env<'a, 'ctx, 'env>, layout: &Layout<'_>, @@ -31,21 +34,31 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( basic_type_from_layout(env, &closure_data_layout) } Struct(sorted_fields) => basic_type_from_record(env, sorted_fields), - Union(variant) => { + Union(union_layout) => { use UnionLayout::*; - let tag_id_type = basic_type_from_layout(env, &variant.tag_id_layout()); + let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); - match variant { - NullableWrapped { + match union_layout { + NonRecursive(tags) => { + let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); + + env.context.struct_type(&[data, tag_id_type], false).into() + } + Recursive(tags) + | NullableWrapped { other_tags: tags, .. } => { let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); - env.context - .struct_type(&[data, tag_id_type], false) - .ptr_type(AddressSpace::Generic) - .into() + if union_layout.stores_tag_id_as_data(env.ptr_bytes) { + env.context + .struct_type(&[data, tag_id_type], false) + .ptr_type(AddressSpace::Generic) + .into() + } else { + data.ptr_type(AddressSpace::Generic).into() + } } NullableUnwrapped { other_fields, .. } => { let block = @@ -56,19 +69,6 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes); block.ptr_type(AddressSpace::Generic).into() } - Recursive(tags) => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); - - env.context - .struct_type(&[data, tag_id_type], false) - .ptr_type(AddressSpace::Generic) - .into() - } - NonRecursive(tags) => { - let data = block_of_memory_slices(env.context, tags, env.ptr_bytes); - - env.context.struct_type(&[data, tag_id_type], false).into() - } } } RecursivePointer => { @@ -145,16 +145,6 @@ pub fn union_data_is_struct_type<'ctx>( context.struct_type(&[struct_type.into(), tag_id_type.into()], false) } -pub fn union_data_block_of_memory<'ctx>( - context: &'ctx Context, - tag_id_int_type: IntType<'ctx>, - layouts: &[&[Layout<'_>]], - ptr_bytes: u32, -) -> StructType<'ctx> { - let data_type = block_of_memory_slices(context, layouts, ptr_bytes); - context.struct_type(&[data_type, tag_id_int_type.into()], false) -} - pub fn block_of_memory<'ctx>( context: &'ctx Context, layout: &Layout<'_>, diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 9b60cf9ad1..9c09469a97 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -4,9 +4,7 @@ use crate::llvm::build::{ tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX, }; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; -use crate::llvm::convert::{ - basic_type_from_layout, block_of_memory_slices, ptr_int, union_data_block_of_memory, -}; +use crate::llvm::convert::{basic_type_from_layout, ptr_int}; use bumpalo::collections::Vec; use inkwell::basic_block::BasicBlock; use inkwell::context::Context; @@ -1399,28 +1397,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( debug_assert!(ptr_as_i64_ptr.is_pointer_value()); // therefore we must cast it to our desired type - let union_type = match union_layout { - UnionLayout::NonRecursive(_) => unreachable!(), - UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => { - union_data_block_of_memory( - env.context, - tag_id_int_type, - tags, - env.ptr_bytes, - ) - .into() - } - UnionLayout::NonNullableUnwrapped { .. } - | UnionLayout::NullableUnwrapped { .. } => { - block_of_memory_slices(env.context, tags, env.ptr_bytes) - } - }; - - let recursive_field_ptr = cast_basic_basic( - env.builder, - ptr_as_i64_ptr, - union_type.ptr_type(AddressSpace::Generic).into(), - ); + let union_type = basic_type_from_layout(env, &Layout::Union(union_layout)); + let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type); deferred_rec.push(recursive_field_ptr); } else if field_layout.contains_refcounted() { @@ -1487,9 +1465,6 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( let (_, only_branch) = cases.pop().unwrap(); env.builder.build_unconditional_branch(only_branch); } else { - // read the tag_id - // let current_tag_id = get_tag_id(env, parent, &union_layout, value_ptr.into()); - let default_block = env.context.append_basic_block(parent, "switch_default"); // switch on it diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index fd8fd1dc4b..bc70105ff6 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2077,7 +2077,7 @@ fn update<'a>( &mut state.procedures, ); - Proc::insert_refcount_operations(arena, &mut state.procedures); + // Proc::insert_refcount_operations(arena, &mut state.procedures); // display the mono IR of the module, for debug purposes if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 4bd851184a..48584c947d 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -188,11 +188,29 @@ impl<'a> UnionLayout<'a> { Layout::Builtin(self.tag_id_builtin()) } - pub fn stores_tag_id(&self) -> bool { + fn stores_tag_id_in_pointer_bits(tags: &[&[Layout<'a>]], ptr_bytes: u32) -> bool { + tags.len() <= ptr_bytes as usize + } + + // i.e. it is not implicit and not stored in the pointer bits + pub fn stores_tag_id_as_data(&self, ptr_bytes: u32) -> bool { match self { - UnionLayout::NonRecursive(_) - | UnionLayout::Recursive(_) - | UnionLayout::NullableWrapped { .. } => true, + UnionLayout::NonRecursive(_) => true, + UnionLayout::Recursive(tags) + | UnionLayout::NullableWrapped { + other_tags: tags, .. + } => !Self::stores_tag_id_in_pointer_bits(tags, ptr_bytes), + UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, + } + } + + pub fn stores_tag_id_in_pointer(&self, ptr_bytes: u32) -> bool { + match self { + UnionLayout::NonRecursive(_) => false, + UnionLayout::Recursive(tags) + | UnionLayout::NullableWrapped { + other_tags: tags, .. + } => Self::stores_tag_id_in_pointer_bits(tags, ptr_bytes), UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, } } diff --git a/compiler/test_gen/src/gen_primitives.rs b/compiler/test_gen/src/gen_primitives.rs index 3c932db913..77b4f7cb24 100644 --- a/compiler/test_gen/src/gen_primitives.rs +++ b/compiler/test_gen/src/gen_primitives.rs @@ -1555,9 +1555,9 @@ fn rbtree_balance_full() { balance Red 0 0 Empty Empty "# ), - false, - *const i64, - |x: *const i64| x.is_null() + true, + usize, + |x| x != 0 ); } diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index 38c28b9fff..247bebde87 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -1079,8 +1079,8 @@ fn nested_recursive_literal() { #" ), 0, - &(i64, i64, u8), - |x: &(i64, i64, u8)| x.2 + usize, + |_| 0 ); } From 5012b11129571f840df288c0b58229f72c64a24e Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 18 Jul 2021 00:13:45 +0200 Subject: [PATCH 56/61] fix oversight --- compiler/gen_llvm/src/llvm/build.rs | 3 ++- compiler/gen_llvm/src/llvm/convert.rs | 3 --- compiler/load/src/file.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index b062d2d125..3ae6ed6e1c 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1198,13 +1198,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type(); + let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value()); lookup_at_index_ptr2( env, tag_id_type, union_layout, field_layouts, *index as usize, - argument.into_pointer_value(), + ptr, ) } UnionLayout::NullableUnwrapped { diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 88e1808ec8..729c118626 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -19,9 +19,6 @@ fn basic_type_from_record<'a, 'ctx, 'env>( .as_basic_type_enum() } -//pub const fn store_tag_id_in_pointer<'a, 'ctx, 'env>( -// env: &crate::llvm::build::Env<'a, 'ctx, 'env>, - pub fn basic_type_from_layout<'a, 'ctx, 'env>( env: &crate::llvm::build::Env<'a, 'ctx, 'env>, layout: &Layout<'_>, diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index bc70105ff6..fd8fd1dc4b 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2077,7 +2077,7 @@ fn update<'a>( &mut state.procedures, ); - // Proc::insert_refcount_operations(arena, &mut state.procedures); + Proc::insert_refcount_operations(arena, &mut state.procedures); // display the mono IR of the module, for debug purposes if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS { From 33caa0f26dfcd74171904405d162792b26350ca4 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 18 Jul 2021 00:25:23 +0200 Subject: [PATCH 57/61] cleanup --- compiler/gen_llvm/src/llvm/build.rs | 123 ++++++++++++---------------- 1 file changed, 53 insertions(+), 70 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 3ae6ed6e1c..f9dfd3520f 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -919,6 +919,34 @@ where struct_value.into_struct_value() } +fn struct_pointer_from_fields<'a, 'ctx, 'env, I>( + env: &Env<'a, 'ctx, 'env>, + struct_type: StructType<'ctx>, + input_pointer: PointerValue<'ctx>, + values: I, +) where + I: Iterator)>, +{ + let struct_ptr = env + .builder + .build_bitcast( + input_pointer, + struct_type.ptr_type(AddressSpace::Generic), + "struct_ptr", + ) + .into_pointer_value(); + + // Insert field exprs into struct_val + for (index, field_val) in values { + let field_ptr = env + .builder + .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") + .unwrap(); + + env.builder.build_store(field_ptr, field_val); + } +} + pub fn build_exp_expr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, @@ -1295,6 +1323,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( // Create the struct_type let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); + let struct_type = env.context.struct_type(&field_types, false); if union_layout.stores_tag_id_as_data(env.ptr_bytes) { let tag_id_ptr = builder @@ -1310,45 +1339,21 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( .build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index") .unwrap(); - let struct_type = env.context.struct_type(&field_types, false); - let struct_ptr = env - .builder - .build_bitcast( - opaque_struct_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "struct_ptr", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } + struct_pointer_from_fields( + env, + struct_type, + opaque_struct_ptr, + field_vals.into_iter().enumerate(), + ); raw_data_ptr.into() } else { - let struct_type = env.context.struct_type(&field_types, false); - let struct_ptr = env - .builder - .build_bitcast( - raw_data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "struct_ptr", - ) - .into_pointer_value(); - - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } + struct_pointer_from_fields( + env, + struct_type, + raw_data_ptr, + field_vals.into_iter().enumerate(), + ); tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() } @@ -1506,7 +1511,6 @@ pub fn build_tag<'a, 'ctx, 'env>( debug_assert_eq!(arguments.len(), fields.len()); let ctx = env.context; - let builder = env.builder; // Determine types let num_fields = arguments.len() + 1; @@ -1543,23 +1547,13 @@ pub fn build_tag<'a, 'ctx, 'env>( reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[fields]); let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } + struct_pointer_from_fields( + env, + struct_type, + data_ptr, + field_vals.into_iter().enumerate(), + ); data_ptr.into() } @@ -1583,7 +1577,6 @@ pub fn build_tag<'a, 'ctx, 'env>( debug_assert!(union_size == 2); let ctx = env.context; - let builder = env.builder; // Determine types let num_fields = arguments.len() + 1; @@ -1627,23 +1620,13 @@ pub fn build_tag<'a, 'ctx, 'env>( allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]); let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let struct_ptr = env - .builder - .build_bitcast( - data_ptr, - struct_type.ptr_type(AddressSpace::Generic), - "block_of_memory_to_tag", - ) - .into_pointer_value(); - // Insert field exprs into struct_val - for (index, field_val) in field_vals.into_iter().enumerate() { - let field_ptr = builder - .build_struct_gep(struct_ptr, index as u32, "struct_gep") - .unwrap(); - - builder.build_store(field_ptr, field_val); - } + struct_pointer_from_fields( + env, + struct_type, + data_ptr, + field_vals.into_iter().enumerate(), + ); data_ptr.into() } From 89fd4c307409c8fc3e48c582361d71a576b72e0e Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 19 Jul 2021 20:37:39 +0200 Subject: [PATCH 58/61] test for malformed header --- compiler/reporting/src/error/parse.rs | 28 ++++++++++++++++ compiler/reporting/tests/test_reporting.rs | 37 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 646f675580..2cc53ece7a 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -3263,6 +3263,34 @@ fn to_requires_report<'a>( } } + ERequires::Rigid(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, row, col); + let region = Region::from_row_col(row, col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("I am expecting a list of rigids like "), + alloc.keyword("{}"), + alloc.reflow(" or "), + alloc.keyword("{model=>Model}"), + alloc.reflow(" next. A full "), + alloc.keyword("requires"), + alloc.reflow(" definition looks like"), + ]), + alloc + .parser_suggestion("requires {model=>Model, msg=>Msg} {main : Effect {}} ") + .indent(4), + ]); + + Report { + filename, + doc, + title: "BAD RIGIDS".to_string(), + } + } + _ => todo!("unhandled parse error {:?}", parse_problem), } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 56b309a30a..0d99596a83 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5929,6 +5929,43 @@ mod test_reporting { ) } + #[test] + fn platform_requires_rigids() { + report_header_problem_as( + indoc!( + r#" + platform folkertdev/foo + requires { main : Effect {} } + exposes [] + packages {} + imports [Task] + provides [ mainForHost ] + effects fx.Effect + { + putChar : I64 -> Effect {}, + putLine : Str -> Effect {}, + getLine : Effect Str + } + "# + ), + indoc!( + r#" + ── WEIRD PROVIDES ────────────────────────────────────────────────────────────── + + I am partway through parsing a provides list, but I got stuck here: + + 3│ imports [base.Task, Base64 ] + 4│ provides [ main, @Foo ] to base + ^ + + I was expecting a type name, value name or function name next, like + + provides [ Animal, default, tame ] + "# + ), + ) + } + #[test] fn exposes_identifier() { report_header_problem_as( From 8eb0c6fbb8ead4bafe4685741685a08aee5adff2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 19 Jul 2021 22:19:55 +0200 Subject: [PATCH 59/61] fix example --- cli/tests/cli_run.rs | 13 ++ compiler/module/src/symbol.rs | 1 + compiler/mono/src/inc_dec.rs | 9 - compiler/reporting/src/error/parse.rs | 6 +- compiler/reporting/tests/test_reporting.rs | 15 +- examples/effect/thing/platform-dir/Cargo.lock | 23 --- examples/effect/thing/platform-dir/Cargo.toml | 14 -- .../thing/platform-dir/Package-Config.roc | 8 +- examples/effect/thing/platform-dir/Task.roc | 47 +++-- examples/effect/thing/platform-dir/host.c | 7 - examples/effect/thing/platform-dir/host.zig | 184 ++++++++++++++++++ examples/effect/thing/platform-dir/src/lib.rs | 158 --------------- 12 files changed, 239 insertions(+), 246 deletions(-) delete mode 100644 examples/effect/thing/platform-dir/Cargo.lock delete mode 100644 examples/effect/thing/platform-dir/Cargo.toml delete mode 100644 examples/effect/thing/platform-dir/host.c create mode 100644 examples/effect/thing/platform-dir/host.zig delete mode 100644 examples/effect/thing/platform-dir/src/lib.rs diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 19c50a08f8..f89a64d8c3 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -332,4 +332,17 @@ mod cli_run { true, ); } + + #[test] + #[serial(effect)] + fn run_effect() { + check_output_with_stdin( + &example_file("effect", "Main.roc"), + "hello world how are you", + "effect-example", + &[], + "hello world how are you\n", + true, + ); + } } diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index d107bdffe4..86b0b3e20c 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -438,6 +438,7 @@ pub struct ModuleIds { impl ModuleIds { pub fn get_or_insert(&mut self, module_name: &InlinableString) -> ModuleId { + dbg!(module_name); match self.by_name.get(module_name) { Some(id) => *id, None => { diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 9954707853..4cbc8b5bec 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -968,15 +968,6 @@ impl<'a> Context<'a> { // live vars of the whole expression let invoke_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default()); - // the result of an invoke should not be touched in the fail branch - // but it should be present in the pass branch (otherwise it would be dead) - // NOTE: we cheat a bit here to allow `invoke` when generating code for `expect` - let is_dead = !invoke_live_vars.contains(symbol); - - if is_dead && layout.is_refcounted() { - panic!("A variable of a reference-counted layout is dead; that's a bug!"); - } - let fail = { // TODO should we use ctor info like Lean? let ctx = self.clone(); diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 2cc53ece7a..bf9da32395 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -3239,7 +3239,6 @@ fn to_requires_report<'a>( ERequires::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col), ERequires::ListStart(row, col) => { - dbg!(row, col); let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let region = Region::from_row_col(row, col); @@ -3280,14 +3279,14 @@ fn to_requires_report<'a>( alloc.reflow(" definition looks like"), ]), alloc - .parser_suggestion("requires {model=>Model, msg=>Msg} {main : Effect {}} ") + .parser_suggestion("requires {model=>Model, msg=>Msg} {main : Effect {}}") .indent(4), ]); Report { filename, doc, - title: "BAD RIGIDS".to_string(), + title: "BAD REQUIRES RIGIDS".to_string(), } } @@ -3396,6 +3395,7 @@ fn to_space_report<'a>( title: "TAB CHARACTER".to_string(), } } + _ => todo!("unhandled type parse error: {:?}", &parse_problem), } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 0d99596a83..d3c7ce42d7 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5950,17 +5950,18 @@ mod test_reporting { ), indoc!( r#" - ── WEIRD PROVIDES ────────────────────────────────────────────────────────────── + ── BAD REQUIRES RIGIDS ───────────────────────────────────────────────────────── - I am partway through parsing a provides list, but I got stuck here: + I am partway through parsing a header, but I got stuck here: - 3│ imports [base.Task, Base64 ] - 4│ provides [ main, @Foo ] to base - ^ + 1│ platform folkertdev/foo + 2│ requires { main : Effect {} } + ^ - I was expecting a type name, value name or function name next, like + I am expecting a list of rigids like `{}` or `{model=>Model}` next. A full + `requires` definition looks like - provides [ Animal, default, tame ] + requires {model=>Model, msg=>Msg} {main : Effect {}} "# ), ) diff --git a/examples/effect/thing/platform-dir/Cargo.lock b/examples/effect/thing/platform-dir/Cargo.lock deleted file mode 100644 index c386bb6c4a..0000000000 --- a/examples/effect/thing/platform-dir/Cargo.lock +++ /dev/null @@ -1,23 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "host" -version = "0.1.0" -dependencies = [ - "roc_std 0.1.0", -] - -[[package]] -name = "libc" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "roc_std" -version = "0.1.0" -dependencies = [ - "libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" diff --git a/examples/effect/thing/platform-dir/Cargo.toml b/examples/effect/thing/platform-dir/Cargo.toml deleted file mode 100644 index 9571ddc430..0000000000 --- a/examples/effect/thing/platform-dir/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "host" -version = "0.1.0" -authors = ["The Roc Contributors"] -license = "UPL-1.0" -edition = "2018" - -[lib] -crate-type = ["staticlib"] - -[dependencies] -roc_std = { path = "../../../../roc_std" } - -[workspace] diff --git a/examples/effect/thing/platform-dir/Package-Config.roc b/examples/effect/thing/platform-dir/Package-Config.roc index a9e40a3ddf..df4a415ae0 100644 --- a/examples/effect/thing/platform-dir/Package-Config.roc +++ b/examples/effect/thing/platform-dir/Package-Config.roc @@ -1,16 +1,16 @@ platform folkertdev/foo - requires { main : Effect {} } + requires {model=>Model, msg=>Msg} {main : Effect {}} exposes [] packages {} - imports [Task] + imports [ Task.{ Task } ] provides [ mainForHost ] effects fx.Effect { - putChar : I64 -> Effect {}, putLine : Str -> Effect {}, getLine : Effect Str } -mainForHost : Task.Task {} [] as Fx + +mainForHost : Task {} [] as Fx mainForHost = main diff --git a/examples/effect/thing/platform-dir/Task.roc b/examples/effect/thing/platform-dir/Task.roc index 85a7f927c2..1c561e47f9 100644 --- a/examples/effect/thing/platform-dir/Task.roc +++ b/examples/effect/thing/platform-dir/Task.roc @@ -1,31 +1,36 @@ interface Task - exposes [ Task, after, always, fail, map, putLine, getLine ] - imports [ Effect ] + exposes [ Task, succeed, fail, after, map, putLine, getLine ] + imports [ fx.Effect ] -Task a err : Effect.Effect (Result a err) -always : a -> Task a * -always = \x -> Effect.always (Ok x) +Task ok err : Effect.Effect (Result ok err) + + +succeed : val -> Task val * +succeed = \val -> + Effect.always (Ok val) + fail : err -> Task * err -fail = \x -> Effect.always (Err x) +fail = \val -> + Effect.always (Err val) -getLine : Task Str * -getLine = Effect.after Effect.getLine always +after : Task a err, (a -> Task b err) -> Task b err +after = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> transform a + Err err -> Task.fail err + +map : Task a err, (a -> b) -> Task b err +map = \effect, transform -> + Effect.map effect \result -> + when result is + Ok a -> Ok (transform a) + Err err -> Err err putLine : Str -> Task {} * putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) -map : Task a err, (a -> b) -> Task b err -map = \task, transform -> - Effect.map task \res -> - when res is - Ok x -> Ok (transform x) - Err e -> Err e - -after : Task a err, (a -> Task b err) -> Task b err -after = \task, transform -> - Effect.after task \res -> - when res is - Ok x -> transform x - Err e -> Task.fail e +getLine : Task Str * +getLine = Effect.map Effect.getLine (\x -> Ok x) diff --git a/examples/effect/thing/platform-dir/host.c b/examples/effect/thing/platform-dir/host.c deleted file mode 100644 index 0378c69589..0000000000 --- a/examples/effect/thing/platform-dir/host.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -extern int rust_main(); - -int main() { - return rust_main(); -} diff --git a/examples/effect/thing/platform-dir/host.zig b/examples/effect/thing/platform-dir/host.zig new file mode 100644 index 0000000000..307bd69a73 --- /dev/null +++ b/examples/effect/thing/platform-dir/host.zig @@ -0,0 +1,184 @@ +const std = @import("std"); +const str = @import("str"); +const RocStr = str.RocStr; +const testing = std.testing; +const expectEqual = testing.expectEqual; +const expect = testing.expect; +const maxInt = std.math.maxInt; + +comptime { + // This is a workaround for https://github.com/ziglang/zig/issues/8218 + // which is only necessary on macOS. + // + // Once that issue is fixed, we can undo the changes in + // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing + // -fcompiler-rt in link.rs instead of doing this. Note that this + // workaround is present in many host.zig files, so make sure to undo + // it everywhere! + if (std.builtin.os.tag == .macos) { + _ = @import("compiler_rt"); + } +} + +const mem = std.mem; +const Allocator = mem.Allocator; + +extern fn roc__mainForHost_1_exposed([*]u8) void; +extern fn roc__mainForHost_size() i64; +extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void; +extern fn roc__mainForHost_1_Fx_size() i64; +extern fn roc__mainForHost_1_Fx_result_size() i64; + +extern fn malloc(size: usize) callconv(.C) ?*c_void; +extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void; +extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void; + +export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { + return malloc(size); +} + +export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void { + return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size); +} + +export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { + free(@alignCast(16, @ptrCast([*]u8, c_ptr))); +} + +const Unit = extern struct {}; + +pub export fn main() u8 { + const stdout = std.io.getStdOut().writer(); + const stderr = std.io.getStdErr().writer(); + + const size = @intCast(usize, roc__mainForHost_size()); + const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable; + var output = @ptrCast([*]u8, raw_output); + + defer { + std.heap.c_allocator.free(raw_output); + } + + var ts1: std.os.timespec = undefined; + std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts1) catch unreachable; + + roc__mainForHost_1_exposed(output); + + const elements = @ptrCast([*]u64, @alignCast(8, output)); + + var flag = elements[0]; + + if (flag == 0) { + // all is well + const closure_data_pointer = @ptrCast([*]u8, output[8..size]); + + call_the_closure(closure_data_pointer); + } else { + const msg = @intToPtr([*:0]const u8, elements[1]); + stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; + + return 0; + } + + var ts2: std.os.timespec = undefined; + std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable; + + const delta = to_seconds(ts2) - to_seconds(ts1); + + stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable; + + return 0; +} + +fn to_seconds(tms: std.os.timespec) f64 { + return @intToFloat(f64, tms.tv_sec) + (@intToFloat(f64, tms.tv_nsec) / 1_000_000_000.0); +} + +fn call_the_closure(closure_data_pointer: [*]u8) void { + const size = roc__mainForHost_1_Fx_result_size(); + const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable; + var output = @ptrCast([*]u8, raw_output); + + defer { + std.heap.c_allocator.free(raw_output); + } + + const flags: u8 = 0; + + roc__mainForHost_1_Fx_caller(&flags, closure_data_pointer, output); + + const elements = @ptrCast([*]u64, @alignCast(8, output)); + + var flag = elements[0]; + + if (flag == 0) { + return; + } else { + unreachable; + } +} + +pub export fn roc_fx_getLine() str.RocStr { + if (roc_fx_getLine_help()) |value| { + return value; + } else |err| { + return str.RocStr.empty(); + } +} + +fn roc_fx_getLine_help() !RocStr { + const stdin = std.io.getStdIn().reader(); + var buf: [400]u8 = undefined; + + const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse ""; + + return str.RocStr.init(@ptrCast([*]const u8, line), line.len); +} + +pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 { + const stdout = std.io.getStdOut().writer(); + + for (rocPath.asSlice()) |char| { + stdout.print("{c}", .{char}) catch unreachable; + } + + stdout.print("\n", .{}) catch unreachable; + + return 0; +} + +const GetInt = extern struct { + value: i64, + error_code: u8, + is_error: bool, +}; + +pub export fn roc_fx_getInt() GetInt { + if (roc_fx_getInt_help()) |value| { + const get_int = GetInt{ .is_error = false, .value = value, .error_code = 0 }; + return get_int; + } else |err| switch (err) { + error.InvalidCharacter => { + return GetInt{ .is_error = true, .value = 0, .error_code = 0 }; + }, + else => { + return GetInt{ .is_error = true, .value = 0, .error_code = 1 }; + }, + } + + return 0; +} + +fn roc_fx_getInt_help() !i64 { + const stdin = std.io.getStdIn().reader(); + var buf: [40]u8 = undefined; + + const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse ""; + + return std.fmt.parseInt(i64, line, 10); +} + +fn readLine() []u8 { + const stdin = std.io.getStdIn().reader(); + return (stdin.readUntilDelimiterOrEof(&line_buf, '\n') catch unreachable) orelse ""; +} diff --git a/examples/effect/thing/platform-dir/src/lib.rs b/examples/effect/thing/platform-dir/src/lib.rs deleted file mode 100644 index 2fe8147386..0000000000 --- a/examples/effect/thing/platform-dir/src/lib.rs +++ /dev/null @@ -1,158 +0,0 @@ -#![allow(non_snake_case)] - -use roc_std::alloca; -use roc_std::RocCallResult; -use roc_std::RocStr; -use std::alloc::Layout; -use std::ffi::c_void; -use std::time::SystemTime; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed"] - fn roc_main(output: *mut u8) -> (); - - #[link_name = "roc__mainForHost_1_size"] - fn roc_main_size() -> i64; - - #[link_name = "roc__mainForHost_1_Fx_caller"] - fn call_Fx( - flags: &(), - function_pointer: *const u8, - closure_data: *const u8, - output: *mut u8, - ) -> (); - - #[link_name = "roc__mainForHost_1_Fx_size"] - fn size_Fx() -> i64; - - #[link_name = "roc__mainForHost_1_Fx_result_size"] - fn size_Fx_result() -> i64; - - fn malloc(size: usize) -> *mut c_void; - fn realloc(c_ptr: *mut c_void, size: usize) -> *mut c_void; - fn free(c_ptr: *mut c_void); -} - -#[no_mangle] -pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { - return malloc(size); -} - -#[no_mangle] -pub unsafe fn roc_realloc( - c_ptr: *mut c_void, - new_size: usize, - _old_size: usize, - _alignment: u32, -) -> *mut c_void { - return realloc(c_ptr, new_size); -} - -#[no_mangle] -pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { - return free(c_ptr); -} - -#[no_mangle] -pub fn roc_fx_putChar(foo: i64) -> () { - let character = foo as u8 as char; - print!("{}", character); - - () -} - -#[no_mangle] -pub fn roc_fx_putLine(line: RocStr) -> () { - let bytes = line.as_slice(); - let string = unsafe { std::str::from_utf8_unchecked(bytes) }; - println!("{}", string); - - () -} - -#[no_mangle] -pub fn roc_fx_getLine() -> RocStr { - use std::io::{self, BufRead}; - - let stdin = io::stdin(); - let line1 = stdin.lock().lines().next().unwrap().unwrap(); - - RocStr::from_slice(line1.as_bytes()) -} - -unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 { - let size = size_Fx_result() as usize; - - alloca::with_stack_bytes(size, |buffer| { - let buffer: *mut std::ffi::c_void = buffer; - let buffer: *mut u8 = buffer as *mut u8; - - call_Fx( - &(), - function_pointer, - closure_data_ptr as *const u8, - buffer as *mut u8, - ); - - let output = &*(buffer as *mut RocCallResult<()>); - - match output.into() { - Ok(_) => 0, - Err(e) => panic!("failed with {}", e), - } - }) -} - -#[no_mangle] -pub fn rust_main() -> isize { - eprintln!("Running Roc closure"); - let start_time = SystemTime::now(); - - let size = unsafe { roc_main_size() } as usize; - let layout = Layout::array::(size).unwrap(); - let answer = unsafe { - let buffer = std::alloc::alloc(layout); - - roc_main(buffer); - - let output = &*(buffer as *mut RocCallResult<()>); - - match output.into() { - Ok(()) => { - let function_pointer = { - // this is a pointer to the location where the function pointer is stored - // we pass just the function pointer - let temp = buffer.offset(8) as *const i64; - - (*temp) as *const u8 - }; - - let closure_data_ptr = buffer.offset(16); - - let result = - call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8); - - std::alloc::dealloc(buffer, layout); - - result - } - Err(msg) => { - std::alloc::dealloc(buffer, layout); - - panic!("Roc failed with message: {}", msg); - } - } - }; - let end_time = SystemTime::now(); - let duration = end_time.duration_since(start_time).unwrap(); - - eprintln!( - "Roc closure took {:.4} ms to compute this answer: {:?}", - duration.as_secs_f64() * 1000.0, - // truncate the answer, so stdout is not swamped - answer - ); - - // Exit code - 0 -} From b70ecf6351868ca681108e4d49620254f1c9a3c7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 19 Jul 2021 22:28:19 +0200 Subject: [PATCH 60/61] make Effect simpler --- examples/effect/Main.roc | 6 ++-- .../thing/platform-dir/Package-Config.roc | 4 +-- examples/effect/thing/platform-dir/Task.roc | 36 ------------------- 3 files changed, 5 insertions(+), 41 deletions(-) delete mode 100644 examples/effect/thing/platform-dir/Task.roc diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index 9b10bb3bc8..9e3dd5f506 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -1,8 +1,8 @@ app "effect-example" packages { base: "thing/platform-dir" } - imports [base.Task] + imports [fx.Effect] provides [ main ] to base -main : Task.Task {} [] +main : Effect.Effect {} main = - Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing + Effect.after Effect.getLine \lineThisThing -> Effect.putLine lineThisThing diff --git a/examples/effect/thing/platform-dir/Package-Config.roc b/examples/effect/thing/platform-dir/Package-Config.roc index df4a415ae0..5e9516eeba 100644 --- a/examples/effect/thing/platform-dir/Package-Config.roc +++ b/examples/effect/thing/platform-dir/Package-Config.roc @@ -2,7 +2,7 @@ platform folkertdev/foo requires {model=>Model, msg=>Msg} {main : Effect {}} exposes [] packages {} - imports [ Task.{ Task } ] + imports [fx.Effect] provides [ mainForHost ] effects fx.Effect { @@ -12,5 +12,5 @@ platform folkertdev/foo -mainForHost : Task {} [] as Fx +mainForHost : Effect.Effect {} as Fx mainForHost = main diff --git a/examples/effect/thing/platform-dir/Task.roc b/examples/effect/thing/platform-dir/Task.roc deleted file mode 100644 index 1c561e47f9..0000000000 --- a/examples/effect/thing/platform-dir/Task.roc +++ /dev/null @@ -1,36 +0,0 @@ -interface Task - exposes [ Task, succeed, fail, after, map, putLine, getLine ] - imports [ fx.Effect ] - - -Task ok err : Effect.Effect (Result ok err) - - -succeed : val -> Task val * -succeed = \val -> - Effect.always (Ok val) - - -fail : err -> Task * err -fail = \val -> - Effect.always (Err val) - -after : Task a err, (a -> Task b err) -> Task b err -after = \effect, transform -> - Effect.after effect \result -> - when result is - Ok a -> transform a - Err err -> Task.fail err - -map : Task a err, (a -> b) -> Task b err -map = \effect, transform -> - Effect.map effect \result -> - when result is - Ok a -> Ok (transform a) - Err err -> Err err - -putLine : Str -> Task {} * -putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) - -getLine : Task Str * -getLine = Effect.map Effect.getLine (\x -> Ok x) From 6c44714b2aa18b8cde4f4b0beb379e012302869f Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 19 Jul 2021 20:51:01 -0400 Subject: [PATCH 61/61] Drop obsolete dbg! --- compiler/module/src/symbol.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 86b0b3e20c..d107bdffe4 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -438,7 +438,6 @@ pub struct ModuleIds { impl ModuleIds { pub fn get_or_insert(&mut self, module_name: &InlinableString) -> ModuleId { - dbg!(module_name); match self.by_name.get(module_name) { Some(id) => *id, None => {