mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
wasm: Get RC reset/reuse working correctly
This commit is contained in:
parent
e71a6da62d
commit
aa0e9e8d31
3 changed files with 86 additions and 22 deletions
|
@ -1575,13 +1575,24 @@ impl<'a> WasmBackend<'a> {
|
||||||
StoredValue::Local { local_id, .. } => {
|
StoredValue::Local { local_id, .. } => {
|
||||||
// Tag is stored as a heap pointer.
|
// Tag is stored as a heap pointer.
|
||||||
if let Some(reused) = maybe_reused {
|
if let Some(reused) = maybe_reused {
|
||||||
// Reuse an existing heap allocation
|
// Reuse an existing heap allocation, if one is available (not NULL at runtime)
|
||||||
self.storage.load_symbols(&mut self.code_builder, &[reused]);
|
self.storage.load_symbols(&mut self.code_builder, &[reused]);
|
||||||
|
self.code_builder.if_();
|
||||||
|
{
|
||||||
|
self.storage.load_symbols(&mut self.code_builder, &[reused]);
|
||||||
|
self.code_builder.set_local(local_id);
|
||||||
|
}
|
||||||
|
self.code_builder.else_();
|
||||||
|
{
|
||||||
|
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||||
|
self.code_builder.set_local(local_id);
|
||||||
|
}
|
||||||
|
self.code_builder.end();
|
||||||
} else {
|
} else {
|
||||||
// Call the allocator to get a memory address.
|
// Call the allocator to get a memory address.
|
||||||
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||||
}
|
|
||||||
self.code_builder.set_local(local_id);
|
self.code_builder.set_local(local_id);
|
||||||
|
}
|
||||||
(local_id, 0)
|
(local_id, 0)
|
||||||
}
|
}
|
||||||
StoredValue::VirtualMachineStack { .. } => {
|
StoredValue::VirtualMachineStack { .. } => {
|
||||||
|
@ -1627,7 +1638,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.code_builder.i64_store(id_align, id_offset);
|
self.code_builder.i64_store(id_align, id_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if stores_tag_id_in_pointer {
|
} else if stores_tag_id_in_pointer && tag_id != 0 {
|
||||||
self.code_builder.get_local(local_id);
|
self.code_builder.get_local(local_id);
|
||||||
self.code_builder.i32_const(tag_id as i32);
|
self.code_builder.i32_const(tag_id as i32);
|
||||||
self.code_builder.i32_or();
|
self.code_builder.i32_or();
|
||||||
|
|
|
@ -144,6 +144,7 @@ pub fn refcount_reset_proc_body<'a>(
|
||||||
let rc = root.create_symbol(ident_ids, "rc");
|
let rc = root.create_symbol(ident_ids, "rc");
|
||||||
let refcount_1 = root.create_symbol(ident_ids, "refcount_1");
|
let refcount_1 = root.create_symbol(ident_ids, "refcount_1");
|
||||||
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
||||||
|
let masked = root.create_symbol(ident_ids, "masked");
|
||||||
|
|
||||||
let union_layout = match layout {
|
let union_layout = match layout {
|
||||||
Layout::Union(u) => u,
|
Layout::Union(u) => u,
|
||||||
|
@ -201,6 +202,39 @@ pub fn refcount_reset_proc_body<'a>(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let alloc_addr_stmt = {
|
||||||
|
let alignment = root.create_symbol(ident_ids, "alignment");
|
||||||
|
let alignment_expr = Expr::Literal(Literal::Int(
|
||||||
|
(layout.alignment_bytes(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([masked, 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let rc_contents_stmt = refcount_union_contents(
|
let rc_contents_stmt = refcount_union_contents(
|
||||||
root,
|
root,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
@ -211,7 +245,7 @@ pub fn refcount_reset_proc_body<'a>(
|
||||||
structure,
|
structure,
|
||||||
tag_id_sym,
|
tag_id_sym,
|
||||||
tag_id_layout,
|
tag_id_layout,
|
||||||
Stmt::Ret(structure),
|
alloc_addr_stmt,
|
||||||
);
|
);
|
||||||
|
|
||||||
tag_id_stmt(root.arena.alloc(
|
tag_id_stmt(root.arena.alloc(
|
||||||
|
@ -300,13 +334,14 @@ pub fn refcount_reset_proc_body<'a>(
|
||||||
|
|
||||||
// Refcount pointer
|
// Refcount pointer
|
||||||
let rc_ptr_stmt = {
|
let rc_ptr_stmt = {
|
||||||
rc_ptr_from_data_ptr(
|
rc_ptr_from_data_ptr_help(
|
||||||
root,
|
root,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
structure,
|
structure,
|
||||||
rc_ptr,
|
rc_ptr,
|
||||||
union_layout.stores_tag_id_in_pointer(root.target_info),
|
union_layout.stores_tag_id_in_pointer(root.target_info),
|
||||||
root.arena.alloc(rc_stmt),
|
root.arena.alloc(rc_stmt),
|
||||||
|
masked,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -380,20 +415,45 @@ pub fn rc_ptr_from_data_ptr<'a>(
|
||||||
rc_ptr_sym: Symbol,
|
rc_ptr_sym: Symbol,
|
||||||
mask_lower_bits: bool,
|
mask_lower_bits: bool,
|
||||||
following: &'a Stmt<'a>,
|
following: &'a Stmt<'a>,
|
||||||
|
) -> Stmt<'a> {
|
||||||
|
let addr_sym = root.create_symbol(ident_ids, "addr");
|
||||||
|
rc_ptr_from_data_ptr_help(
|
||||||
|
root,
|
||||||
|
ident_ids,
|
||||||
|
structure,
|
||||||
|
rc_ptr_sym,
|
||||||
|
mask_lower_bits,
|
||||||
|
following,
|
||||||
|
addr_sym,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rc_ptr_from_data_ptr_help<'a>(
|
||||||
|
root: &CodeGenHelp<'a>,
|
||||||
|
ident_ids: &mut IdentIds,
|
||||||
|
structure: Symbol,
|
||||||
|
rc_ptr_sym: Symbol,
|
||||||
|
mask_lower_bits: bool,
|
||||||
|
following: &'a Stmt<'a>,
|
||||||
|
addr_sym: Symbol,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
use std::ops::Neg;
|
use std::ops::Neg;
|
||||||
|
|
||||||
// Typecast the structure pointer to an integer
|
// Typecast the structure pointer to an integer
|
||||||
// Backends expect a number Layout to choose the right "subtract" instruction
|
// Backends expect a number Layout to choose the right "subtract" instruction
|
||||||
let addr_sym = root.create_symbol(ident_ids, "addr");
|
let as_int_sym = if mask_lower_bits {
|
||||||
let addr_expr = Expr::Call(Call {
|
root.create_symbol(ident_ids, "as_int")
|
||||||
|
} else {
|
||||||
|
addr_sym
|
||||||
|
};
|
||||||
|
let as_int_expr = Expr::Call(Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
op: LowLevel::PtrCast,
|
op: LowLevel::PtrCast,
|
||||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
},
|
},
|
||||||
arguments: root.arena.alloc([structure]),
|
arguments: root.arena.alloc([structure]),
|
||||||
});
|
});
|
||||||
let addr_stmt = |next| Stmt::Let(addr_sym, addr_expr, root.layout_isize, next);
|
let as_int_stmt = |next| Stmt::Let(as_int_sym, as_int_expr, root.layout_isize, next);
|
||||||
|
|
||||||
// Mask for lower bits (for tag union id)
|
// Mask for lower bits (for tag union id)
|
||||||
let mask_sym = root.create_symbol(ident_ids, "mask");
|
let mask_sym = root.create_symbol(ident_ids, "mask");
|
||||||
|
@ -402,15 +462,14 @@ pub fn rc_ptr_from_data_ptr<'a>(
|
||||||
));
|
));
|
||||||
let mask_stmt = |next| Stmt::Let(mask_sym, mask_expr, root.layout_isize, next);
|
let mask_stmt = |next| Stmt::Let(mask_sym, mask_expr, root.layout_isize, next);
|
||||||
|
|
||||||
let masked_sym = root.create_symbol(ident_ids, "masked");
|
|
||||||
let and_expr = Expr::Call(Call {
|
let and_expr = Expr::Call(Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
op: LowLevel::And,
|
op: LowLevel::And,
|
||||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
},
|
},
|
||||||
arguments: root.arena.alloc([addr_sym, mask_sym]),
|
arguments: root.arena.alloc([as_int_sym, mask_sym]),
|
||||||
});
|
});
|
||||||
let and_stmt = |next| Stmt::Let(masked_sym, and_expr, root.layout_isize, next);
|
let and_stmt = |next| Stmt::Let(addr_sym, and_expr, root.layout_isize, next);
|
||||||
|
|
||||||
// Pointer size constant
|
// Pointer size constant
|
||||||
let ptr_size_sym = root.create_symbol(ident_ids, "ptr_size");
|
let ptr_size_sym = root.create_symbol(ident_ids, "ptr_size");
|
||||||
|
@ -426,14 +485,7 @@ pub fn rc_ptr_from_data_ptr<'a>(
|
||||||
op: LowLevel::NumSub,
|
op: LowLevel::NumSub,
|
||||||
update_mode: UpdateModeId::BACKEND_DUMMY,
|
update_mode: UpdateModeId::BACKEND_DUMMY,
|
||||||
},
|
},
|
||||||
arguments: root.arena.alloc([
|
arguments: root.arena.alloc([addr_sym, ptr_size_sym]),
|
||||||
if mask_lower_bits {
|
|
||||||
masked_sym
|
|
||||||
} else {
|
|
||||||
addr_sym
|
|
||||||
},
|
|
||||||
ptr_size_sym,
|
|
||||||
]),
|
|
||||||
});
|
});
|
||||||
let sub_stmt = |next| Stmt::Let(rc_addr_sym, sub_expr, root.layout_isize, next);
|
let sub_stmt = |next| Stmt::Let(rc_addr_sym, sub_expr, root.layout_isize, next);
|
||||||
|
|
||||||
|
@ -448,7 +500,7 @@ pub fn rc_ptr_from_data_ptr<'a>(
|
||||||
let cast_stmt = |next| Stmt::Let(rc_ptr_sym, cast_expr, LAYOUT_PTR, next);
|
let cast_stmt = |next| Stmt::Let(rc_ptr_sym, cast_expr, LAYOUT_PTR, next);
|
||||||
|
|
||||||
if mask_lower_bits {
|
if mask_lower_bits {
|
||||||
addr_stmt(root.arena.alloc(
|
as_int_stmt(root.arena.alloc(
|
||||||
//
|
//
|
||||||
mask_stmt(root.arena.alloc(
|
mask_stmt(root.arena.alloc(
|
||||||
//
|
//
|
||||||
|
@ -468,7 +520,7 @@ pub fn rc_ptr_from_data_ptr<'a>(
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
addr_stmt(root.arena.alloc(
|
as_int_stmt(root.arena.alloc(
|
||||||
//
|
//
|
||||||
ptr_size_stmt(root.arena.alloc(
|
ptr_size_stmt(root.arena.alloc(
|
||||||
//
|
//
|
||||||
|
|
|
@ -2205,9 +2205,10 @@ fn nullable_eval_cfold() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn nested_switch() {
|
fn nested_switch() {
|
||||||
// exposed bug with passing the right symbol/layout down into switch branch generation
|
// exposed bug with passing the right symbol/layout down into switch branch generation
|
||||||
|
// This is also the only test_gen test that exercises Reset/Reuse (as of Aug 2022)
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue