Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

free or reuse unconditionally when value is unique #5622

Merged
merged 6 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/compiler/alias_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ fn apply_refcount_operation(
builder.add_recursive_touch(block, argument)?;
}
ModifyRc::DecRef(symbol) => {
// this is almost certainly suboptimal, but not incorrect
let argument = env.symbols[symbol];
builder.add_recursive_touch(block, argument)?;
}
ModifyRc::Free(symbol) => {
// this is almost certainly suboptimal, but not incorrect
let argument = env.symbols[symbol];
builder.add_recursive_touch(block, argument)?;
}
Expand Down
2 changes: 2 additions & 0 deletions crates/compiler/builtins/bitcode/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ comptime {
exportUtilsFn(utils.test_panic, "test_panic");
exportUtilsFn(utils.increfRcPtrC, "incref_rc_ptr");
exportUtilsFn(utils.decrefRcPtrC, "decref_rc_ptr");
exportUtilsFn(utils.freeRcPtrC, "free_rc_ptr");
exportUtilsFn(utils.increfDataPtrC, "incref_data_ptr");
exportUtilsFn(utils.decrefDataPtrC, "decref_data_ptr");
exportUtilsFn(utils.freeDataPtrC, "free_data_ptr");
exportUtilsFn(utils.isUnique, "is_unique");
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
Expand Down
39 changes: 36 additions & 3 deletions crates/compiler/builtins/bitcode/src/utils.zig
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,29 @@ pub fn increfDataPtrC(
return increfRcPtrC(isizes, inc_amount);
}

pub fn freeDataPtrC(
bytes_or_null: ?[*]isize,
alignment: u32,
) callconv(.C) void {
var bytes = bytes_or_null orelse return;

const ptr = @ptrToInt(bytes);
const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11;
const masked_ptr = ptr & ~tag_mask;

const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);

return freeRcPtrC(isizes - 1, alignment);
}

pub fn freeRcPtrC(
bytes_or_null: ?[*]isize,
alignment: u32,
) callconv(.C) void {
var bytes = bytes_or_null orelse return;
return free_ptr_to_refcount(bytes, alignment);
}

pub fn decref(
bytes_or_null: ?[*]u8,
data_bytes: usize,
Expand All @@ -236,13 +259,23 @@ pub fn decref(
decref_ptr_to_refcount(isizes - 1, alignment);
}

inline fn decref_ptr_to_refcount(
inline fn free_ptr_to_refcount(
JTeeuwissen marked this conversation as resolved.
Show resolved Hide resolved
refcount_ptr: [*]isize,
alignment: u32,
) void {
if (RC_TYPE == Refcount.none) return;
const extra_bytes = std.math.max(alignment, @sizeOf(usize));

// NOTE: we don't even check whether the refcount is "infinity" here!
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
}

inline fn decref_ptr_to_refcount(
refcount_ptr: [*]isize,
alignment: u32,
) void {
if (RC_TYPE == Refcount.none) return;

if (DEBUG_INCDEC and builtin.target.cpu.arch != .wasm32) {
std.debug.print("| decrement {*}: ", .{refcount_ptr});
}
Expand All @@ -264,13 +297,13 @@ inline fn decref_ptr_to_refcount(
}

if (refcount == REFCOUNT_ONE_ISIZE) {
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
free_ptr_to_refcount(refcount_ptr, alignment);
}
},
Refcount.atomic => {
var last = @atomicRmw(isize, &refcount_ptr[0], std.builtin.AtomicRmwOp.Sub, 1, Monotonic);
if (last == REFCOUNT_ONE_ISIZE) {
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
free_ptr_to_refcount(refcount_ptr, alignment);
}
},
Refcount.none => unreachable,
Expand Down
2 changes: 2 additions & 0 deletions crates/compiler/builtins/src/bitcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,10 @@ pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
pub const UTILS_ALLOCATE_WITH_REFCOUNT: &str = "roc_builtins.utils.allocate_with_refcount";
pub const UTILS_INCREF_RC_PTR: &str = "roc_builtins.utils.incref_rc_ptr";
pub const UTILS_DECREF_RC_PTR: &str = "roc_builtins.utils.decref_rc_ptr";
pub const UTILS_FREE_RC_PTR: &str = "roc_builtins.utils.free_rc_ptr";
pub const UTILS_INCREF_DATA_PTR: &str = "roc_builtins.utils.incref_data_ptr";
pub const UTILS_DECREF_DATA_PTR: &str = "roc_builtins.utils.decref_data_ptr";
pub const UTILS_FREE_DATA_PTR: &str = "roc_builtins.utils.free_data_ptr";
pub const UTILS_IS_UNIQUE: &str = "roc_builtins.utils.is_unique";
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
pub const UTILS_DICT_PSEUDO_SEED: &str = "roc_builtins.utils.dict_pseudo_seed";
Expand Down
1 change: 1 addition & 0 deletions crates/compiler/can/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
LowLevel::PtrCast => unimplemented!(),
LowLevel::PtrStore => unimplemented!(),
LowLevel::PtrLoad => unimplemented!(),
LowLevel::PtrClearTagId => unimplemented!(),
LowLevel::Alloca => unimplemented!(),
LowLevel::RefCountIncRcPtr => unimplemented!(),
LowLevel::RefCountDecRcPtr=> unimplemented!(),
Expand Down
17 changes: 11 additions & 6 deletions crates/compiler/gen_dev/src/generic64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2996,12 +2996,17 @@ impl<
);
}

