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 1 commit
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
34 changes: 34 additions & 0 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,6 +259,17 @@ pub fn decref(
decref_ptr_to_refcount(isizes - 1, alignment);
}

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,
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
33 changes: 33 additions & 0 deletions crates/compiler/gen_llvm/src/llvm/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2919,6 +2919,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
44 changes: 42 additions & 2 deletions crates/compiler/gen_wasm/src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bitvec::vec::BitVec;
use bumpalo::collections::{String, Vec};

use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_error_macros::internal_error;
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
Expand Down Expand Up @@ -719,7 +719,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> {

Stmt::Jump(id, arguments) => self.stmt_jump(*id, arguments),

Stmt::Refcounting(modify, following) => self.stmt_refcounting(modify, following),
Stmt::Refcounting(modify, following) => match modify {
ModifyRc::Free(symbol) => self.stmt_refcounting_free(*symbol, following),
_ => self.stmt_refcounting(modify, following),
},

Stmt::Dbg { .. } => todo!("dbg is not implemented in the wasm backend"),
Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"),
Expand Down Expand Up @@ -999,6 +1002,43 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
self.stmt(rc_stmt);
}

fn stmt_refcounting_free(&mut self, value: Symbol, following: &'a Stmt<'a>) {
let layout = self.storage.symbol_layouts[&value];
let alignment = self.layout_interner.allocation_alignment_bytes(layout);

// Get pointer and offset
let value_storage = self.storage.get(&value).to_owned();
let stored_with_local =
self.storage
.ensure_value_has_local(&mut self.code_builder, value, value_storage);
let (tag_local_id, tag_offset) = match stored_with_local {
StoredValue::StackMemory { location, .. } => {
location.local_and_offset(self.storage.stack_frame_pointer)
}
StoredValue::Local { local_id, .. } => (local_id, 0),
StoredValue::VirtualMachineStack { .. } => {
internal_error!("{:?} should have a local variable", value)
}
};

// load pointer, and add the offset to the pointer
self.code_builder.get_local(tag_local_id);

if tag_offset > 0 {
self.code_builder.i32_const(tag_offset as i32);
self.code_builder.i32_add();
}

// NOTE: UTILS_FREE_DATA_PTR clears any tag id bits

// push the allocation's alignment
self.code_builder.i32_const(alignment as i32);

self.call_host_fn_after_loading_args(bitcode::UTILS_FREE_DATA_PTR, 2, false);

self.stmt(following);
}

pub fn stmt_internal_error(&mut self, msg: &'a str) {
let msg_sym = self.create_symbol("panic_str");
let msg_storage = self.storage.allocate_var(
Expand Down
13 changes: 13 additions & 0 deletions crates/compiler/gen_wasm/src/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1979,6 +1979,19 @@ impl<'a> LowLevelCall<'a> {
);
}
PtrLoad => backend.expr_unbox(self.ret_symbol, self.arguments[0]),
PtrClearTagId => {
let ptr = self.arguments[0];

let ptr_local_id = match backend.storage.get(&ptr) {
StoredValue::Local { local_id, .. } => *local_id,
_ => internal_error!("A pointer will always be an i32"),
};

backend.code_builder.get_local(ptr_local_id);

backend.code_builder.i32_const(-4); // 11111111...1100
backend.code_builder.i32_and();
}
Alloca => {
// Alloca : a -> Ptr a
let arg = self.arguments[0];
Expand Down
1 change: 1 addition & 0 deletions crates/compiler/load_internal/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3141,6 +3141,7 @@ fn update<'a>(
arena,
&layout_interner,
module_id,
state.target_info,
ident_ids,
&mut update_mode_ids,
&mut state.procedures,
Expand Down
2 changes: 2 additions & 0 deletions crates/compiler/module/src/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub enum LowLevel {
PtrCast,
PtrStore,
PtrLoad,
PtrClearTagId,
Alloca,
RefCountIncRcPtr,
RefCountDecRcPtr,
Expand Down Expand Up @@ -232,6 +233,7 @@ macro_rules! map_symbol_to_lowlevel {
LowLevel::PtrCast => unimplemented!(),
LowLevel::PtrStore => unimplemented!(),
LowLevel::PtrLoad => unimplemented!(),
LowLevel::PtrClearTagId => unimplemented!(),
LowLevel::Alloca => unimplemented!(),
LowLevel::RefCountIncRcPtr => unimplemented!(),
LowLevel::RefCountDecRcPtr=> unimplemented!(),
Expand Down
Loading