remove dict/hash stuff from the zig builtins

This commit is contained in:
Folkert 2022-07-13 11:59:46 +02:00
parent 4d55b756bb
commit 6c26d8812f
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
8 changed files with 3 additions and 1737 deletions

View file

@ -1,815 +0,0 @@
const std = @import("std");
const testing = std.testing;
const expectEqual = testing.expectEqual;
const mem = std.mem;
const assert = std.debug.assert;
const utils = @import("utils.zig");
const RocList = @import("list.zig").RocList;
const INITIAL_SEED = 0xc70f6907;
const InPlace = enum(u8) {
InPlace,
Clone,
};
const Slot = enum(u8) {
Empty,
Filled,
PreviouslyFilled,
};
const MaybeIndexTag = enum { index, not_found };
const MaybeIndex = union(MaybeIndexTag) { index: usize, not_found: void };
fn nextSeed(seed: u64) u64 {
// TODO is this a valid way to get a new seed? are there better ways?
return seed + 1;
}
fn totalCapacityAtLevel(input: usize) usize {
if (input == 0) {
return 0;
}
var n = input;
var slots: usize = 8;
while (n > 1) : (n -= 1) {
slots = slots * 2 + slots;
}
return slots;
}
fn capacityOfLevel(input: usize) usize {
if (input == 0) {
return 0;
}
var n = input;
var slots: usize = 8;
while (n > 1) : (n -= 1) {
slots = slots * 2;
}
return slots;
}
// aligmnent of elements. The number (16 or 8) indicates the maximum
// alignment of the key and value. The tag furthermore indicates
// which has the biggest aligmnent. If both are the same, we put
// the key first
const Alignment = extern struct {
bits: u8,
const VALUE_BEFORE_KEY_FLAG: u8 = 0b1000_0000;
fn toU32(self: Alignment) u32 {
if (self.bits >= VALUE_BEFORE_KEY_FLAG) {
return self.bits ^ Alignment.VALUE_BEFORE_KEY_FLAG;
} else {
return self.bits;
}
}
fn keyFirst(self: Alignment) bool {
if (self.bits & Alignment.VALUE_BEFORE_KEY_FLAG > 0) {
return false;
} else {
return true;
}
}
};
pub fn decref(
bytes_or_null: ?[*]u8,
data_bytes: usize,
alignment: Alignment,
) void {
return utils.decref(bytes_or_null, data_bytes, alignment.toU32());
}
pub fn allocateWithRefcount(
data_bytes: usize,
alignment: Alignment,
) [*]u8 {
return utils.allocateWithRefcount(data_bytes, alignment.toU32());
}
pub const RocDict = extern struct {
dict_bytes: ?[*]u8,
dict_entries_len: usize,
number_of_levels: usize,
pub fn empty() RocDict {
return RocDict{
.dict_entries_len = 0,
.number_of_levels = 0,
.dict_bytes = null,
};
}
pub fn allocate(
number_of_levels: usize,
number_of_entries: usize,
alignment: Alignment,
key_size: usize,
value_size: usize,
) RocDict {
const number_of_slots = totalCapacityAtLevel(number_of_levels);
const slot_size = slotSize(key_size, value_size);
const data_bytes = number_of_slots * slot_size;
return RocDict{
.dict_bytes = allocateWithRefcount(data_bytes, alignment),
.number_of_levels = number_of_levels,
.dict_entries_len = number_of_entries,
};
}
pub fn reallocate(
self: RocDict,
alignment: Alignment,
key_width: usize,
value_width: usize,
) RocDict {
const new_level = self.number_of_levels + 1;
const slot_size = slotSize(key_width, value_width);
const old_capacity = self.capacity();
const new_capacity = totalCapacityAtLevel(new_level);
const delta_capacity = new_capacity - old_capacity;
const data_bytes = new_capacity * slot_size;
const first_slot = allocateWithRefcount(data_bytes, alignment);
// transfer the memory
if (self.dict_bytes) |source_ptr| {
const dest_ptr = first_slot;
var source_offset: usize = 0;
var dest_offset: usize = 0;
if (alignment.keyFirst()) {
// copy keys
@memcpy(dest_ptr + dest_offset, source_ptr + source_offset, old_capacity * key_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 = first_slot + 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);
}
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(self.dict_bytes, self.capacity() * slotSize(key_width, value_width), alignment);
return result;
}
pub fn asU8ptr(self: RocDict) [*]u8 {
return @ptrCast([*]u8, self.dict_bytes);
}
pub fn len(self: RocDict) usize {
return self.dict_entries_len;
}
pub fn isEmpty(self: RocDict) bool {
return self.len() == 0;
}
pub fn isUnique(self: RocDict) bool {
// the empty dict is unique (in the sense that copying it will not leak memory)
if (self.isEmpty()) {
return true;
}
// otherwise, check if the refcount is one
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(@alignOf(usize), self.dict_bytes));
return (ptr - 1)[0] == utils.REFCOUNT_ONE;
}
pub fn capacity(self: RocDict) usize {
return totalCapacityAtLevel(self.number_of_levels);
}
pub fn makeUnique(self: RocDict, alignment: Alignment, key_width: usize, value_width: usize) RocDict {
if (self.isEmpty()) {
return self;
}
if (self.isUnique()) {
return self;
}
// unfortunately, we have to clone
var new_dict = RocDict.allocate(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 = self.capacity() * (@sizeOf(Slot) + key_width + value_width);
@memcpy(new_bytes, old_bytes, number_of_bytes);
// NOTE we fuse an increment of all keys/values with a decrement of the input dict
const data_bytes = self.capacity() * slotSize(key_width, value_width);
decref(self.dict_bytes, data_bytes, alignment);
return new_dict;
}
fn getSlot(self: *const RocDict, index: usize, key_width: usize, value_width: usize) Slot {
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
const ptr = self.dict_bytes orelse unreachable;
return @intToEnum(Slot, ptr[offset]);
}
fn setSlot(self: *RocDict, index: usize, key_width: usize, value_width: usize, slot: Slot) void {
const offset = self.capacity() * (key_width + value_width) + index * @sizeOf(Slot);
const ptr = self.dict_bytes orelse unreachable;
ptr[offset] = @enumToInt(slot);
}
fn setKey(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
if (key_width == 0) {
return;
}
const offset = blk: {
if (alignment.keyFirst()) {
break :blk (index * key_width);
} else {
break :blk (self.capacity() * value_width) + (index * key_width);
}
};
const ptr = self.dict_bytes orelse unreachable;
const source = data orelse unreachable;
const dest = ptr + offset;
@memcpy(dest, source, key_width);
}
fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
if (key_width == 0) {
return null;
}
const offset = blk: {
if (alignment.keyFirst()) {
break :blk (index * key_width);
} else {
break :blk (self.capacity() * value_width) + (index * key_width);
}
};
const ptr = self.dict_bytes orelse unreachable;
return ptr + offset;
}
fn setValue(self: *RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize, data: Opaque) void {
if (value_width == 0) {
return;
}
const offset = blk: {
if (alignment.keyFirst()) {
break :blk (self.capacity() * key_width) + (index * value_width);
} else {
break :blk (index * value_width);
}
};
const ptr = self.dict_bytes orelse unreachable;
const source = data orelse unreachable;
const dest = ptr + offset;
@memcpy(dest, source, value_width);
}
fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
if (value_width == 0) {
return null;
}
const offset = blk: {
if (alignment.keyFirst()) {
break :blk (self.capacity() * key_width) + (index * value_width);
} else {
break :blk (index * value_width);
}
};
const ptr = self.dict_bytes orelse unreachable;
return ptr + offset;
}
fn findIndex(self: *const RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn) MaybeIndex {
if (self.isEmpty()) {
return MaybeIndex.not_found;
}
var seed: u64 = INITIAL_SEED;
var current_level: usize = 1;
var current_level_size: usize = 8;
var next_level_size: usize = 2 * current_level_size;
while (true) {
if (current_level > self.number_of_levels) {
return MaybeIndex.not_found;
}
// hash the key, and modulo by the maximum size
// (so we get an in-bounds index)
const hash = hash_fn(seed, key);
const index = capacityOfLevel(current_level - 1) + @intCast(usize, (hash % current_level_size));
switch (self.getSlot(index, key_width, value_width)) {
Slot.Empty, Slot.PreviouslyFilled => {
return MaybeIndex.not_found;
},
Slot.Filled => {
// is this the same key, or a new key?
const current_key = self.getKey(index, alignment, key_width, value_width);
if (is_eq(key, current_key)) {
return MaybeIndex{ .index = index };
} else {
current_level += 1;
current_level_size *= 2;
next_level_size *= 2;
seed = nextSeed(seed);
continue;
}
},
}
}
}
};
// Dict.empty
pub fn dictEmpty(dict: *RocDict) callconv(.C) void {
dict.* = RocDict.empty();
}
pub fn slotSize(key_size: usize, value_size: usize) usize {
return @sizeOf(Slot) + key_size + value_size;
}
// Dict.len
pub fn dictLen(dict: RocDict) callconv(.C) usize {
return dict.dict_entries_len;
}
// commonly used type aliases
const Opaque = ?[*]u8;
const HashFn = fn (u64, ?[*]u8) callconv(.C) u64;
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
const Inc = fn (?[*]u8) callconv(.C) void;
const IncN = fn (?[*]u8, usize) callconv(.C) void;
const Dec = fn (?[*]u8) callconv(.C) void;
const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]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,
dec_key: Dec,
dec_value: Dec,
output: *RocDict,
) callconv(.C) void {
var seed: u64 = INITIAL_SEED;
var result = input.makeUnique(alignment, key_width, value_width);
var current_level: usize = 1;
var current_level_size: usize = 8;
var next_level_size: usize = 2 * current_level_size;
while (true) {
if (current_level > result.number_of_levels) {
result = result.reallocate(alignment, key_width, value_width);
}
const hash = hash_fn(seed, key);
const index = capacityOfLevel(current_level - 1) + @intCast(usize, (hash % current_level_size));
assert(index < result.capacity());
switch (result.getSlot(index, key_width, value_width)) {
Slot.Empty, Slot.PreviouslyFilled => {
result.setSlot(index, key_width, value_width, Slot.Filled);
result.setKey(index, alignment, key_width, value_width, key);
result.setValue(index, alignment, key_width, value_width, value);
result.dict_entries_len += 1;
break;
},
Slot.Filled => {
// is this the same key, or a new key?
const current_key = result.getKey(index, alignment, key_width, value_width);
if (is_eq(key, current_key)) {
// we will override the old value, but first have to decrement its refcount
const current_value = result.getValue(index, alignment, key_width, value_width);
dec_value(current_value);
// we must consume the key argument!
dec_key(key);
result.setValue(index, alignment, key_width, value_width, value);
break;
} else {
seed = nextSeed(seed);
current_level += 1;
current_level_size *= 2;
next_level_size *= 2;
continue;
}
},
}
}
// write result into pointer
output.* = result;
}
// 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, 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
output.* = input;
return;
},
MaybeIndex.index => |index| {
var dict = input.makeUnique(alignment, key_width, value_width);
assert(index < dict.capacity());
dict.setSlot(index, key_width, value_width, Slot.PreviouslyFilled);
const old_key = dict.getKey(index, alignment, key_width, value_width);
const old_value = dict.getValue(index, alignment, key_width, value_width);
dec_key(old_key);
dec_value(old_value);
dict.dict_entries_len -= 1;
// if the dict is now completely empty, free its allocation
if (dict.dict_entries_len == 0) {
const data_bytes = dict.capacity() * slotSize(key_width, value_width);
decref(dict.dict_bytes, data_bytes, alignment);
output.* = RocDict.empty();
return;
}
output.* = dict;
},
}
}
// Dict.contains : Dict k v, k -> Bool
pub fn dictContains(dict: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn) callconv(.C) bool {
switch (dict.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
MaybeIndex.not_found => {
return false;
},
MaybeIndex.index => |_| {
return true;
},
}
}
// Dict.get : Dict k v, k -> { flag: bool, value: Opaque }
pub fn dictGet(dict: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, inc_value: Inc) callconv(.C) extern struct { value: Opaque, flag: bool } {
switch (dict.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
MaybeIndex.not_found => {
return .{ .flag = false, .value = null };
},
MaybeIndex.index => |index| {
var value = dict.getValue(index, alignment, key_width, value_width);
inc_value(value);
return .{ .flag = true, .value = value };
},
}
}
// Dict.elementsRc
// increment or decrement all dict elements (but not the dict's allocation itself)
pub fn elementsRc(dict: RocDict, alignment: Alignment, key_width: usize, value_width: usize, modify_key: Inc, modify_value: Inc) callconv(.C) void {
const size = dict.capacity();
var i: usize = 0;
while (i < size) : (i += 1) {
switch (dict.getSlot(i, key_width, value_width)) {
Slot.Filled => {
modify_key(dict.getKey(i, alignment, key_width, value_width));
modify_value(dict.getValue(i, alignment, key_width, value_width));
},
else => {},
}
}
}
pub fn dictKeys(
dict: RocDict,
alignment: Alignment,
key_width: usize,
value_width: usize,
inc_key: Inc,
) callconv(.C) RocList {
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) {
return RocList.empty();
}
const data_bytes = length * key_width;
var ptr = allocateWithRefcount(data_bytes, alignment);
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 => {},
}
}
return RocList{ .bytes = ptr, .length = length, .capacity = length };
}
pub fn dictValues(
dict: RocDict,
alignment: Alignment,
key_width: usize,
value_width: usize,
inc_value: Inc,
) callconv(.C) RocList {
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) {
return RocList.empty();
}
const data_bytes = length * value_width;
var ptr = allocateWithRefcount(data_bytes, alignment);
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 => {},
}
}
return RocList{ .bytes = ptr, .length = length, .capacity = length };
}
fn doNothing(_: Opaque) callconv(.C) void {
return;
}
pub fn dictUnion(
dict1: RocDict,
dict2: RocDict,
alignment: Alignment,
key_width: usize,
value_width: usize,
hash_fn: HashFn,
is_eq: EqFn,
inc_key: Inc,
inc_value: Inc,
output: *RocDict,
) callconv(.C) void {
output.* = dict1.makeUnique(alignment, key_width, value_width);
var i: usize = 0;
while (i < dict2.capacity()) : (i += 1) {
switch (dict2.getSlot(i, key_width, value_width)) {
Slot.Filled => {
const key = dict2.getKey(i, alignment, key_width, value_width);
switch (output.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
MaybeIndex.not_found => {
const value = dict2.getValue(i, alignment, key_width, value_width);
inc_value(value);
// we need an extra RC token for the key
inc_key(key);
inc_value(value);
// we know the newly added key is not a duplicate, so the `dec`s are unreachable
const dec_key = doNothing;
const dec_value = doNothing;
dictInsert(output.*, alignment, key, key_width, value, value_width, hash_fn, is_eq, dec_key, dec_value, output);
},
MaybeIndex.index => |_| {
// the key is already in the output dict
continue;
},
}
},
else => {},
}
}
}
pub fn dictIntersection(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Inc, dec_value: Inc, output: *RocDict) callconv(.C) void {
output.* = dict1.makeUnique(alignment, key_width, value_width);
var i: usize = 0;
const size = dict1.capacity();
while (i < size) : (i += 1) {
switch (output.getSlot(i, key_width, value_width)) {
Slot.Filled => {
const key = dict1.getKey(i, alignment, key_width, value_width);
switch (dict2.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
MaybeIndex.not_found => {
dictRemove(output.*, alignment, key, key_width, value_width, hash_fn, is_eq, dec_key, dec_value, output);
},
MaybeIndex.index => |_| {
// keep this key/value
continue;
},
}
},
else => {},
}
}
}
pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void {
output.* = dict1.makeUnique(alignment, key_width, value_width);
var i: usize = 0;
const size = dict1.capacity();
while (i < size) : (i += 1) {
switch (output.getSlot(i, key_width, value_width)) {
Slot.Filled => {
const key = dict1.getKey(i, alignment, key_width, value_width);
switch (dict2.findIndex(alignment, key, key_width, value_width, hash_fn, is_eq)) {
MaybeIndex.not_found => {
// keep this key/value
continue;
},
MaybeIndex.index => |_| {
dictRemove(output.*, alignment, key, key_width, value_width, hash_fn, is_eq, dec_key, dec_value, output);
},
}
},
else => {},
}
}
}
pub fn setFromList(list: RocList, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, output: *RocDict) callconv(.C) void {
output.* = RocDict.empty();
var ptr = @ptrCast([*]u8, list.bytes);
const dec_value = doNothing;
const value = null;
const size = list.length;
var i: usize = 0;
while (i < size) : (i += 1) {
const key = ptr + i * key_width;
dictInsert(output.*, alignment, key, key_width, value, value_width, hash_fn, is_eq, dec_key, dec_value, output);
}
// NOTE: decref checks for the empty case
const data_bytes = size * key_width;
decref(list.bytes, data_bytes, alignment);
}
pub fn dictWalk(
dict: RocDict,
caller: Caller3,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
accum: Opaque,
alignment: Alignment,
key_width: usize,
value_width: usize,
accum_width: usize,
output: Opaque,
) callconv(.C) void {
const alignment_u32 = alignment.toU32();
// allocate space to write the result of the stepper into
// experimentally aliasing the accum and output pointers is not a good idea
// TODO handle alloc failing!
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment_u32) orelse unreachable;
var b1 = output orelse unreachable;
var b2 = bytes_ptr;
if (data_is_owned) {
inc_n_data(data, dict.len());
}
@memcpy(b2, accum orelse unreachable, accum_width);
var i: usize = 0;
const size = dict.capacity();
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);
const value = dict.getValue(i, alignment, key_width, value_width);
caller(data, b2, key, value, b1);
std.mem.swap([*]u8, &b1, &b2);
},
else => {},
}
}
@memcpy(output orelse unreachable, b2, accum_width);
utils.dealloc(bytes_ptr, alignment_u32);
}

