Begin work on implementing the new map internals

This commit is contained in:
gingerBill
2022-11-07 23:02:21 +00:00
parent f1c24f434b
commit c96e0afbf1
13 changed files with 832 additions and 492 deletions
+2
View File
@@ -2072,6 +2072,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if info.generated_struct == nil {
return
}
/*
entries := &m.entries
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
@@ -2106,6 +2107,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
value := data + entry_type.offsets[3] // value: Value
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
}
*/
}
case runtime.Type_Info_Struct:
+10 -11
View File
@@ -112,22 +112,21 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
free(raw_data(str), allocator, loc)
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free(raw_data(str), allocator, loc)
}
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
free((^byte)(str), allocator, loc)
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free((^byte)(str), allocator, loc)
}
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
free(raw_data(array), array.allocator, loc)
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
return free(raw_data(array), array.allocator, loc)
}
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
free(raw_data(array), allocator, loc)
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free(raw_data(array), allocator, loc)
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
raw := transmute(Raw_Map)m
delete_slice(raw.hashes, raw.entries.allocator, loc)
free(raw.entries.data, raw.entries.allocator, loc)
return runtime.map_free(raw, loc)
}
+2 -2
View File
@@ -273,7 +273,7 @@ length :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).len
case Type_Info_Map:
return (^runtime.Raw_Map)(val.data).entries.len
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
case Type_Info_String:
if a.is_cstring {
@@ -305,7 +305,7 @@ capacity :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).cap
case Type_Info_Map:
return (^runtime.Raw_Map)(val.data).entries.cap
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
}
return 0
}
+25 -2
View File
@@ -394,9 +394,32 @@ Raw_Dynamic_Array :: struct {
allocator: Allocator,
}
// The raw, type-erased representation of a map.
//
// 32-bytes on 64-bit
// 16-bytes on 32-bit
Raw_Map :: struct {
hashes: []Map_Index,
entries: Raw_Dynamic_Array,
// A single allocation spanning all keys, values, and hashes.
// {
// k: Map_Cell(K) * (capacity / ks_per_cell)
// v: Map_Cell(V) * (capacity / vs_per_cell)
// h: Map_Cell(H) * (capacity / hs_per_cell)
// }
//
// The data is allocated assuming 64-byte alignment, meaning the address is
// always a multiple of 64. This means we have 6 bits of zeros in the pointer
// to store the capacity. We can store a value as large as 2^6-1 or 63 in
// there. This conveniently is the maximum log2 capacity we can have for a map
// as Odin uses signed integers to represent capacity.
//
// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
// requires no padding, meaning it can be indexed as a regular array of
// Map_Hash directly, though for consistency sake it's written as if it were
// an array of Map_Cell(Map_Hash).
data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
len: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
}
Raw_Any :: struct {
+6 -31
View File
@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
}
@builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
}
raw := transmute(Raw_Map)m
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
if err == nil {
err = err1
}
return err
return map_free(transmute(Raw_Map)m, loc)
}
@@ -285,19 +272,13 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil {
return
}
raw_map := (^Raw_Map)(m)
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
entries.len = 0
for _, i in raw_map.hashes {
raw_map.hashes[i] = MAP_SENTINEL
}
map_clear_dynamic((^Raw_Map)(m), map_info(K, V))
}
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil {
h := __get_map_header_table(T)
__dynamic_map_reserve(m, h, uint(capacity), loc)
__dynamic_map_reserve((^Raw_Map)(m), map_info(K, V), uint(capacity), loc)
}
}
@@ -325,15 +306,9 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
if m != nil {
key := key
h := __get_map_header(m)
fr := __map_find(h, &key)
if fr.entry_index != MAP_SENTINEL {
entry := __dynamic_map_get_entry(h, fr.entry_index)
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
__dynamic_map_erase(h, fr)
}
info := map_info(K, V)
_ = map_erase_dynamic((^Raw_Map)(m), info, uintptr(&key))
// TODO(bill) old key and value
}
return
}
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1364,7 +1364,6 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
if (key || value) {
poly->Map.entry_type = nullptr;
poly->Map.internal_type = nullptr;
poly->Map.lookup_result_type = nullptr;
init_map_internal_types(poly);
+8 -41
View File
@@ -2176,40 +2176,9 @@ Type *make_optional_ok_type(Type *value, bool typed) {
return t;
}
void init_map_entry_type(Type *type) {
GB_ASSERT(type->kind == Type_Map);
if (type->Map.entry_type != nullptr) return;
// NOTE(bill): The preload types may have not been set yet
GB_ASSERT(t_map_hash != nullptr);
/*
struct {
hash: uintptr,
next: int,
key: Key,
value: Value,
}
*/
Scope *s = create_scope(nullptr, builtin_pkg->scope);
auto fields = slice_make<Entity *>(permanent_allocator(), 4);
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, 0, EntityState_Resolved);
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved);
fields[2] = alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, 2, EntityState_Resolved);
fields[3] = alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 3, EntityState_Resolved);
Type *entry_type = alloc_type_struct();
entry_type->Struct.fields = fields;
entry_type->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
type_set_offsets(entry_type);
type->Map.entry_type = entry_type;
}
void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map);
init_map_entry_type(type);
GB_ASSERT(t_allocator != nullptr);
if (type->Map.internal_type != nullptr) return;
Type *key = type->Map.key;
@@ -2221,19 +2190,17 @@ void init_map_internal_types(Type *type) {
/*
struct {
hashes: []int;
entries: [dynamic]EntryType;
data: uintptr,
size: uintptr,
allocator: runtime.Allocator,
}
*/
Scope *s = create_scope(nullptr, builtin_pkg->scope);
Type *hashes_type = alloc_type_slice(t_int);
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
auto fields = slice_make<Entity *>(permanent_allocator(), 2);
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved);
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved);
auto fields = slice_make<Entity *>(permanent_allocator(), 3);
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("data")), t_uintptr, false, 0, EntityState_Resolved);
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("size")), t_uintptr, false, 1, EntityState_Resolved);
fields[2] = alloc_entity_field(s, make_token_ident(str_lit("allocator")), t_allocator, false, 2, EntityState_Resolved);
generated_struct_type->Struct.fields = fields;
type_set_offsets(generated_struct_type);
+4 -4
View File
@@ -2842,12 +2842,12 @@ void init_core_source_code_location(Checker *c) {
}
void init_core_map_type(Checker *c) {
if (t_map_hash != nullptr) {
if (t_map_info != nullptr) {
return;
}
t_map_hash = find_core_type(c, str_lit("Map_Hash"));
t_map_header = find_core_type(c, str_lit("Map_Header"));
t_map_header_table = find_core_type(c, str_lit("Map_Header_Table"));
t_map_info = find_core_type(c, str_lit("Map_Info"));
t_map_cell_info = find_core_type(c, str_lit("Map_Cell_Info"));
init_mem_allocator(c);
}
void init_preload(Checker *c) {
+63 -47
View File
@@ -298,7 +298,7 @@ lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
type = core_type(type);
GB_ASSERT(is_type_valid_for_keys(type));
GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
Type *pt = alloc_type_pointer(type);
@@ -500,51 +500,67 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
return value;
}
lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) {
// IMPORTANT NOTE(bill): This must match the definition in dynamic_map_internal.odin
enum : i64 {
MAP_CACHE_LINE_LOG2 = 6,
MAP_CACHE_LINE_SIZE = 1 << MAP_CACHE_LINE_LOG2
};
GB_STATIC_ASSERT(MAP_CACHE_LINE_SIZE >= 64);
void lb_map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
i64 elem_sz = type_size_of(type);
i64 len = 1;
if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
len = MAP_CACHE_LINE_SIZE / elem_sz;
}
i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
if (size_) *size_ = size;
if (len_) *len_ = len;
}
LLVMValueRef lb_gen_map_cell_info(lbModule *m, Type *type) {
i64 size = 0, len = 0;
lb_map_cell_size_and_len(type, &size, &len);
LLVMValueRef const_values[4] = {};
const_values[0] = lb_const_int(m, t_uintptr, type_size_of(type)).value;
const_values[1] = lb_const_int(m, t_uintptr, type_align_of(type)).value;
const_values[2] = lb_const_int(m, t_uintptr, size).value;
const_values[3] = lb_const_int(m, t_uintptr, len).value;
return llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
}
lbValue lb_gen_map_info_ptr(lbProcedure *p, Type *map_type) {
lbModule *m = p->module;
map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map);
lbAddr *found = map_get(&m->map_header_table_map, map_type);
lbAddr *found = map_get(&m->map_info_map, map_type);
if (found) {
return lb_addr_load(p, *found);
return lb_addr_get_ptr(p, *found);
}
GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct);
i64 entry_size = type_size_of (map_type->Map.entry_type);
i64 entry_align = type_align_of (map_type->Map.entry_type);
GB_ASSERT(t_map_info != nullptr);
GB_ASSERT(t_map_cell_info != nullptr);
i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
i64 key_size = type_size_of (map_type->Map.key);
LLVMValueRef key_cell_info = lb_gen_map_cell_info(m, map_type->Map.key);
LLVMValueRef value_cell_info = lb_gen_map_cell_info(m, map_type->Map.value);
i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
i64 value_size = type_size_of (map_type->Map.value);
LLVMValueRef const_values[4] = {};
const_values[0] = key_cell_info;
const_values[1] = value_cell_info;
const_values[2] = lb_get_hasher_proc_for_type(m, map_type->Map.key).value;
const_values[3] = lb_get_equal_proc_for_type(m, map_type->Map.key).value;
Type *key_type = map_type->Map.key;
Type *val_type = map_type->Map.value;
gb_unused(val_type);
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_info};
Type *st = base_type(t_map_header_table);
GB_ASSERT(st->Struct.fields.count == 7);
LLVMValueRef const_values[7] = {};
const_values[0] = lb_get_equal_proc_for_type(m, key_type) .value;
const_values[1] = lb_const_int(m, t_int, entry_size) .value;
const_values[2] = lb_const_int(m, t_int, entry_align) .value;
const_values[3] = lb_const_int(m, t_uintptr, key_offset) .value;
const_values[4] = lb_const_int(m, t_int, key_size) .value;
const_values[5] = lb_const_int(m, t_uintptr, value_offset).value;
const_values[6] = lb_const_int(m, t_int, value_size) .value;
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_header_table};
lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr);
lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
lb_make_global_private_const(addr);
map_set(&m->map_header_table_map, map_type, addr);
return lb_addr_load(p, addr);
map_set(&m->map_info_map, map_type, addr);
return lb_addr_get_ptr(p, addr);
}
lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
@@ -616,12 +632,13 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) {
Type *map_type = base_type(type_deref(map_ptr.type));
lbValue key_ptr = {};
auto args = array_make<lbValue>(permanent_allocator(), 4);
lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type);
args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
args[3] = key_ptr;
args[1] = lb_gen_map_info_ptr(p, map_type);
args[2] = key_ptr;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
@@ -633,20 +650,19 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr,
map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map);
lbValue key_ptr = {};
lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
lbValue key_ptr = lb_address_from_load_or_generate_local(p, map_key);
key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbAddr value_addr = lb_add_local_generated(p, v.type, false);
lb_addr_store(p, value_addr, v);
auto args = array_make<lbValue>(permanent_allocator(), 6);
auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type);
args[2] = key_hash;
args[3] = key_ptr;
args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr);
args[5] = lb_emit_source_code_location_as_global(p, node);
args[1] = lb_gen_map_info_ptr(p, map_type);
args[2] = key_ptr;
args[3] = lb_emit_conv(p, value_addr.addr, t_rawptr);
args[4] = lb_emit_source_code_location_as_global(p, node);
lb_emit_runtime_call(p, "__dynamic_map_set", args);
}
@@ -660,8 +676,8 @@ void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type));
args[2] = lb_const_int(p->module, t_int, capacity);
args[1] = lb_gen_map_info_ptr(p, type_deref(map_ptr.type));
args[2] = lb_const_int(p->module, t_uint, capacity);
args[3] = lb_emit_source_code_location_as_global(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
}
+1 -1
View File
@@ -160,7 +160,7 @@ struct lbModule {
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
PtrMap<Type *, lbAddr> map_header_table_map;
PtrMap<Type *, lbAddr> map_info_map;
};
struct lbGenerator {
+5 -19
View File
@@ -75,7 +75,7 @@ void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->objc_classes, a);
string_map_init(&m->objc_selectors, a);
map_init(&m->map_header_table_map, a, 0);
map_init(&m->map_info_map, a, 0);
}
@@ -1939,27 +1939,13 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
defer (m->internal_type_level += 1);
unsigned field_count = cast(unsigned)(internal_type->Struct.fields.count);
GB_ASSERT(field_count == 2);
LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
GB_ASSERT(field_count == 3);
LLVMTypeRef entries_fields[] = {
lb_type(m, t_rawptr), // data
lb_type(m, t_int), // len
lb_type(m, t_int), // cap
LLVMTypeRef fields[3] = {
lb_type(m, t_uintptr), // data
lb_type(m, t_uintptr), // len
lb_type(m, t_allocator), // allocator
};
fields[0] = lb_type(m, internal_type->Struct.fields[0]->type);
fields[1] = LLVMStructTypeInContext(ctx, entries_fields, gb_count_of(entries_fields), false);
{ // Add this to simplify things
lbStructFieldRemapping entries_field_remapping = {};
slice_init(&entries_field_remapping, permanent_allocator(), gb_count_of(entries_fields));
for_array(i, entries_field_remapping) {
entries_field_remapping[i] = cast(i32)i;
}
map_set(&m->struct_field_remapping, cast(void *)fields[1], entries_field_remapping);
}
return LLVMStructTypeInContext(ctx, fields, field_count, false);
}
+8 -14
View File
@@ -226,7 +226,6 @@ struct TypeProc {
TYPE_KIND(Map, struct { \
Type *key; \
Type *value; \
Type *entry_type; \
Type *internal_type; \
Type *lookup_result_type; \
}) \
@@ -685,9 +684,8 @@ gb_global Type *t_allocator_error = nullptr;
gb_global Type *t_source_code_location = nullptr;
gb_global Type *t_source_code_location_ptr = nullptr;
gb_global Type *t_map_hash = nullptr;
gb_global Type *t_map_header = nullptr;
gb_global Type *t_map_header_table = nullptr;
gb_global Type *t_map_info = nullptr;
gb_global Type *t_map_cell_info = nullptr;
gb_global Type *t_equal_proc = nullptr;
@@ -3330,8 +3328,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
}
}
} else if (type->kind == Type_DynamicArray) {
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
// `Raw_Dynamic_Array` type?
GB_ASSERT(t_allocator != nullptr);
String allocator_str = str_lit("allocator");
gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 3);
@@ -3342,15 +3338,12 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
return sel;
}
} else if (type->kind == Type_Map) {
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
// `Raw_Map` type?
GB_ASSERT(t_allocator != nullptr);
String allocator_str = str_lit("allocator");
gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 3);
gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 2);
if (field_name == allocator_str) {
selection_add_index(&sel, 1);
selection_add_index(&sel, 3);
selection_add_index(&sel, 2);
sel.entity = entity__allocator;
return sel;
}
@@ -3795,11 +3788,12 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_Map:
/*
struct {
hashes: []int, // 2 words
entries: [dynamic]Entry_Type, // 5 words
data: uintptr, // 1 word
size: uintptr, // 1 word
allocator: runtime.Allocator, // 2 words
}
*/
return (2 + (3 + 2))*build_context.word_size;
return (1 + 1 + 2)*build_context.word_size;
case Type_Tuple: {
i64 count, align, size;