diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 1b154c6194..7e287298d8 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -41,6 +41,10 @@ impl HelperOp { fn is_decref(&self) -> bool { matches!(self, Self::DecRef(_)) } + + fn is_dec(&self) -> bool { + matches!(self, Self::Dec) + } } #[derive(Debug)] @@ -81,7 +85,6 @@ pub struct CodeGenHelp<'a> { home: ModuleId, target_info: TargetInfo, layout_isize: InLayout<'a>, - union_refcount: UnionLayout<'a>, specializations: Vec<'a, Specialization<'a>>, debug_recursion_depth: usize, } @@ -90,15 +93,11 @@ impl<'a> CodeGenHelp<'a> { pub fn new(arena: &'a Bump, target_info: TargetInfo, home: ModuleId) -> Self { let layout_isize = Layout::isize(target_info); - // Refcount is a boxed isize. TODO: use the new Box layout when dev backends support it - let union_refcount = UnionLayout::NonNullableUnwrapped(arena.alloc([layout_isize])); - CodeGenHelp { arena, home, target_info, layout_isize, - union_refcount, specializations: Vec::with_capacity_in(16, arena), debug_recursion_depth: 0, } diff --git a/crates/compiler/mono/src/code_gen_help/refcount.rs b/crates/compiler/mono/src/code_gen_help/refcount.rs index 1bac15153e..21140ec675 100644 --- a/crates/compiler/mono/src/code_gen_help/refcount.rs +++ b/crates/compiler/mono/src/code_gen_help/refcount.rs @@ -186,6 +186,56 @@ pub fn refcount_generic<'a>( } } +fn if_unique<'a>( + root: &mut CodeGenHelp<'a>, + ident_ids: &mut IdentIds, + value: Symbol, + when_unique: impl FnOnce(JoinPointId) -> Stmt<'a>, + when_done: Stmt<'a>, +) -> Stmt<'a> { + // joinpoint f = + // + // in + // if is_unique then + // (f) + // else + // jump f + + let joinpoint = root.create_symbol(ident_ids, "is_unique_joinpoint"); + let joinpoint = JoinPointId(joinpoint); + + let is_unique = root.create_symbol(ident_ids, "is_unique"); + + let mut stmt = Stmt::Switch { + cond_symbol: is_unique, + cond_layout: Layout::BOOL, + branches: root + .arena + .alloc([(true as _, BranchInfo::None, when_unique(joinpoint))]), + default_branch: ( + BranchInfo::None, + root.arena.alloc(Stmt::Jump(joinpoint, &[])), + ), + ret_layout: Layout::UNIT, + }; + + stmt = Stmt::Join { + id: joinpoint, + parameters: &[], + body: root.arena.alloc(when_done), + remainder: root.arena.alloc(stmt), + }; + + let_lowlevel( + root.arena, + root.layout_isize, + is_unique, + LowLevel::RefCountIsUnique, + &[value], + root.arena.alloc(stmt), + ) +} + pub fn refcount_reset_proc_body<'a>( root: &mut CodeGenHelp<'a>, ident_ids: &mut IdentIds, @@ -348,12 +398,8 @@ pub fn refcount_reset_proc_body<'a>( ); // Refcount value - let rc_expr = Expr::UnionAtIndex { - structure: rc_ptr, - tag_id: 0, - union_layout: root.union_refcount, - index: 0, - }; + let rc_expr = Expr::ExprUnbox { symbol: rc_ptr }; + let rc_stmt = Stmt::Let( rc, rc_expr, @@ -361,6 +407,9 @@ pub fn refcount_reset_proc_body<'a>( root.arena.alloc(refcount_1_stmt), ); + // a Box never masks bits + let mask_lower_bits = false; + // Refcount pointer let rc_ptr_stmt = { rc_ptr_from_data_ptr_help( @@ -368,7 +417,7 @@ pub fn refcount_reset_proc_body<'a>( ident_ids, structure, rc_ptr, - union_layout.stores_tag_id_in_pointer(root.target_info), + mask_lower_bits, root.arena.alloc(rc_stmt), addr, recursion_ptr, @@ -508,12 +557,8 @@ pub fn refcount_resetref_proc_body<'a>( ); // Refcount value - let rc_expr = Expr::UnionAtIndex { - structure: rc_ptr, - tag_id: 0, - union_layout: root.union_refcount, - index: 0, - }; + let rc_expr = Expr::ExprUnbox { symbol: rc_ptr }; + let rc_stmt = Stmt::Let( rc, rc_expr, @@ -521,6 +566,9 @@ pub fn refcount_resetref_proc_body<'a>( root.arena.alloc(refcount_1_stmt), ); + // a Box never masks bits + let mask_lower_bits = false; + // Refcount pointer let rc_ptr_stmt = { rc_ptr_from_data_ptr_help( @@ -528,7 +576,7 @@ pub fn refcount_resetref_proc_body<'a>( ident_ids, structure, rc_ptr, - union_layout.stores_tag_id_in_pointer(root.target_info), + mask_lower_bits, root.arena.alloc(rc_stmt), addr, recursion_ptr, @@ -1813,7 +1861,8 @@ fn refcount_boxed<'a>( Layout::OPAQUE_PTR, ); - if layout_interner.is_refcounted(inner_layout) && !ctx.op.is_decref() { + // decrement the inner value if the operation is a decrement and the box itself is unique + if layout_interner.is_refcounted(inner_layout) && ctx.op.is_dec() { let inner = root.create_symbol(ident_ids, "inner"); let inner_expr = Expr::ExprUnbox { symbol: outer }; @@ -1829,16 +1878,24 @@ fn refcount_boxed<'a>( ) .unwrap(); - Stmt::Let( - inner, - inner_expr, - inner_layout, - arena.alloc(Stmt::Let( - mod_inner_unit, - mod_inner_expr, - LAYOUT_UNIT, - arena.alloc(get_rc_and_modify_outer), - )), + if_unique( + root, + ident_ids, + outer, + |id| { + Stmt::Let( + inner, + inner_expr, + inner_layout, + arena.alloc(Stmt::Let( + mod_inner_unit, + mod_inner_expr, + LAYOUT_UNIT, + arena.alloc(Stmt::Jump(id, &[])), + )), + ) + }, + get_rc_and_modify_outer, ) } else { get_rc_and_modify_outer