fn build_ptr_to_stack_value(
&mut self,
sym: Symbol,
value: Symbol,
element_layout: InLayout<'a>,
) {
fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol) {
let buf = &mut self.buf;

let ptr_reg = self.storage_manager.load_to_general_reg(buf, &ptr);
let sym_reg = self.storage_manager.claim_general_reg(buf, &sym);

ASM::mov_reg64_imm64(buf, sym_reg, !0b111);
ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg);
}

fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) {
// 1. acquire some stack space
let element_width = self.interner().stack_size(element_layout);
let allocation = self.debug_symbol("stack_allocation");
Expand Down
40 changes: 32 additions & 8 deletions crates/compiler/gen_dev/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
Literal, Param, Proc, ProcLayout, SelfRecursive, Stmt,
Literal, ModifyRc, Param, Proc, ProcLayout, SelfRecursive, Stmt,
};
use roc_mono::layout::{
Builtin, InLayout, LambdaName, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
Expand Down Expand Up @@ -525,6 +525,29 @@ trait Backend<'a> {
self.return_symbol(sym, ret_layout);
self.free_symbols(stmt);
}
Stmt::Refcounting(ModifyRc::Free(symbol), following) => {
let dst = Symbol::DEV_TMP;

let layout = *self.layout_map().get(symbol).unwrap();
let alignment_bytes = self.interner().allocation_alignment_bytes(layout);
let alignment = self.debug_symbol("alignment");
self.load_literal_i32(&alignment, alignment_bytes as i32);

// NOTE: UTILS_FREE_DATA_PTR clears any tag id bits

self.build_fn_call(
&dst,
bitcode::UTILS_FREE_DATA_PTR.to_string(),
&[*symbol, alignment],
&[Layout::I64, Layout::I32],
&Layout::UNIT,
);

self.free_symbol(&dst);
self.free_symbol(&alignment);

self.build_stmt(layout_ids, following, ret_layout)
}
Stmt::Refcounting(modify, following) => {
let sym = modify.get_symbol();
let layout = *self.layout_map().get(&sym).unwrap();
Expand Down Expand Up @@ -1605,8 +1628,12 @@ trait Backend<'a> {
self.build_ptr_load(*sym, args[0], *ret_layout);
}

LowLevel::PtrClearTagId => {
self.build_ptr_clear_tag_id(*sym, args[0]);
}

LowLevel::Alloca => {
self.build_ptr_to_stack_value(*sym, args[0], arg_layouts[0]);
self.build_alloca(*sym, args[0], arg_layouts[0]);
}