View file

@ -53,31 +53,6 @@ comptime {
exportListFn(list.listIsUnique, "is_unique");
}
// Dict Module
const dict = @import("dict.zig");
const hash = @import("hash.zig");
comptime {
exportDictFn(dict.dictLen, "len");
exportDictFn(dict.dictEmpty, "empty");
exportDictFn(dict.dictInsert, "insert");
exportDictFn(dict.dictRemove, "remove");
exportDictFn(dict.dictContains, "contains");
exportDictFn(dict.dictGet, "get");
exportDictFn(dict.elementsRc, "elementsRc");
exportDictFn(dict.dictKeys, "keys");
exportDictFn(dict.dictValues, "values");
exportDictFn(dict.dictUnion, "union");
exportDictFn(dict.dictIntersection, "intersection");
exportDictFn(dict.dictDifference, "difference");
exportDictFn(dict.dictWalk, "walk");
exportDictFn(dict.setFromList, "set_from_list");
exportDictFn(hash.wyhash, "hash");
exportDictFn(hash.wyhash_rocstr, "hash_str");
}
// Num Module
const num = @import("num.zig");

View file

@ -336,24 +336,6 @@ pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe";
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
pub const DICT_LEN: &str = "roc_builtins.dict.len";
pub const DICT_EMPTY: &str = "roc_builtins.dict.empty";
pub const DICT_INSERT: &str = "roc_builtins.dict.insert";
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";
pub const DICT_UNION: &str = "roc_builtins.dict.union";
pub const DICT_DIFFERENCE: &str = "roc_builtins.dict.difference";
pub const DICT_INTERSECTION: &str = "roc_builtins.dict.intersection";
pub const DICT_WALK: &str = "roc_builtins.dict.walk";
pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list";
pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
pub const LIST_MAP3: &str = "roc_builtins.list.map3";

