mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
implement decref, fuse RC operations
This commit is contained in:
parent
c9e2958947
commit
210bd18fe3
8 changed files with 422 additions and 122 deletions
|
@ -3,6 +3,7 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const level_size = 32;
|
||||
|
||||
|
@ -48,7 +49,7 @@ fn total_slots_at_level(input: usize) usize {
|
|||
return slots;
|
||||
}
|
||||
|
||||
fn slots_at_level(input: usize) usize {
|
||||
fn capacityOfLevel(input: usize) usize {
|
||||
if (input == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -118,60 +119,18 @@ pub const RocDict = extern struct {
|
|||
|
||||
pub fn allocate(
|
||||
allocator: *Allocator,
|
||||
result_in_place: InPlace,
|
||||
number_of_levels: usize,
|
||||
number_of_entries: usize,
|
||||
alignment: usize,
|
||||
alignment: Alignment,
|
||||
key_size: usize,
|
||||
value_size: usize,
|
||||
) RocDict {
|
||||
const number_of_slots = total_slots_at_level(number_of_levels);
|
||||
const first_slot = switch (alignment) {
|
||||
8 => blk: {
|
||||
const slot_size = slotSize(key_size, value_size);
|
||||
|
||||
const length = @sizeOf(usize) + (number_of_slots * slot_size);
|
||||
|
||||
var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable;
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
if (result_in_place == InPlace.InPlace) {
|
||||
as_usize_array[0] = @intCast(usize, number_of_slots);
|
||||
} else {
|
||||
as_usize_array[0] = REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + @sizeOf(usize);
|
||||
|
||||
break :blk first_slot;
|
||||
},
|
||||
16 => blk: {
|
||||
const slot_size = slotSize(key_size, value_size);
|
||||
|
||||
const length = 2 * @sizeOf(usize) + (number_of_slots * slot_size);
|
||||
|
||||
var new_bytes: []align(16) u8 = allocator.alignedAlloc(u8, 16, length) catch unreachable;
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
if (result_in_place == InPlace.InPlace) {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = @intCast(usize, number_of_slots);
|
||||
} else {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + 2 * @sizeOf(usize);
|
||||
|
||||
break :blk first_slot;
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
const slot_size = slotSize(key_size, value_size);
|
||||
const data_bytes = number_of_slots * slot_size;
|
||||
|
||||
return RocDict{
|
||||
.dict_bytes = first_slot,
|
||||
.dict_bytes = allocateWithRefcount(allocator, alignment, data_bytes),
|
||||
.number_of_levels = number_of_levels,
|
||||
.dict_entries_len = number_of_entries,
|
||||
};
|
||||
|
@ -180,7 +139,7 @@ pub const RocDict = extern struct {
|
|||
pub fn reallocate(
|
||||
self: RocDict,
|
||||
allocator: *Allocator,
|
||||
alignment: usize,
|
||||
alignment: Alignment,
|
||||
key_width: usize,
|
||||
value_width: usize,
|
||||
) RocDict {
|
||||
|
@ -189,23 +148,10 @@ pub const RocDict = extern struct {
|
|||
|
||||
const old_capacity = self.capacity();
|
||||
const new_capacity = total_slots_at_level(new_level);
|
||||
const delta_capacity = new_capacity - old_capacity;
|
||||
|
||||
const first_slot = switch (alignment) {
|
||||
8 => blk: {
|
||||
const length = @sizeOf(usize) + (new_capacity * slot_size);
|
||||
|
||||
var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable;
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
as_usize_array[0] = REFCOUNT_ONE;
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + @sizeOf(usize);
|
||||
|
||||
break :blk first_slot;
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
const data_bytes = new_capacity * slot_size;
|
||||
const first_slot = allocateWithRefcount(allocator, alignment, data_bytes);
|
||||
|
||||
// transfer the memory
|
||||
|
||||
|
@ -215,28 +161,47 @@ pub const RocDict = extern struct {
|
|||
if (old_capacity > 0) {
|
||||
var source_offset: usize = 0;
|
||||
var dest_offset: usize = 0;
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * key_width);
|
||||
|
||||
source_offset += old_capacity * key_width;
|
||||
dest_offset += old_capacity * key_width + (new_capacity * key_width);
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * value_width);
|
||||
if (alignment.keyFirst()) {
|
||||
// copy keys
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * key_width);
|
||||
|
||||
source_offset += old_capacity * value_width;
|
||||
dest_offset += old_capacity * value_width + (new_capacity * value_width);
|
||||
// copy values
|
||||
source_offset = old_capacity * key_width;
|
||||
dest_offset = new_capacity * key_width;
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * value_width);
|
||||
} else {
|
||||
// copy values
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * value_width);
|
||||
|
||||
// copy keys
|
||||
source_offset = old_capacity * value_width;
|
||||
dest_offset = new_capacity * value_width;
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * key_width);
|
||||
}
|
||||
|
||||
// copy slots
|
||||
source_offset = old_capacity * (key_width + value_width);
|
||||
dest_offset = new_capacity * (key_width + value_width);
|
||||
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * @sizeOf(Slot));
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
const first_new_slot_value = dest_ptr + old_capacity * slot_size + new_capacity * (key_width + value_width);
|
||||
const first_new_slot_value = dest_ptr + old_capacity * slot_size + delta_capacity * (key_width + value_width);
|
||||
while (i < (new_capacity - old_capacity)) : (i += 1) {
|
||||
(first_new_slot_value)[i] = @enumToInt(Slot.Empty);
|
||||
}
|
||||
|
||||
return RocDict{
|
||||
const result = RocDict{
|
||||
.dict_bytes = first_slot,
|
||||
.number_of_levels = self.number_of_levels + 1,
|
||||
.dict_entries_len = self.dict_entries_len,
|
||||
};
|
||||
|
||||
// NOTE we fuse an increment of all keys/values with a decrement of the input dict
|
||||
decref(allocator, alignment, self.dict_bytes, self.capacity() * slotSize(key_width, value_width));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn asU8ptr(self: RocDict) [*]u8 {
|
||||
|
@ -270,7 +235,7 @@ pub const RocDict = extern struct {
|
|||
return total_slots_at_level(self.number_of_levels);
|
||||
}
|
||||
|
||||
pub fn makeUnique(self: RocDict, allocator: *Allocator, in_place: InPlace, alignment: Alignment, key_width: usize, value_width: usize, inc_key: Inc, inc_value: Inc) RocDict {
|
||||
pub fn makeUnique(self: RocDict, allocator: *Allocator, alignment: Alignment, key_width: usize, value_width: usize) RocDict {
|
||||
if (self.isEmpty()) {
|
||||
return self;
|
||||
}
|
||||
|
@ -281,28 +246,21 @@ pub const RocDict = extern struct {
|
|||
|
||||
// unfortunately, we have to clone
|
||||
|
||||
var new_dict = RocDict.allocate(allocator, in_place, 8, self.dict_entries_len, alignment.toUsize(), key_width, value_width);
|
||||
const in_place = InPlace.Clone;
|
||||
var new_dict = RocDict.allocate(allocator, self.number_of_levels, self.dict_entries_len, alignment, key_width, value_width);
|
||||
|
||||
var old_bytes: [*]u8 = @ptrCast([*]u8, self.dict_bytes);
|
||||
var new_bytes: [*]u8 = @ptrCast([*]u8, new_dict.dict_bytes);
|
||||
|
||||
const number_of_bytes = 8 * (@sizeOf(Slot) + key_width + value_width);
|
||||
const number_of_bytes = self.capacity() * (@sizeOf(Slot) + key_width + value_width);
|
||||
@memcpy(new_bytes, old_bytes, number_of_bytes);
|
||||
|
||||
// we copied potentially-refcounted values; make sure to increment
|
||||
const size = new_dict.capacity();
|
||||
var i: usize = 0;
|
||||
|
||||
i = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
switch (new_dict.getSlot(i, key_width, value_width)) {
|
||||
Slot.Filled => {
|
||||
inc_key(new_dict.getKey(i, alignment, key_width, value_width));
|
||||
inc_value(new_dict.getValue(i, alignment, key_width, value_width));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
// NOTE we fuse an increment of all keys/values with a decrement of the input dict
|
||||
decref(allocator, alignment, self.dict_bytes, self.capacity() * slotSize(key_width, value_width));
|
||||
|
||||
return new_dict;
|
||||
}
|
||||
|
@ -412,7 +370,7 @@ pub const RocDict = extern struct {
|
|||
// hash the key, and modulo by the maximum size
|
||||
// (so we get an in-bounds index)
|
||||
const hash = hash_fn(seed, key);
|
||||
const index = hash % current_level_size;
|
||||
const index = capacityOfLevel(current_level - 1) + (hash % current_level_size);
|
||||
|
||||
switch (self.getSlot(index, key_width, value_width)) {
|
||||
Slot.Empty, Slot.PreviouslyFilled => {
|
||||
|
@ -461,20 +419,10 @@ const Inc = fn (?[*]u8) callconv(.C) void;
|
|||
const Dec = fn (?[*]u8) callconv(.C) void;
|
||||
|
||||
// Dict.insert : Dict k v, k, v -> Dict k v
|
||||
pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value: Opaque, value_width: usize, hash_fn: HashFn, is_eq: EqFn, inc_key: Inc, dec_key: Dec, inc_value: Inc, dec_value: Dec, output: *RocDict) callconv(.C) void {
|
||||
pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value: Opaque, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void {
|
||||
var seed: u64 = INITIAL_SEED;
|
||||
|
||||
var result: RocDict = blk: {
|
||||
if (input.isEmpty()) {
|
||||
break :blk input;
|
||||
} else {
|
||||
const in_place = InPlace.Clone;
|
||||
|
||||
var temp = input.makeUnique(std.heap.c_allocator, in_place, alignment, key_width, value_width, inc_key, inc_value);
|
||||
|
||||
break :blk temp;
|
||||
}
|
||||
};
|
||||
var result = input.makeUnique(std.heap.c_allocator, alignment, key_width, value_width);
|
||||
|
||||
var current_level: usize = 1;
|
||||
var current_level_size: usize = 8;
|
||||
|
@ -482,11 +430,12 @@ pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width:
|
|||
|
||||
while (true) {
|
||||
if (current_level > result.number_of_levels) {
|
||||
result = result.reallocate(std.heap.c_allocator, alignment.toUsize(), key_width, value_width);
|
||||
result = result.reallocate(std.heap.c_allocator, alignment, key_width, value_width);
|
||||
}
|
||||
|
||||
const hash = hash_fn(seed, key);
|
||||
const index = hash % current_level_size;
|
||||
const index = capacityOfLevel(current_level - 1) + (hash % current_level_size);
|
||||
assert(index < result.capacity());
|
||||
|
||||
switch (result.getSlot(index, key_width, value_width)) {
|
||||
Slot.Empty, Slot.PreviouslyFilled => {
|
||||
|
@ -528,11 +477,8 @@ pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width:
|
|||
output.* = result;
|
||||
}
|
||||
|
||||
// { ptr, length, level: u8 }
|
||||
// [ key1 .. key8, value1, ...
|
||||
|
||||
// Dict.remove : Dict k v, k -> Dict k v
|
||||
pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, inc_key: Inc, dec_key: Dec, inc_value: Inc, dec_value: Dec, output: *RocDict) callconv(.C) void {
|
||||
pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void {
|
||||
switch (input.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
|
||||
MaybeIndex.not_found => {
|
||||
// the key was not found; we're done
|
||||
|
@ -540,8 +486,7 @@ pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width:
|
|||
return;
|
||||
},
|
||||
MaybeIndex.index => |index| {
|
||||
// TODO make sure input is unique (or duplicate otherwise)
|
||||
var dict = input;
|
||||
var dict = input.makeUnique(std.heap.c_allocator, alignment, key_width, value_width);
|
||||
|
||||
dict.setSlot(index, key_width, value_width, Slot.PreviouslyFilled);
|
||||
const old_key = dict.getKey(index, alignment, key_width, value_width);
|
||||
|
@ -600,6 +545,189 @@ pub fn elementsRc(dict: RocDict, alignment: Alignment, key_width: usize, value_w
|
|||
}
|
||||
}
|
||||
|
||||
pub const RocList = extern struct {
|
||||
bytes: ?[*]u8,
|
||||
length: usize,
|
||||
};
|
||||
|
||||
pub fn dictKeys(dict: RocDict, alignment: Alignment, key_width: usize, value_width: usize, inc_key: Inc, output: *RocList) callconv(.C) void {
|
||||
const size = dict.capacity();
|
||||
|
||||
var length: usize = 0;
|
||||
var i: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
switch (dict.getSlot(i, key_width, value_width)) {
|
||||
Slot.Filled => {
|
||||
length += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
output.* = RocList{ .bytes = null, .length = 0 };
|
||||
return;
|
||||
}
|
||||
|
||||
const data_bytes = length * key_width;
|
||||
var ptr = allocateWithRefcount(std.heap.c_allocator, alignment, data_bytes);
|
||||
|
||||
var offset = blk: {
|
||||
if (alignment.keyFirst()) {
|
||||
break :blk 0;
|
||||
} else {
|
||||
break :blk (dict.capacity() * value_width);
|
||||
}
|
||||
};
|
||||
|
||||
i = 0;
|
||||
var copied: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
switch (dict.getSlot(i, key_width, value_width)) {
|
||||
Slot.Filled => {
|
||||
const key = dict.getKey(i, alignment, key_width, value_width);
|
||||
inc_key(key);
|
||||
|
||||
const key_cast = @ptrCast([*]const u8, key);
|
||||
@memcpy(ptr + (copied * key_width), key_cast, key_width);
|
||||
copied += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
output.* = RocList{ .bytes = ptr, .length = length };
|
||||
}
|
||||
|
||||
pub fn dictValues(dict: RocDict, alignment: Alignment, key_width: usize, value_width: usize, inc_value: Inc, output: *RocList) callconv(.C) void {
|
||||
const size = dict.capacity();
|
||||
|
||||
var length: usize = 0;
|
||||
var i: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
switch (dict.getSlot(i, key_width, value_width)) {
|
||||
Slot.Filled => {
|
||||
length += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
output.* = RocList{ .bytes = null, .length = 0 };
|
||||
return;
|
||||
}
|
||||
|
||||
const data_bytes = length * value_width;
|
||||
var ptr = allocateWithRefcount(std.heap.c_allocator, alignment, data_bytes);
|
||||
|
||||
var offset = blk: {
|
||||
if (alignment.keyFirst()) {
|
||||
break :blk (dict.capacity() * key_width);
|
||||
} else {
|
||||
break :blk 0;
|
||||
}
|
||||
};
|
||||
|
||||
i = 0;
|
||||
var copied: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
switch (dict.getSlot(i, key_width, value_width)) {
|
||||
Slot.Filled => {
|
||||
const value = dict.getValue(i, alignment, key_width, value_width);
|
||||
inc_value(value);
|
||||
|
||||
const value_cast = @ptrCast([*]const u8, value);
|
||||
@memcpy(ptr + (copied * value_width), value_cast, value_width);
|
||||
copied += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
output.* = RocList{ .bytes = ptr, .length = length };
|
||||
}
|
||||
|
||||
fn decref(
|
||||
allocator: *Allocator,
|
||||
alignment: Alignment,
|
||||
bytes_or_null: ?[*]u8,
|
||||
data_bytes: usize,
|
||||
) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
|
||||
const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
|
||||
|
||||
const refcount = (usizes - 1)[0];
|
||||
const refcount_isize = @bitCast(isize, refcount);
|
||||
|
||||
switch (alignment.toUsize()) {
|
||||
8 => {
|
||||
if (refcount == REFCOUNT_ONE) {
|
||||
allocator.free((bytes - 8)[0 .. 8 + data_bytes]);
|
||||
} else if (refcount_isize < 0) {
|
||||
(usizes - 1)[0] = refcount + 1;
|
||||
}
|
||||
},
|
||||
16 => {
|
||||
if (refcount == REFCOUNT_ONE) {
|
||||
allocator.free((bytes - 16)[0 .. 16 + data_bytes]);
|
||||
} else if (refcount_isize < 0) {
|
||||
(usizes - 1)[0] = refcount + 1;
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn allocateWithRefcount(
|
||||
allocator: *Allocator,
|
||||
alignment: Alignment,
|
||||
data_bytes: usize,
|
||||
) [*]u8 {
|
||||
comptime const result_in_place = InPlace.Clone;
|
||||
|
||||
switch (alignment.toUsize()) {
|
||||
8 => {
|
||||
const length = @sizeOf(usize) + data_bytes;
|
||||
|
||||
var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable;
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
if (result_in_place == InPlace.InPlace) {
|
||||
as_usize_array[0] = @intCast(usize, number_of_slots);
|
||||
} else {
|
||||
as_usize_array[0] = REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + @sizeOf(usize);
|
||||
|
||||
return first_slot;
|
||||
},
|
||||
16 => {
|
||||
const length = 2 * @sizeOf(usize) + data_bytes;
|
||||
|
||||
var new_bytes: []align(16) u8 = allocator.alignedAlloc(u8, 16, length) catch unreachable;
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
if (result_in_place == InPlace.InPlace) {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = @intCast(usize, number_of_slots);
|
||||
} else {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + 2 * @sizeOf(usize);
|
||||
|
||||
return first_slot;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "RocDict.init() contains nothing" {
|
||||
const key_size = @sizeOf(usize);
|
||||
const value_size = @sizeOf(usize);
|
||||
|
|
|
@ -14,6 +14,8 @@ comptime {
|
|||
exportDictFn(dict.dictContains, "contains");
|
||||
exportDictFn(dict.dictGet, "get");
|
||||
exportDictFn(dict.elementsRc, "elementsRc");
|
||||
exportDictFn(dict.dictKeys, "keys");
|
||||
exportDictFn(dict.dictValues, "values");
|
||||
|
||||
exportDictFn(hash.wyhash, "hash");
|
||||
exportDictFn(hash.wyhash_rocstr, "hash_str");
|
||||
|
|
|
@ -44,3 +44,5 @@ pub const DICT_REMOVE: &str = "roc_builtins.dict.remove";
|
|||
pub const DICT_CONTAINS: &str = "roc_builtins.dict.contains";
|
||||
pub const DICT_GET: &str = "roc_builtins.dict.get";
|
||||
pub const DICT_ELEMENTS_RC: &str = "roc_builtins.dict.elementsRc";
|
||||
pub const DICT_KEYS: &str = "roc_builtins.dict.keys";
|
||||
pub const DICT_VALUES: &str = "roc_builtins.dict.values";
|
||||
|
|
|
@ -1990,7 +1990,6 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let arg_key = Symbol::ARG_2;
|
||||
|
||||
let temp_record = Symbol::ARG_3;
|
||||
let temp_flag = Symbol::ARG_4;
|
||||
|
||||
let bool_var = var_store.fresh();
|
||||
let flag_var = var_store.fresh();
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::llvm::build::{
|
|||
call_bitcode_fn, call_void_bitcode_fn, complex_bitcast, load_symbol, load_symbol_and_layout,
|
||||
set_name, Env, Scope,
|
||||
};
|
||||
use crate::llvm::convert::{as_const_zero, basic_type_from_layout};
|
||||
use crate::llvm::convert::{as_const_zero, basic_type_from_layout, collection};
|
||||
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout, Mode};
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
use inkwell::types::BasicType;
|
||||
|
@ -128,10 +128,7 @@ pub fn dict_insert<'a, 'ctx, 'env>(
|
|||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
|
||||
let inc_key_fn = build_rc_wrapper(env, layout_ids, key_layout, Mode::Inc(1));
|
||||
let dec_key_fn = build_rc_wrapper(env, layout_ids, key_layout, Mode::Dec);
|
||||
|
||||
let inc_value_fn = build_rc_wrapper(env, layout_ids, value_layout, Mode::Inc(1));
|
||||
let dec_value_fn = build_rc_wrapper(env, layout_ids, value_layout, Mode::Dec);
|
||||
|
||||
call_void_bitcode_fn(
|
||||
|
@ -145,9 +142,7 @@ pub fn dict_insert<'a, 'ctx, 'env>(
|
|||
value_width.into(),
|
||||
hash_fn.as_global_value().as_pointer_value().into(),
|
||||
eq_fn.as_global_value().as_pointer_value().into(),
|
||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_value_fn.as_global_value().as_pointer_value().into(),
|
||||
result_ptr.into(),
|
||||
],
|
||||
|
@ -200,10 +195,7 @@ pub fn dict_remove<'a, 'ctx, 'env>(
|
|||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
|
||||
let inc_key_fn = build_rc_wrapper(env, layout_ids, key_layout, Mode::Inc(1));
|
||||
let dec_key_fn = build_rc_wrapper(env, layout_ids, key_layout, Mode::Dec);
|
||||
|
||||
let inc_value_fn = build_rc_wrapper(env, layout_ids, value_layout, Mode::Inc(1));
|
||||
let dec_value_fn = build_rc_wrapper(env, layout_ids, value_layout, Mode::Dec);
|
||||
|
||||
call_void_bitcode_fn(
|
||||
|
@ -216,9 +208,7 @@ pub fn dict_remove<'a, 'ctx, 'env>(
|
|||
value_width.into(),
|
||||
hash_fn.as_global_value().as_pointer_value().into(),
|
||||
eq_fn.as_global_value().as_pointer_value().into(),
|
||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_value_fn.as_global_value().as_pointer_value().into(),
|
||||
result_ptr.into(),
|
||||
],
|
||||
|
@ -451,7 +441,53 @@ pub fn dict_keys<'a, 'ctx, 'env>(
|
|||
key_layout: &Layout<'a>,
|
||||
value_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
todo!()
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let zig_list_type = env.module.get_struct_type("dict.RocList").unwrap();
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder
|
||||
.build_store(dict_ptr, struct_to_zig_dict(env, dict.into_struct_value()));
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
|
||||
let inc_key_fn = build_rc_wrapper(env, layout_ids, key_layout, Mode::Inc(1));
|
||||
|
||||
let list_ptr = builder.build_alloca(zig_list_type, "list_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
||||
list_ptr.into(),
|
||||
],
|
||||
&bitcode::DICT_KEYS,
|
||||
);
|
||||
|
||||
let list_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
list_ptr,
|
||||
collection(env.context, env.ptr_bytes).ptr_type(AddressSpace::Generic),
|
||||
"to_roc_list",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder.build_load(list_ptr, "load_keys_list")
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -462,7 +498,53 @@ pub fn dict_values<'a, 'ctx, 'env>(
|
|||
key_layout: &Layout<'a>,
|
||||
value_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
todo!()
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let zig_list_type = env.module.get_struct_type("dict.RocList").unwrap();
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder
|
||||
.build_store(dict_ptr, struct_to_zig_dict(env, dict.into_struct_value()));
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let value_width = env
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
|
||||
let inc_value_fn = build_rc_wrapper(env, layout_ids, value_layout, Mode::Inc(1));
|
||||
|
||||
let list_ptr = builder.build_alloca(zig_list_type, "list_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
||||
list_ptr.into(),
|
||||
],
|
||||
&bitcode::DICT_VALUES,
|
||||
);
|
||||
|
||||
let list_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
list_ptr,
|
||||
collection(env.context, env.ptr_bytes).ptr_type(AddressSpace::Generic),
|
||||
"to_roc_list",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder.build_load(list_ptr, "load_keys_list")
|
||||
}
|
||||
|
||||
fn build_hash_wrapper<'a, 'ctx, 'env>(
|
||||
|
|
|
@ -149,4 +149,88 @@ mod gen_dict {
|
|||
f64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keys() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
myDict : Dict I64 I64
|
||||
myDict =
|
||||
Dict.empty
|
||||
|> Dict.insert 0 100
|
||||
|> Dict.insert 1 100
|
||||
|> Dict.insert 2 100
|
||||
|
||||
|
||||
Dict.keys myDict
|
||||
"#
|
||||
),
|
||||
&[0, 1, 2],
|
||||
&[i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn values() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
myDict : Dict I64 I64
|
||||
myDict =
|
||||
Dict.empty
|
||||
|> Dict.insert 0 100
|
||||
|> Dict.insert 1 200
|
||||
|> Dict.insert 2 300
|
||||
|
||||
|
||||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[100, 200, 300],
|
||||
&[i64]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_list_with_fold() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
myDict : Dict I64 I64
|
||||
myDict =
|
||||
[1,2,3]
|
||||
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
||||
|
||||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[2, 3, 1],
|
||||
&[i64]
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
range : I64, I64, List I64-> List I64
|
||||
range = \low, high, accum ->
|
||||
if low < high then
|
||||
range (low + 1) high (List.append accum low)
|
||||
else
|
||||
accum
|
||||
|
||||
myDict : Dict I64 I64
|
||||
myDict =
|
||||
# 25 elements (8 + 16 + 1) is guaranteed to overflow/reallocate at least twice
|
||||
range 0 25 []
|
||||
|> List.walk (\value, accum -> Dict.insert accum value value) Dict.empty
|
||||
|
||||
Dict.values myDict
|
||||
|> List.len
|
||||
"#
|
||||
),
|
||||
25,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ pub fn helper<'a>(
|
|||
mode,
|
||||
);
|
||||
|
||||
// fn_val.print_to_stderr();
|
||||
fn_val.print_to_stderr();
|
||||
// module.print_to_stderr();
|
||||
|
||||
panic!(
|
||||
|
|
|
@ -578,7 +578,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
||||
ListRepeat => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
ListReverse => arena.alloc_slice_copy(&[owned]),
|
||||
ListAppend => arena.alloc_slice_copy(&[owned, owned]),
|
||||
ListPrepend => arena.alloc_slice_copy(&[owned, owned]),
|
||||
StrJoinWith => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
||||
|
@ -589,6 +588,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
ListWalkBackwards => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
||||
|
||||
// TODO when we have lists with capacity (if ever)
|
||||
// List.append should own its first argument
|
||||
ListAppend => arena.alloc_slice_copy(&[borrowed, owned]),
|
||||
|
||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue