Skip to content

Commit

Permalink
Merge pull request #5622 from roc-lang/reset-reuse-free
Browse files Browse the repository at this point in the history
free or reuse unconditionally when value is unique
  • Loading branch information
rtfeldman authored Jun 28, 2023
2 parents fd5616d + 48e6ef7 commit 0ade2a8
Show file tree
Hide file tree
Showing 37 changed files with 577 additions and 289 deletions.
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(
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 @@ -2930,6 +2948,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

0 comments on commit 0ade2a8

Please sign in to comment.