wasm: Get RC reset/reuse working correctly

This commit is contained in:
Brian Carroll 2022-08-05 18:29:36 +01:00
parent e71a6da62d
commit aa0e9e8d31
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
3 changed files with 86 additions and 22 deletions

View file

@ -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();

View file

@ -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(
// //

View file

@ -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#"