View file

@ -2,7 +2,6 @@ use crate::llvm::bitcode::{
call_bitcode_fn, call_bitcode_fn_fixing_for_convention, call_list_bitcode_fn,
call_str_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
self, allocate_list, empty_polymorphic_list, list_append_unsafe, list_concat, list_drop_at,
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_prepend,
@ -6066,13 +6065,7 @@ fn run_low_level<'a, 'ctx, 'env>(
BasicValueEnum::IntValue(bool_val)
}
Hash => {
debug_assert_eq!(args.len(), 2);
let seed = load_symbol(scope, &args[0]);
let (value, layout) = load_symbol_and_layout(scope, &args[1]);
debug_assert!(seed.is_int_value());
generic_hash(env, layout_ids, seed.into_int_value(), value, layout).into()
unimplemented!()
}
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => {

View file

@ -1,868 +0,0 @@
use crate::debug_info_init;
use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::tag_pointer_clear_tag_id;
use crate::llvm::build::Env;
use crate::llvm::build::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec;
use inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
};
use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::use_roc_value;
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
Loop(UnionLayout<'a>),
}
pub fn generic_hash<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
seed: IntValue<'ctx>,
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
) -> IntValue<'ctx> {
// NOTE: C and Zig use this value for their initial HashMap seed: 0xc70f6907
build_hash_layout(
env,
layout_ids,
seed,
val,
layout,
WhenRecursive::Unreachable,
)
}
fn build_hash_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
seed: IntValue<'ctx>,
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
when_recursive: WhenRecursive<'a>,
) -> IntValue<'ctx> {
match layout {
Layout::Builtin(builtin) => {
hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive)
}
Layout::Struct { field_layouts, .. } => build_hash_struct(
env,
layout_ids,
field_layouts,
when_recursive,
seed,
val.into_struct_value(),
),
Layout::LambdaSet(lambda_set) => build_hash_layout(
env,
layout_ids,
seed,
val,
&lambda_set.runtime_representation(),
when_recursive,
),
Layout::Union(union_layout) => build_hash_tag(env, layout_ids, union_layout, seed, val),
Layout::Boxed(_inner_layout) => {
// build_hash_box(env, layout_ids, layout, inner_layout, seed, val)
todo!()
}
Layout::RecursivePointer => match when_recursive {
WhenRecursive::Unreachable => {
unreachable!("recursion pointers should never be hashed directly")
}
WhenRecursive::Loop(union_layout) => {
let layout = Layout::Union(union_layout);
let bt = basic_type_from_layout(env, &layout);
// cast the i64 pointer to a pointer to block of memory
let field_cast = env
.builder
.build_bitcast(val, bt, "i64_to_opaque")
.into_pointer_value();
build_hash_tag(env, layout_ids, &union_layout, seed, field_cast.into())
}
},
}
}
fn append_hash_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
seed: IntValue<'ctx>,
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
when_recursive: WhenRecursive<'a>,
) -> IntValue<'ctx> {
build_hash_layout(env, layout_ids, seed, val, layout, when_recursive)
}
fn hash_builtin<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
seed: IntValue<'ctx>,
val: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
builtin: &Builtin<'a>,
when_recursive: WhenRecursive<'a>,
) -> IntValue<'ctx> {
let ptr_bytes = env.target_info;
match builtin {
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
let hash_bytes = store_and_use_as_u8_ptr(env, val, layout);
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
}
Builtin::Str => {
// let zig deal with big vs small string
call_bitcode_fn(env, &[seed.into(), val], bitcode::DICT_HASH_STR).into_int_value()
}
Builtin::List(element_layout) => build_hash_list(
env,
layout_ids,
layout,
element_layout,
when_recursive,
seed,
val.into_struct_value(),
),
}
}
fn build_hash_struct<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
field_layouts: &'a [Layout<'a>],
when_recursive: WhenRecursive<'a>,
seed: IntValue<'ctx>,
value: StructValue<'ctx>,
) -> IntValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
.get(symbol, &struct_layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let seed_type = env.context.i64_type();
let arg_type = basic_type_from_layout(env, &struct_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
seed_type.into(),
&[seed_type.into(), arg_type],
);
build_hash_struct_help(
env,
layout_ids,
function_value,
when_recursive,
field_layouts,
);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
let call = env
.builder
.build_call(function, &[seed.into(), value.into()], "struct_hash");
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap().into_int_value()
}
fn build_hash_struct_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
parent: FunctionValue<'ctx>,
when_recursive: WhenRecursive<'a>,
field_layouts: &[Layout<'a>],
) {
let ctx = env.context;
debug_info_init!(env, parent);
// Add args to scope
let mut it = parent.get_param_iter();
let seed = it.next().unwrap().into_int_value();
let value = it.next().unwrap().into_struct_value();
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
value.set_name(Symbol::ARG_2.as_str(&env.interns));
let entry = ctx.append_basic_block(parent, "entry");
env.builder.position_at_end(entry);
let result = hash_struct(env, layout_ids, seed, value, when_recursive, field_layouts);
env.builder.build_return(Some(&result));
}
fn hash_struct<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mut seed: IntValue<'ctx>,
value: StructValue<'ctx>,
when_recursive: WhenRecursive<'a>,
field_layouts: &[Layout<'a>],
) -> IntValue<'ctx> {
let ptr_bytes = env.target_info;
let layout = Layout::struct_no_name_order(field_layouts);
// Optimization: if the bit representation of equal values is the same
// just hash the bits. Caveat here is tags: e.g. `Nothing` in `Just a`
// contains garbage bits after the tag (currently)
if false {
// this is a struct of only basic types, so we can just hash its bits
let hash_bytes = store_and_use_as_u8_ptr(env, value.into(), &layout);
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
} else {
for (index, field_layout) in field_layouts.iter().enumerate() {
let field = env
.builder
.build_extract_value(value, index as u32, "hash_field")
.unwrap();
let field = use_roc_value(env, *field_layout, field, "store_field_for_hashing");
if let Layout::RecursivePointer = field_layout {
match &when_recursive {
WhenRecursive::Unreachable => {
unreachable!("The current layout should not be recursive, but is")
}
WhenRecursive::Loop(union_layout) => {
let field_layout = Layout::Union(*union_layout);
let bt = basic_type_from_layout(env, &field_layout);
// cast the i64 pointer to a pointer to block of memory
let field_cast = env
.builder
.build_bitcast(field, bt, "i64_to_opaque")
.into_pointer_value();
seed = append_hash_layout(
env,
layout_ids,
seed,
field_cast.into(),
&field_layout,
when_recursive.clone(),
)
}
}
} else {
seed = append_hash_layout(
env,
layout_ids,
seed,
field,
field_layout,
when_recursive.clone(),
);
}
}
seed
}
}
fn build_hash_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
union_layout: &UnionLayout<'a>,
seed: IntValue<'ctx>,
value: BasicValueEnum<'ctx>,
) -> IntValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
.get(symbol, &Layout::Union(*union_layout))
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let seed_type = env.context.i64_type();
let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
seed_type.into(),
&[seed_type.into(), arg_type],
);
build_hash_tag_help(env, layout_ids, function_value, union_layout);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
let call = env
.builder
.build_call(function, &[seed.into(), value.into()], "struct_hash");
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap().into_int_value()
}
fn build_hash_tag_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
parent: FunctionValue<'ctx>,
union_layout: &UnionLayout<'a>,
) {
let ctx = env.context;
debug_info_init!(env, parent);
// Add args to scope
let mut it = parent.get_param_iter();
let seed = it.next().unwrap().into_int_value();
let value = it.next().unwrap();
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
value.set_name(Symbol::ARG_2.as_str(&env.interns));
let entry = ctx.append_basic_block(parent, "entry");
env.builder.position_at_end(entry);
let result = hash_tag(env, layout_ids, parent, seed, value, union_layout);
env.builder.build_return(Some(&result));
}
fn hash_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
parent: FunctionValue<'ctx>,
seed: IntValue<'ctx>,
tag: BasicValueEnum<'ctx>,
union_layout: &UnionLayout<'a>,
) -> IntValue<'ctx> {
use UnionLayout::*;
let entry_block = env.builder.get_insert_block().unwrap();
let merge_block = env.context.append_basic_block(parent, "merge_block");
env.builder.position_at_end(merge_block);
let tag_id_layout = union_layout.tag_id_layout();
let tag_id_basic_type = basic_type_from_layout(env, &tag_id_layout);
let merge_phi = env.builder.build_phi(seed.get_type(), "merge_hash");
env.builder.position_at_end(entry_block);
match union_layout {
NonRecursive(tags) => {
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
for (tag_id, field_layouts) in tags.iter().enumerate() {
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
// hash the tag id
let hash_bytes = store_and_use_as_u8_ptr(
env,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.target_info),
);
// hash the tag data
let tag = tag.into_pointer_value();
let answer =
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
merge_phi.add_incoming(&[(&answer, block)]);
env.builder.build_unconditional_branch(merge_block);
cases.push((
current_tag_id.get_type().const_int(tag_id as u64, false),
block,
));
}
env.builder.position_at_end(entry_block);
match cases.pop() {
Some((_, default)) => {
env.builder.build_switch(current_tag_id, default, &cases);
}
None => {
// we're hashing empty tag unions; this code is effectively unreachable
env.builder.build_unreachable();
}
}
}
Recursive(tags) => {
let current_tag_id = get_tag_id(env, parent, union_layout, tag);
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
for (tag_id, field_layouts) in tags.iter().enumerate() {
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
// hash the tag id
let hash_bytes = store_and_use_as_u8_ptr(
env,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.target_info),
);
// hash the tag data
let tag = tag_pointer_clear_tag_id(env, tag.into_pointer_value());
let answer =
hash_ptr_to_struct(env, layout_ids, union_layout, field_layouts, seed, tag);
merge_phi.add_incoming(&[(&answer, block)]);
env.builder.build_unconditional_branch(merge_block);
cases.push((
current_tag_id.get_type().const_int(tag_id as u64, false),
block,
));
}
env.builder.position_at_end(entry_block);
let default = cases.pop().unwrap().1;
env.builder.build_switch(current_tag_id, default, &cases);
}
NullableUnwrapped { other_fields, .. } => {
let tag = tag.into_pointer_value();
let is_null = env.builder.build_is_null(tag, "is_null");
let hash_null_block = env.context.append_basic_block(parent, "hash_null_block");
let hash_other_block = env.context.append_basic_block(parent, "hash_other_block");
env.builder
.build_conditional_branch(is_null, hash_null_block, hash_other_block);
{
env.builder.position_at_end(hash_null_block);
let answer = hash_null(seed);
merge_phi.add_incoming(&[(&answer, hash_null_block)]);
env.builder.build_unconditional_branch(merge_block);
}
{
env.builder.position_at_end(hash_other_block);
let answer =
hash_ptr_to_struct(env, layout_ids, union_layout, other_fields, seed, tag);
merge_phi.add_incoming(&[(&answer, hash_other_block)]);
env.builder.build_unconditional_branch(merge_block);
}
}
NullableWrapped {
other_tags,
nullable_id,
} => {
let tag = tag.into_pointer_value();
let is_null = env.builder.build_is_null(tag, "is_null");
let hash_null_block = env.context.append_basic_block(parent, "hash_null_block");
let hash_other_block = env.context.append_basic_block(parent, "hash_other_block");
env.builder
.build_conditional_branch(is_null, hash_null_block, hash_other_block);
{
env.builder.position_at_end(hash_null_block);
let answer = hash_null(seed);
merge_phi.add_incoming(&[(&answer, hash_null_block)]);
env.builder.build_unconditional_branch(merge_block);
}
{
let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena);
for (mut tag_id, field_layouts) in other_tags.iter().enumerate() {
if tag_id >= *nullable_id as usize {
tag_id += 1;
}
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
// hash the tag id
let hash_bytes = store_and_use_as_u8_ptr(
env,
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false)
.into(),
&tag_id_layout,
);
let seed1 = hash_bitcode_fn(
env,
seed,
hash_bytes,
tag_id_layout.stack_size(env.target_info),
);
// hash tag data
let tag = tag_pointer_clear_tag_id(env, tag);
let answer = hash_ptr_to_struct(
env,
layout_ids,
union_layout,
field_layouts,
seed1,
tag,
);
merge_phi.add_incoming(&[(&answer, block)]);
env.builder.build_unconditional_branch(merge_block);
cases.push((
tag_id_basic_type
.into_int_type()
.const_int(tag_id as u64, false),
block,
));
}
env.builder.position_at_end(hash_other_block);
let tag_id = get_tag_id(env, parent, union_layout, tag.into());
let default = cases.pop().unwrap().1;
env.builder.build_switch(tag_id, default, &cases);
}
}
NonNullableUnwrapped(field_layouts) => {
let answer = hash_ptr_to_struct(
env,
layout_ids,
union_layout,
field_layouts,
seed,
tag.into_pointer_value(),
);
merge_phi.add_incoming(&[(&answer, entry_block)]);
env.builder.build_unconditional_branch(merge_block);
}
}
env.builder.position_at_end(merge_block);
merge_phi.as_basic_value().into_int_value()
}
fn build_hash_list<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
element_layout: &Layout<'a>,
when_recursive: WhenRecursive<'a>,
seed: IntValue<'ctx>,
value: StructValue<'ctx>,
) -> IntValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
.get(symbol, layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let seed_type = env.context.i64_type();
let arg_type = basic_type_from_layout(env, layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
seed_type.into(),
&[seed_type.into(), arg_type],
);
build_hash_list_help(
env,
layout_ids,
function_value,
when_recursive,
element_layout,
);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
let call = env
.builder
.build_call(function, &[seed.into(), value.into()], "struct_hash");
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap().into_int_value()
}
fn build_hash_list_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
parent: FunctionValue<'ctx>,
when_recursive: WhenRecursive<'a>,
element_layout: &Layout<'a>,
) {
let ctx = env.context;
debug_info_init!(env, parent);
// Add args to scope
let mut it = parent.get_param_iter();
let seed = it.next().unwrap().into_int_value();
let value = it.next().unwrap().into_struct_value();
seed.set_name(Symbol::ARG_1.as_str(&env.interns));
value.set_name(Symbol::ARG_2.as_str(&env.interns));
let entry = ctx.append_basic_block(parent, "entry");
env.builder.position_at_end(entry);
let result = hash_list(
env,
layout_ids,
parent,
seed,
value,
when_recursive,
element_layout,
);
env.builder.build_return(Some(&result));
}
fn hash_list<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
parent: FunctionValue<'ctx>,
seed: IntValue<'ctx>,
value: StructValue<'ctx>,
when_recursive: WhenRecursive<'a>,
element_layout: &Layout<'a>,
) -> IntValue<'ctx> {
use crate::llvm::build_list::{incrementing_elem_loop, load_list};
use inkwell::types::BasicType;
// hash of a list is the hash of its elements
let done_block = env.context.append_basic_block(parent, "done");
let loop_block = env.context.append_basic_block(parent, "loop");
let element_type = basic_type_from_layout(env, element_layout);
let ptr_type = element_type.ptr_type(inkwell::AddressSpace::Generic);
let (length, ptr) = load_list(env.builder, value, ptr_type);
let result = env.builder.build_alloca(env.context.i64_type(), "result");
env.builder.build_store(result, seed);
let is_empty = env.builder.build_int_compare(
inkwell::IntPredicate::EQ,
length,
env.ptr_int().const_zero(),
"is_empty",
);
env.builder
.build_conditional_branch(is_empty, done_block, loop_block);
env.builder.position_at_end(loop_block);
let loop_fn = |_index, element| {
let seed = env
.builder
.build_load(result, "load_current")
.into_int_value();
let answer = append_hash_layout(
env,
layout_ids,
seed,
element,
element_layout,
when_recursive.clone(),
);
env.builder.build_store(result, answer);
};
incrementing_elem_loop(
env,
parent,
*element_layout,
ptr,
length,
"current_index",
loop_fn,
);
env.builder.build_unconditional_branch(done_block);
env.builder.position_at_end(done_block);
env.builder
.build_load(result, "load_current")
.into_int_value()
}
fn hash_null(seed: IntValue<'_>) -> IntValue<'_> {
seed
}
fn hash_ptr_to_struct<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
union_layout: &UnionLayout<'a>,
field_layouts: &'a [Layout<'a>],
seed: IntValue<'ctx>,
tag: PointerValue<'ctx>,
) -> IntValue<'ctx> {
use inkwell::types::BasicType;
let wrapper_type = argument_type_from_union_layout(env, union_layout);
// cast the opaque pointer to a pointer of the correct shape
let wrapper_ptr = env
.builder
.build_bitcast(tag, wrapper_type, "hash_ptr_to_struct_opaque_to_correct")
.into_pointer_value();
let struct_ptr = env
.builder
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
.unwrap();
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let struct_ptr = env
.builder
.build_bitcast(
struct_ptr,
struct_type.ptr_type(inkwell::AddressSpace::Generic),
"cast_tag_data",
)
.into_pointer_value();
let struct_value = env
.builder
.build_load(struct_ptr, "load_struct1")
.into_struct_value();
build_hash_struct(
env,
layout_ids,
field_layouts,
WhenRecursive::Loop(*union_layout),
seed,
struct_value,
)
}
fn store_and_use_as_u8_ptr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: BasicValueEnum<'ctx>,
layout: &Layout<'a>,
) -> PointerValue<'ctx> {
let basic_type = basic_type_from_layout(env, layout);
let alloc = env.builder.build_alloca(basic_type, "store");
env.builder.build_store(alloc, value);
let u8_ptr = env
.context
.i8_type()
.ptr_type(inkwell::AddressSpace::Generic);
env.builder
.build_bitcast(alloc, u8_ptr, "as_u8_ptr")
.into_pointer_value()
}
fn hash_bitcode_fn<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
seed: IntValue<'ctx>,
buffer: PointerValue<'ctx>,
width: u32,
) -> IntValue<'ctx> {
let num_bytes = env.ptr_int().const_int(width as u64, false);
call_bitcode_fn(
env,
&[seed.into(), buffer.into(), num_bytes.into()],
bitcode::DICT_HASH,
)
.into_int_value()
}

View file

@ -1,6 +1,5 @@
pub mod bitcode;
pub mod build;
pub mod build_hash;
pub mod build_list;
pub mod build_str;
pub mod compare;

View file

@ -18,7 +18,7 @@ pub fn eq_generic<'a>(
ctx: &mut Context<'a>,
layout: Layout<'a>,
) -> Stmt<'a> {
let eq_todo = || todo!("Specialized `==` operator for `{:?}`", layout);
let _eq_todo = || todo!("Specialized `==` operator for `{:?}`", layout);
let main_body = match layout {
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {

View file

@ -103,7 +103,7 @@ pub fn refcount_generic<'a>(
structure: Symbol,
) -> Stmt<'a> {
debug_assert!(is_rc_implemented_yet(&layout));
let rc_todo = || todo!("Please update is_rc_implemented_yet for `{:?}`", layout);
let _rc_todo = || todo!("Please update is_rc_implemented_yet for `{:?}`", layout);
match layout {
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {