use zig's decref instead of an LLVM implementation

This commit is contained in:
Folkert 2021-09-01 16:58:05 +02:00
parent 59c63ef3a8
commit 5cb7dbd3cc
5 changed files with 34 additions and 104 deletions

View file

@ -33,11 +33,12 @@ pub fn build(b: *Builder) void {
// LLVM IR 32-bit (wasm)
var target = b.standardTargetOptions(.{});
// 32-bit x86, useful for debuggin
// target.os_tag = std.Target.Os.Tag.linux;
// target.cpu_arch = std.Target.Cpu.Arch.i386;
// target.abi = std.Target.Abi.musl;
// Thisis what we want eventually, currently segfaults
// This is what we want eventually, currently segfaults
target.os_tag = std.Target.Os.Tag.wasi;
target.cpu_arch = std.Target.Cpu.Arch.wasm32;
target.abi = std.Target.Abi.none;

View file

@ -109,6 +109,7 @@ comptime {
const utils = @import("utils.zig");
comptime {
exportUtilsFn(utils.test_panic, "test_panic");
exportUtilsFn(utils.decrefC, "decref");
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
}

View file

@ -104,6 +104,18 @@ pub const IntWidth = enum(u8) {
Usize,
};
pub fn decrefC(
bytes_or_null: ?[*]u8,
data_bytes: usize,
alignment: u32,
) callconv(.C) void {
// IMPORTANT: bytes_or_null is this case is expected to be a pointer to the refcount
// (NOT the start of the data, or the start of the allocation)
if (bytes_or_null) |bytes| {
return @call(.{ .modifier = always_inline }, decref, .{ bytes + @sizeOf(usize), data_bytes, alignment });
}
}
pub fn decref(
bytes_or_null: ?[*]u8,
data_bytes: usize,

View file

@ -82,3 +82,4 @@ pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
pub const DEC_DIV: &str = "roc_builtins.dec.div";
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
pub const UTILS_DECREF: &str = "roc_builtins.utils.decref";

View file

@ -1,4 +1,5 @@
use crate::debug_info_init;
use crate::llvm::bitcode::{self, call_void_bitcode_fn};
use crate::llvm::build::{
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I32,
@ -227,113 +228,27 @@ impl<'ctx> PointerToRefcount<'ctx> {
}
};
let refcount = refcount_ptr.get_refcount(env);
let alignment = env.context.i32_type().const_int(alignment as _, false);
let is_static_allocation = builder.build_int_compare(
IntPredicate::EQ,
refcount,
env.ptr_int().const_zero(),
"is_static_allocation",
);
// has to be non-zero, or the deallocation is skipped
let data_bytes = env.ptr_int().const_int(1, false);
// build blocks
let branch_block = ctx.append_basic_block(parent, "branch");
let then_block = ctx.append_basic_block(parent, "then");
let else_block = ctx.append_basic_block(parent, "else");
let return_block = ctx.append_basic_block(parent, "return");
builder.build_conditional_branch(is_static_allocation, return_block, branch_block);
let add_intrinsic = match env.ptr_bytes {
8 => LLVM_SADD_WITH_OVERFLOW_I64,
4 => LLVM_SADD_WITH_OVERFLOW_I32,
_ => unreachable!(),
};
let add_with_overflow;
{
builder.position_at_end(branch_block);
add_with_overflow = env
.call_intrinsic(
add_intrinsic,
call_void_bitcode_fn(
env,
&[
refcount.into(),
refcount_type.const_int(-1_i64 as u64, true).into(),
env.builder.build_bitcast(
parent.get_nth_param(0).unwrap(),
env.context.i8_type().ptr_type(AddressSpace::Generic),
"foo",
),
data_bytes.into(),
alignment.into(),
],
)
.into_struct_value();
let has_overflowed = builder
.build_extract_value(add_with_overflow, 1, "has_overflowed")
.unwrap();
let has_overflowed_comparison = builder.build_int_compare(
IntPredicate::EQ,
has_overflowed.into_int_value(),
ctx.bool_type().const_int(1_u64, false),
"has_overflowed",
roc_builtins::bitcode::UTILS_DECREF,
);
// TODO what would be most optimial for the branch predictor
//
// are most refcounts 1 most of the time? or not?
builder.build_conditional_branch(has_overflowed_comparison, then_block, else_block);
}
// build then block
{
builder.position_at_end(then_block);
if !env.is_gen_test {
let ptr = builder.build_pointer_cast(
refcount_ptr.value,
ctx.i8_type().ptr_type(AddressSpace::Generic),
"cast_to_i8_ptr",
);
match alignment {
n if env.ptr_bytes == n => {
// the refcount ptr is also the ptr to the allocated region
env.call_dealloc(ptr, alignment);
}
n if 2 * env.ptr_bytes == n => {
// we need to step back another ptr_bytes to get the allocated ptr
let allocated = Self::from_ptr_to_data(env, ptr);
env.call_dealloc(allocated.value, alignment);
}
n => unreachable!("invalid extra_bytes {:?}", n),
}
}
builder.build_unconditional_branch(return_block);
}
// build else block
{
builder.position_at_end(else_block);
let max = builder.build_int_compare(
IntPredicate::EQ,
refcount,
refcount_type.const_int(REFCOUNT_MAX as u64, false),
"refcount_max_check",
);
let decremented = builder
.build_extract_value(add_with_overflow, 0, "decrement_refcount")
.unwrap()
.into_int_value();
let selected = builder.build_select(max, refcount, decremented, "select_refcount");
refcount_ptr.set_refcount(env, selected.into_int_value());
builder.build_unconditional_branch(return_block);
}
{
builder.position_at_end(return_block);
builder.build_return(None);
}
}
}
fn modify_refcount_struct<'a, 'ctx, 'env>(