misc cleanup + change refcount pointer to avoid branching

This commit is contained in:
Brendan Hansknecht 2023-03-12 10:29:53 -07:00
parent 216fd3f9f6
commit dfb748fb03
No known key found for this signature in database
GPG key ID: 0EA784685083E75B
11 changed files with 108 additions and 210 deletions

View file

@ -2789,7 +2789,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
let alignment =
element_layout.alignment_bytes(layout_interner, env.target_info);
build_list::decref(env, value.into_struct_value(), alignment, parent);
build_list::decref(env, value.into_struct_value(), alignment);
}
lay if lay.is_refcounted() => {

View file

@ -394,15 +394,34 @@ pub(crate) fn list_len<'ctx>(
}
/// List.capacity : List * -> Nat
pub(crate) fn list_capacity<'ctx>(
builder: &Builder<'ctx>,
pub(crate) fn list_capacity<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
wrapper_struct: StructValue<'ctx>,
) -> IntValue<'ctx> {
// TODO: This won't work on seemless slices. Needs to return the len if the capacity is negative.
builder
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "list_capacity")
.unwrap()
.into_int_value()
call_list_bitcode_fn(
env,
&[wrapper_struct],
&[],
BitcodeReturns::Basic,
bitcode::LIST_CAPACITY,
)
.into_int_value()
}
// Gets a pointer to just after the refcount for a list or seamless slice.
// The value is just after the refcount so that normal lists and seamless slices can share code paths easily.
pub(crate) fn list_refcount_ptr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
wrapper_struct: StructValue<'ctx>,
) -> PointerValue<'ctx> {
call_list_bitcode_fn(
env,
&[wrapper_struct],
&[],
BitcodeReturns::Basic,
bitcode::LIST_REFCOUNT_PTR,
)
.into_pointer_value()
}
pub(crate) fn destructure<'ctx>(
@ -801,56 +820,8 @@ pub(crate) fn decref<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
wrapper_struct: StructValue<'ctx>,
alignment: u32,
parent: FunctionValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
let refcount_ptr = list_refcount_ptr(env, wrapper_struct);
let capacity = list_capacity(builder, wrapper_struct);
let is_regular_list = builder.build_int_compare(
IntPredicate::SGE,
capacity,
env.ptr_int().const_zero(),
"cap >= 0",
);
let get_list_ptr_block = ctx.append_basic_block(parent, "get_list_ptr");
let get_slice_ptr_block = ctx.append_basic_block(parent, "get_slice_ptr");
let decref_block = ctx.append_basic_block(parent, "decref");
builder.build_conditional_branch(is_regular_list, get_list_ptr_block, get_slice_ptr_block);
builder.position_at_end(get_list_ptr_block);
let ptr_type = env.context.i8_type().ptr_type(AddressSpace::default());
let (_, list_ptr) = load_list(builder, wrapper_struct, ptr_type);
builder.build_unconditional_branch(decref_block);
builder.position_at_end(get_slice_ptr_block);
let refcount_ptr_int = builder.build_left_shift(
capacity,
env.ptr_int().const_int(1, false),
"extract_refcount_from_capacity",
);
// TODO: adding one here feels silly.
// We should probably expose a zig builtin to directly deal with the refcount pointer.
// Instead we add one and then zig subtracts one.
let slice_ptr_int = builder.build_int_add(
refcount_ptr_int,
env.ptr_int().const_int(1, false),
"inc_refcount_ptr",
);
let slice_ptr = builder.build_int_to_ptr(slice_ptr_int, ptr_type, "to_slice_ptr");
builder.build_unconditional_branch(decref_block);
builder.position_at_end(decref_block);
let result = builder.build_phi(ptr_type, "branch");
result.add_incoming(&[
(&list_ptr, get_list_ptr_block),
(&slice_ptr, get_slice_ptr_block),
]);
crate::llvm::refcounting::decref_pointer_check_null(
env,
result.as_basic_value().into_pointer_value(),
alignment,
);
crate::llvm::refcounting::decref_pointer_check_null(env, refcount_ptr, alignment);
}

View file