LowLevel::RefCountDecRcPtr => self.build_fn_call(
Expand Down Expand Up @@ -2247,12 +2274,9 @@ trait Backend<'a> {

fn build_ptr_load(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>);

fn build_ptr_to_stack_value(
&mut self,
sym: Symbol,
value: Symbol,
element_layout: InLayout<'a>,
);
fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol);

fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>);

/// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding.
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const InLayout<'a>)>;
Expand Down
59 changes: 55 additions & 4 deletions crates/compiler/gen_llvm/src/llvm/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
let then_block = ctx.append_basic_block(parent, "then_reset");
let else_block = ctx.append_basic_block(parent, "else_decref");

let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
let refcount_ptr = PointerToRefcount::from_ptr_to_data(
env,
if union_layout.stores_tag_id_in_pointer(env.target_info) {
tag_pointer_clear_tag_id(env, tag_ptr)
} else {
tag_ptr
},
);

let is_unique = match update_mode {
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
Expand Down Expand Up @@ -1265,8 +1271,20 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(

let not_unique_block = ctx.append_basic_block(parent, "else_decref");

let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
// reset is only generated for union values
let union_layout = match layout_interner.get_repr(layout) {
LayoutRepr::Union(ul) => ul,
_ => unreachable!(),
};

let refcount_ptr = PointerToRefcount::from_ptr_to_data(
env,
if union_layout.stores_tag_id_in_pointer(env.target_info) {
tag_pointer_clear_tag_id(env, tag_ptr)
} else {
tag_ptr
},
);

let is_unique = match update_mode {
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
Expand Down Expand Up @@ -2919,6 +2937,39 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
cont,
)
}

Free(symbol) => {
// unconditionally deallocate the symbol
let (value, layout) = scope.load_symbol_and_layout(symbol);
let alignment = layout_interner.alignment_bytes(layout);

debug_assert!(value.is_pointer_value());
let value = value.into_pointer_value();

let clear_tag_id = match layout_interner.chase_recursive(layout) {
LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target_info),
_ => false,
};

let ptr = if clear_tag_id {
tag_pointer_clear_tag_id(env, value)
} else {
value
};

let rc_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
rc_ptr.deallocate(env, alignment);

build_exp_stmt(
env,
layout_interner,
layout_ids,
func_spec_solutions,
scope,
parent,
cont,
)
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions crates/compiler/gen_llvm/src/llvm/lowlevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,12 @@ pub(crate) fn run_low_level<'a, 'ctx>(
.new_build_load(element_type, ptr.into_pointer_value(), "ptr_load")
}

PtrClearTagId => {
arguments!(ptr);

tag_pointer_clear_tag_id(env, ptr.into_pointer_value()).into()
}

Alloca => {
arguments!(initial_value);

Expand Down
32 changes: 31 additions & 1 deletion crates/compiler/gen_llvm/src/llvm/refcounting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue};
use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
Expand Down Expand Up @@ -193,6 +193,14 @@ impl<'ctx> PointerToRefcount<'ctx> {

builder.build_return(None);
}

pub fn deallocate<'a, 'env>(
&self,
env: &Env<'a, 'ctx, 'env>,
alignment: u32,
) -> InstructionValue<'ctx> {
free_pointer(env, self.value, alignment)
}
}

fn incref_pointer<'ctx>(
Expand All @@ -216,6 +224,28 @@ fn incref_pointer<'ctx>(
);
}

fn free_pointer<'ctx>(
env: &Env<'_, 'ctx, '_>,
pointer: PointerValue<'ctx>,
alignment: u32,
) -> InstructionValue<'ctx> {
let alignment = env.context.i32_type().const_int(alignment as _, false);
call_void_bitcode_fn(
env,
&[
env.builder
.build_pointer_cast(
pointer,
env.ptr_int().ptr_type(AddressSpace::default()),
"to_isize_ptr",
)
.into(),
alignment.into(),
],
roc_builtins::bitcode::UTILS_FREE_RC_PTR,
)
}

fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, alignment: u32) {
let alignment = env.context.i32_type().const_int(alignment as _, false);
call_void_bitcode_fn(
Expand Down
Loading