Add @(require_results) to map procedures where possible

This commit is contained in:
gingerBill
2022-11-13 23:47:00 +00:00
parent 3edb3d8d8c
commit 489e8dc592

View File

@@ -107,6 +107,7 @@ Map_Cell_Info :: struct {
map_cell_info :: intrinsics.type_map_cell_info
// Same as the above procedure but at runtime with the cell Map_Cell_Info value.
@(require_results)
map_cell_index_dynamic :: #force_inline proc "contextless" (base: uintptr, #no_alias info: ^Map_Cell_Info, index: uintptr) -> uintptr {
// Micro-optimize the common cases to save on integer division.
elements_per_cell := uintptr(info.elements_per_cell)
@@ -128,6 +129,7 @@ map_cell_index_dynamic :: #force_inline proc "contextless" (base: uintptr, #no_a
}
// Same as above procedure but with compile-time constant index.
@(require_results)
map_cell_index_dynamic_const :: proc "contextless" (base: uintptr, #no_alias info: ^Map_Cell_Info, $INDEX: uintptr) -> uintptr {
elements_per_cell := uintptr(info.elements_per_cell)
size_of_cell := uintptr(info.size_of_cell)
@@ -143,6 +145,7 @@ map_cell_index_dynamic_const :: proc "contextless" (base: uintptr, #no_alias inf
// The following compile-time procedure indexes such a [N]Cell(T) structure as
// if it were a flat array accounting for the internal padding introduced by the
// Cell structure.
@(require_results)
map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T), index: uintptr) -> ^T #no_bounds_check {
N :: size_of(Map_Cell(T){}.data) / size_of(T) when size_of(T) > 0 else 1
@@ -179,11 +182,13 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
}
// len() for map
@(require_results)
map_len :: #force_inline proc "contextless" (m: Raw_Map) -> int {
return m.len
}
// cap() for map
@(require_results)
map_cap :: #force_inline proc "contextless" (m: Raw_Map) -> int {
// The data uintptr stores the capacity in the lower six bits which gives the
// a maximum value of 2^6-1, or 63. We store the integer log2 of capacity
@@ -196,10 +201,12 @@ map_cap :: #force_inline proc "contextless" (m: Raw_Map) -> int {
// some math is needed to compute it. Compute it as a fixed point percentage to
// avoid floating point operations. This division can be optimized out by
// multiplying by the multiplicative inverse of 100.
@(require_results)
map_load_factor :: #force_inline proc "contextless" (log2_capacity: uintptr) -> uintptr {
return ((uintptr(1) << log2_capacity) * MAP_LOAD_FACTOR) / 100
}
@(require_results)
map_resize_threshold :: #force_inline proc "contextless" (m: Raw_Map) -> int {
return int(map_load_factor(map_log2_cap(m)))
}
@@ -208,12 +215,14 @@ map_resize_threshold :: #force_inline proc "contextless" (m: Raw_Map) -> int {
// used in the implementation rather than map_cap since the check for data = 0
// isn't necessary in the implementation. cap() on the otherhand needs to work
// when called on an empty map.
@(require_results)
map_log2_cap :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
return m.data & (64 - 1)
}
// Canonicalize the data by removing the tagged capacity stored in the lower six
// bits of the data uintptr.
@(require_results)
map_data :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
return m.data &~ uintptr(64 - 1)
}
@@ -224,15 +233,18 @@ Map_Hash :: uintptr
// Procedure to check if a slot is empty for a given hash. This is represented
// by the zero value to make the zero value useful. This is a procedure just
// for prose reasons.
@(require_results)
map_hash_is_empty :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
return hash == 0
}
@(require_results)
map_hash_is_deleted :: #force_no_inline proc "contextless" (hash: Map_Hash) -> bool {
// The MSB indicates a tombstone
N :: size_of(Map_Hash)*8 - 1
return hash >> N != 0
}
@(require_results)
map_hash_is_valid :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
// The MSB indicates a tombstone
N :: size_of(Map_Hash)*8 - 1
@@ -242,12 +254,14 @@ map_hash_is_valid :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
// Computes the desired position in the array. This is just index % capacity,
// but a procedure as there's some math involved here to recover the capacity.
@(require_results)
map_desired_position :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash) -> uintptr {
// We do not use map_cap since we know the capacity will not be zero here.
capacity := uintptr(1) << map_log2_cap(m)
return uintptr(hash & Map_Hash(capacity - 1))
}
@(require_results)
map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash, slot: uintptr) -> uintptr {
// We do not use map_cap since we know the capacity will not be zero here.
capacity := uintptr(1) << map_log2_cap(m)
@@ -275,6 +289,7 @@ Map_Info :: struct {
// map_info :: proc "contextless" ($T: typeid/map[$K]$V) -> ^Map_Info {...}
map_info :: intrinsics.type_map_info
@(require_results)
map_kvh_data_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info) -> (ks: uintptr, vs: uintptr, hs: [^]Map_Hash, sk: uintptr, sv: uintptr) {
INFO_HS := intrinsics.type_map_cell_info(Map_Hash)
@@ -291,13 +306,14 @@ map_kvh_data_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Inf
return
}
@(require_results)
map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info) -> (vs: uintptr) {
capacity := uintptr(1) << map_log2_cap(m)
return map_cell_index_dynamic(map_data(m), info.ks, capacity) // Skip past ks to get start of vs
}
@(private)
@(private, require_results)
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
@@ -315,6 +331,7 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr
}
// The only procedure which needs access to the context is the one which allocates the map.
@(require_results)
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
result.allocator = allocator // set the allocator always
if log2_capacity == 0 {
@@ -355,6 +372,7 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
// there is no type information.
//
// This procedure returns the address of the just inserted value.
@(require_results)
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
h := h
pos := map_desired_position(m^, h)
@@ -429,6 +447,7 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
}
}
@(require_results)
map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
log2_capacity := map_log2_cap(m^)
new_capacity := uintptr(1) << max(log2_capacity + 1, MAP_MIN_LOG2_CAPACITY)
@@ -436,7 +455,9 @@ map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Inf
}
@(require_results)
map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
@(require_results)
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
z := intrinsics.count_leading_zeros(x)
if z > 0 && x & (x-1) != 0 {
@@ -482,7 +503,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
}
k := map_cell_index_dynamic(ks, info.ks, i)
v := map_cell_index_dynamic(vs, info.vs, i)
map_insert_hash_dynamic(&resized, info, hash, k, v)
_ = map_insert_hash_dynamic(&resized, info, hash, k, v)
// Only need to do this comparison on each actually added pair, so do not
// fold it into the for loop comparator as a micro-optimization.
n -= 1
@@ -499,6 +520,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
}
@(require_results)
map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
if m.allocator.procedure == nil {
m.allocator = context.allocator
@@ -530,9 +552,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
k := map_cell_index_dynamic(ks, info.ks, i)
v := map_cell_index_dynamic(vs, info.vs, i)
map_insert_hash_dynamic(&shrunk, info, hash, k, v)
_ = map_insert_hash_dynamic(&shrunk, info, hash, k, v)
// Only need to do this comparison on each actually added pair, so do not
// fold it into the for loop comparator as a micro-optimization.
n -= 1
@@ -555,6 +575,7 @@ map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_loc
return mem_free_with_size(ptr, size, m.allocator, loc)
}
@(require_results)
map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
if map_len(m) == 0 {
return 0, false
@@ -577,6 +598,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
d += 1
}
}
@(require_results)
map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
if map_len(m) == 0 {
return false
@@ -626,16 +648,17 @@ map_clear_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
}
map_kvh_data_static :: #force_inline proc "contextless" (m: $T/map[$K]$V) -> ([^]Map_Cell(K), [^]Map_Cell(V), [^]Map_Hash) {
H :: Map_Hash
@(require_results)
map_kvh_data_static :: #force_inline proc "contextless" (m: $T/map[$K]$V) -> (ks: [^]Map_Cell(K), vs: [^]Map_Cell(V), hs: [^]Map_Hash) {
capacity := uintptr(cap(m))
ks := ([^]Map_Cell(K))(map_data(transmute(Raw_Map)m))
vs := ([^]Map_Cell(V))(map_cell_index_static(ks, capacity))
hs := ([^]Map_Cell(H))(map_cell_index_static(vs, capacity))
return ks, vs, ([^]Map_Hash)(hs)
ks = ([^]Map_Cell(K))(map_data(transmute(Raw_Map)m))
vs = ([^]Map_Cell(V))(map_cell_index_static(ks, capacity))
hs = ([^]Map_Hash)(map_cell_index_static(vs, capacity))
return
}
@(require_results)
map_get :: proc "contextless" (m: $T/map[$K]$V, key: K) -> (stored_key: K, stored_value: V, ok: bool) {
rm := transmute(Raw_Map)m
if rm.len == 0 {
@@ -719,15 +742,14 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
}
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
assert(result != 0)
m.len += 1
return rawptr(result)
}
// IMPORTANT: USED WITHIN THE COMPILER
@(private)
__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) {
map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error {
return map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
}