@ -28,8 +28,8 @@ use crate::llvm::{
load_roc_value, roc_function_call, BuilderExt, RocReturn,
},
build_list::{
list_append_unsafe, list_capacity, list_concat, list_drop_at, list_get_unsafe, list_len,
list_map, list_map2, list_map3, list_map4, list_prepend, list_replace_unsafe, list_reserve,
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
list_map2, list_map3, list_map4, list_prepend, list_replace_unsafe, list_reserve,
list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity,
pass_update_mode,
},
@ -632,10 +632,16 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
list_len(env.builder, list.into_struct_value()).into()
}
ListGetCapacity => {
// List.capacity : List * -> Nat
// List.capacity: List a -> Nat
arguments!(list);
list_capacity(env.builder, list.into_struct_value()).into()
call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[],
BitcodeReturns::Basic,
bitcode::LIST_CAPACITY,
)
}
ListWithCapacity => {
// List.withCapacity : Nat -> List a

View file

@ -5,7 +5,9 @@ use crate::llvm::build::{
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
FAST_CALL_CONV,
};
use crate::llvm::build_list::{incrementing_elem_loop, list_capacity, load_list};
use crate::llvm::build_list::{
incrementing_elem_loop, list_capacity, list_refcount_ptr, load_list,
};
use crate::llvm::convert::{basic_type_from_layout, zig_str_type, RocUnion};
use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
@ -45,21 +47,6 @@ impl<'ctx> PointerToRefcount<'ctx> {
Self { value }
}
/// # Safety
///
/// the invariant is that the given pointer really points to the refcount,
/// not the data, and only is the start of the allocated buffer if the
/// alignment works out that way.
pub unsafe fn from_ptr_int<'a, 'env>(env: &Env<'a, 'ctx, 'env>, int: IntValue<'ctx>) -> Self {
let refcount_type = env.ptr_int();
let refcount_ptr_type = refcount_type.ptr_type(AddressSpace::default());
let value = env
.builder
.build_int_to_ptr(int, refcount_ptr_type, "to_refcount_ptr");
Self { value }
}
pub fn from_ptr_to_data<'a, 'env>(
env: &Env<'a, 'ctx, 'env>,
data_ptr: PointerValue<'ctx>,
@ -706,10 +693,10 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
let parent = fn_val;
let original_wrapper = arg_val.into_struct_value();
let capacity = list_capacity(builder, original_wrapper);
let capacity = list_capacity(env, original_wrapper);
let is_non_empty = builder.build_int_compare(
IntPredicate::SGT,
IntPredicate::UGT,
capacity,
env.ptr_int().const_zero(),
"cap > 0",
@ -717,11 +704,9 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
// build blocks
let modification_list_block = ctx.append_basic_block(parent, "modification_list_block");
let check_slice_block = ctx.append_basic_block(parent, "check_slice_block");
let modification_slice_block = ctx.append_basic_block(parent, "modification_slice_block");
let cont_block = ctx.append_basic_block(parent, "modify_rc_list_cont");
builder.build_conditional_branch(is_non_empty, modification_list_block, check_slice_block);
builder.build_conditional_branch(is_non_empty, modification_list_block, cont_block);
builder.position_at_end(modification_list_block);
@ -754,60 +739,8 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
);
}
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(call_mode, layout, env, layout_interner);
builder.build_unconditional_branch(cont_block);
builder.position_at_end(check_slice_block);
let is_seamless_slice = builder.build_int_compare(
IntPredicate::SLT,
capacity,
env.ptr_int().const_zero(),
"cap < 0",
);
builder.build_conditional_branch(is_seamless_slice, modification_slice_block, cont_block);
builder.position_at_end(modification_slice_block);
if layout_interner.contains_refcounted(element_layout) {
let ptr_type = basic_type_from_layout(env, layout_interner, element_layout)
.ptr_type(AddressSpace::default());
let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type);
let loop_fn = |layout_interner, _index, element| {
modify_refcount_layout_help(
env,
layout_interner,
layout_ids,
mode.to_call_mode(fn_val),
element,
element_layout,
);
};
incrementing_elem_loop(
env,
layout_interner,
parent,
element_layout,
ptr,
len,
"modify_rc_index",
loop_fn,
);
}
// a slices refcount is `capacity << 1`
let refcount_ptr_int = builder.build_left_shift(
capacity,
env.ptr_int().const_int(1, false),
"extract_refcount_from_capacity",
);
let refcount_ptr = unsafe { PointerToRefcount::from_ptr_int(env, refcount_ptr_int) };
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, list_refcount_ptr(env, original_wrapper));
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(call_mode, layout, env, layout_interner);