mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge branch 'resetref' into Frame-Limited
This commit is contained in:
commit
87abe8df7d
13 changed files with 387 additions and 16 deletions
|
@ -31,6 +31,7 @@ pub enum HelperOp {
|
|||
Dec,
|
||||
DecRef(JoinPointId),
|
||||
Reset,
|
||||
ResetRef,
|
||||
Eq,
|
||||
}
|
||||
|
||||
|
@ -183,6 +184,38 @@ impl<'a> CodeGenHelp<'a> {
|
|||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
// TODO update to not decrement children.
|
||||
pub fn call_resetref_refcount(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
argument: Symbol,
|
||||
) -> (Expr<'a>, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::Reset,
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout);
|
||||
|
||||
let arguments = self.arena.alloc([argument]);
|
||||
let ret_layout = layout;
|
||||
let arg_layouts = self.arena.alloc([layout]);
|
||||
let expr = Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
name: LambdaName::no_niche(proc_name),
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments,
|
||||
});
|
||||
|
||||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
/// Generate a refcount increment procedure, *without* a Call expression.
|
||||
/// *This method should be rarely used* - only when the proc is to be called from Zig.
|
||||
/// Otherwise you want to generate the Proc and the Call together, using another method.
|
||||
|
@ -262,7 +295,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let arg = self.replace_rec_ptr(ctx, layout_interner, layout);
|
||||
match ctx.op {
|
||||
Dec | DecRef(_) => (LAYOUT_UNIT, self.arena.alloc([arg])),
|
||||
Reset => (layout, self.arena.alloc([layout])),
|
||||
Reset | ResetRef => (layout, self.arena.alloc([layout])),
|
||||
Inc => (LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])),
|
||||
Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])),
|
||||
}
|
||||
|
@ -347,6 +380,17 @@ impl<'a> CodeGenHelp<'a> {
|
|||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
ResetRef => (
|
||||
layout,
|
||||
refcount::refcount_resetref_proc_body(
|
||||
self,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
Symbol::ARG_1,
|
||||
),
|
||||
),
|
||||
Eq => (
|
||||
LAYOUT_BOOL,
|
||||
equality::eq_generic(self, ident_ids, ctx, layout_interner, layout),
|
||||
|
@ -360,7 +404,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let inc_amount = (self.layout_isize, ARG_2);
|
||||
self.arena.alloc([roc_value, inc_amount])
|
||||
}
|
||||
Dec | DecRef(_) | Reset => self.arena.alloc([roc_value]),
|
||||
Dec | DecRef(_) | Reset | ResetRef => self.arena.alloc([roc_value]),
|
||||
Eq => self.arena.alloc([roc_value, (layout, ARG_2)]),
|
||||
}
|
||||
};
|
||||
|
@ -411,6 +455,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
niche: Niche::NONE,
|
||||
},
|
||||
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
|
||||
HelperOp::ResetRef => unreachable!("No generated Proc for ResetRef"),
|
||||
HelperOp::Eq => ProcLayout {
|
||||
arguments: self.arena.alloc([layout, layout]),
|
||||
result: LAYOUT_BOOL,
|
||||
|
|
|
@ -413,6 +413,206 @@ pub fn refcount_reset_proc_body<'a>(
|
|||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
pub fn refcount_resetref_proc_body<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
structure: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let rc = root.create_symbol(ident_ids, "rc");
|
||||
let refcount_1 = root.create_symbol(ident_ids, "refcount_1");
|
||||
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
||||
let addr = root.create_symbol(ident_ids, "addr");
|
||||
|
||||
let union_layout = match layout_interner.get(layout) {
|
||||
Layout::Union(u) => u,
|
||||
_ => unimplemented!("Reset is only implemented for UnionLayout"),
|
||||
};
|
||||
|
||||
// Whenever we recurse into a child layout we will want to Decrement
|
||||
ctx.op = HelperOp::Dec;
|
||||
ctx.recursive_union = Some(union_layout);
|
||||
let recursion_ptr = layout_interner.insert(Layout::RecursivePointer(layout));
|
||||
|
||||
// Reset structure is unique. Return a pointer to the allocation.
|
||||
let then_stmt = {
|
||||
let alignment = root.create_symbol(ident_ids, "alignment");
|
||||
let alignment_expr = Expr::Literal(Literal::Int(
|
||||
(layout_interner
|
||||
.get(layout)
|
||||
.alignment_bytes(layout_interner, root.target_info) as i128)
|
||||
.to_ne_bytes(),
|
||||
));
|
||||
let alloc_addr = root.create_symbol(ident_ids, "alloc_addr");
|
||||
let alloc_addr_expr = Expr::Call(Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::NumSubWrap,
|
||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||
},
|
||||
arguments: root.arena.alloc([addr, alignment]),
|
||||
});
|
||||
|
||||
Stmt::Let(
|
||||
alignment,
|
||||
alignment_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
alloc_addr,
|
||||
alloc_addr_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(alloc_addr),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
// Reset structure is not unique. Decrement it and return a NULL pointer.
|
||||
let else_stmt = {
|
||||
let decrement_unit = root.create_symbol(ident_ids, "decrement_unit");
|
||||
let decrement_expr = root
|
||||
.call_specialized_op(
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
root.arena.alloc([structure]),
|
||||
)
|
||||
.unwrap();
|
||||
let decrement_stmt = |next| Stmt::Let(decrement_unit, decrement_expr, LAYOUT_UNIT, next);
|
||||
|
||||
// Zero
|
||||
let zero = root.create_symbol(ident_ids, "zero");
|
||||
let zero_expr = Expr::Literal(Literal::Int(0i128.to_ne_bytes()));
|
||||
let zero_stmt = |next| Stmt::Let(zero, zero_expr, root.layout_isize, next);
|
||||
|
||||
// Null pointer with union layout
|
||||
let null = root.create_symbol(ident_ids, "null");
|
||||
let null_stmt =
|
||||
|next| let_lowlevel(root.arena, root.layout_isize, null, PtrCast, &[zero], next);
|
||||
|
||||
decrement_stmt(root.arena.alloc(
|
||||
//
|
||||
zero_stmt(root.arena.alloc(
|
||||
//
|
||||
null_stmt(root.arena.alloc(
|
||||
//
|
||||
Stmt::Ret(null),
|
||||
)),
|
||||
)),
|
||||
))
|
||||
};
|
||||
|
||||
let if_stmt = Stmt::Switch {
|
||||
cond_symbol: is_unique,
|
||||
cond_layout: LAYOUT_BOOL,
|
||||
branches: root.arena.alloc([(1, BranchInfo::None, then_stmt)]),
|
||||
default_branch: (BranchInfo::None, root.arena.alloc(else_stmt)),
|
||||
ret_layout: layout,
|
||||
};
|
||||
|
||||
// Uniqueness test
|
||||
let is_unique_stmt = {
|
||||
let_lowlevel(
|
||||
root.arena,
|
||||
LAYOUT_BOOL,
|
||||
is_unique,
|
||||
Eq,
|
||||
&[rc, refcount_1],
|
||||
root.arena.alloc(if_stmt),
|
||||
)
|
||||
};
|
||||
|
||||
// Constant for unique refcount
|
||||
let refcount_1_encoded = match root.target_info.ptr_width() {
|
||||
PtrWidth::Bytes4 => i32::MIN as i128,
|
||||
PtrWidth::Bytes8 => i64::MIN as i128,
|
||||
}
|
||||
.to_ne_bytes();
|
||||
let refcount_1_expr = Expr::Literal(Literal::Int(refcount_1_encoded));
|
||||
let refcount_1_stmt = Stmt::Let(
|
||||
refcount_1,
|
||||
refcount_1_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(is_unique_stmt),
|
||||
);
|
||||
|
||||
// Refcount value
|
||||
let rc_expr = Expr::UnionAtIndex {
|
||||
structure: rc_ptr,
|
||||
tag_id: 0,
|
||||
union_layout: root.union_refcount,
|
||||
index: 0,
|
||||
};
|
||||
let rc_stmt = Stmt::Let(
|
||||
rc,
|
||||
rc_expr,
|
||||
root.layout_isize,
|
||||
root.arena.alloc(refcount_1_stmt),
|
||||
);
|
||||
|
||||
// Refcount pointer
|
||||
let rc_ptr_stmt = {
|
||||
rc_ptr_from_data_ptr_help(
|
||||
root,
|
||||
ident_ids,
|
||||
structure,
|
||||
rc_ptr,
|
||||
union_layout.stores_tag_id_in_pointer(root.target_info),
|
||||
root.arena.alloc(rc_stmt),
|
||||
addr,
|
||||
recursion_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
rc_ptr_stmt
|
||||
}
|
||||
|
||||
// Check if refcounting is implemented yet. In the long term, this will be deleted.
|
||||
// In the short term, it helps us to skip refcounting and let it leak, so we can make
|
||||
// progress incrementally. Kept in sync with generate_procs using assertions.
|
||||
pub fn is_rc_implemented_yet<'a, I>(interner: &I, layout: InLayout<'a>) -> bool
|
||||
where
|
||||
I: LayoutInterner<'a>,
|
||||
{
|
||||
use UnionLayout::*;
|
||||
|
||||
match interner.get(layout) {
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => is_rc_implemented_yet(interner, elem_layout),
|
||||
Layout::Builtin(_) => true,
|
||||
Layout::Struct { field_layouts, .. } => field_layouts
|
||||
.iter()
|
||||
.all(|l| is_rc_implemented_yet(interner, *l)),
|
||||
Layout::Union(union_layout) => match union_layout {
|
||||
NonRecursive(tags) => tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
Recursive(tags) => tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
NonNullableUnwrapped(fields) => {
|
||||
fields.iter().all(|l| is_rc_implemented_yet(interner, *l))
|
||||
}
|
||||
NullableWrapped { other_tags, .. } => other_tags
|
||||
.iter()
|
||||
.all(|fields| fields.iter().all(|l| is_rc_implemented_yet(interner, *l))),
|
||||
NullableUnwrapped { other_fields, .. } => other_fields
|
||||
.iter()
|
||||
.all(|l| is_rc_implemented_yet(interner, *l)),
|
||||
},
|
||||
Layout::LambdaSet(lambda_set) => is_rc_implemented_yet(interner, lambda_set.representation),
|
||||
Layout::RecursivePointer(_) => true,
|
||||
Layout::Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn rc_return_stmt<'a>(
|
||||
root: &CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue