Merge pull request #2181 from odin-lang/map-dev

New `map` internals
This commit is contained in:
gingerBill
2022-11-17 15:29:28 +00:00
committed by GitHub
35 changed files with 2147 additions and 896 deletions
+17
View File
@@ -38,6 +38,11 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
@@ -87,6 +92,11 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
timeout-minutes: 10
@@ -146,6 +156,13 @@ jobs:
cd tests\vendor
call build.bat
timeout-minutes: 10
- name: Odin internals tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\internal
call build.bat
timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |
+11 -14
View File
@@ -257,21 +257,18 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_start(w, opt, '{') or_return
if m != nil {
if info.generated_struct == nil {
if info.map_info == nil {
return .Unsupported_Type
}
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)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
for bucket_index in 0..<map_cap {
if !runtime.map_hash_is_valid(hs[bucket_index]) {
continue
}
for i in 0..<entries.len {
opt_write_iteration(w, opt, i) or_return
data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3])
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
// check for string type
{
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
name: string
#partial switch info in ti.variant {
case runtime.Type_Info_String:
case runtime.Type_Info_String:
switch s in a {
case string: name = s
case cstring: name = string(s)
}
opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type
}
}
+3 -6
View File
@@ -399,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
return UNSUPPORTED_TYPE
}
raw_map := (^mem.Raw_Map)(v.data)
if raw_map.entries.allocator.procedure == nil {
raw_map.entries.allocator = p.allocator
if raw_map.allocator.procedure == nil {
raw_map.allocator = p.allocator
}
header := runtime.__get_map_header_table_runtime(t)
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
defer delete(elem_backing, p.allocator)
@@ -421,7 +419,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
return err
}
key_hash := runtime.default_hasher_string(&key, 0)
key_ptr := rawptr(&key)
key_cstr: cstring
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
key_ptr = &key_cstr
}
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
if set_ptr == nil {
delete(key, p.allocator)
}
+14 -28
View File
@@ -2069,41 +2069,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
m := (^mem.Raw_Map)(v.data)
if m != nil {
if info.generated_struct == nil {
if info.map_info == 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)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
/*
NOTE: The layout of a `map` is as follows:
map[Key]Value
## Internal Layout
struct {
hashes: []int,
entries: [dynamic]struct{
hash: uintptr,
next: int,
key: Key,
value: Value,
},
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
j := 0
for bucket_index in 0..<map_cap {
if !runtime.map_hash_is_valid(hs[bucket_index]) {
continue
}
*/
for i in 0..<entries.len {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(entries.data) + uintptr(i*entry_size)
if j > 0 {
io.write_string(fi.writer, ", ", &fi.n)
}
j += 1
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
key := data + entry_type.offsets[2] // key: Key
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
io.write_string(fi.writer, "=", &fi.n)
value := data + entry_type.offsets[3] // value: Value
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
}
}
+3
View File
@@ -188,6 +188,9 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
+21 -17
View File
@@ -77,6 +77,14 @@ free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_locatio
return runtime.mem_free(ptr, allocator, loc)
}
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
return err
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return runtime.mem_free_bytes(bytes, allocator, loc)
}
@@ -112,22 +120,20 @@ 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_with_size(raw_data(str), len(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_with_size(raw_data(array), cap(array)*size_of(E), 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_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m
delete_slice(raw.hashes, raw.entries.allocator, loc)
free(raw.entries.data, raw.entries.allocator, loc)
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
}
@@ -158,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return nil, .Out_Of_Memory
}
DEFAULT_RESERVE_CAPACITY :: 16
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
@@ -173,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
return make_aligned(T, len, align_of(E), allocator, loc)
}
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
}
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
@@ -188,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
array = transmute(T)s
return
}
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator
m: T
reserve_map(&m, cap)
reserve_map(&m, cap, loc)
return m
}
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
+15 -9
View File
@@ -867,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
t.bad_free_array.allocator = internals_allocator
if .Free_All in query_features(t.backing) {
t.clear_on_free_all = true
}
}
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
@@ -874,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
delete(t.bad_free_array)
}
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
clear(&t.allocation_map)
clear(&t.bad_free_array)
}
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
return Allocator{
data = data,
@@ -883,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
data := (^Tracking_Allocator)(allocator_data)
if mode == .Query_Info {
info := (^Allocator_Query_Info)(old_memory)
@@ -895,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
info.pointer = nil
}
return nil, nil
return
}
result: []byte
err: Allocator_Error
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory,
location = loc,
})
} else {
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
if err != nil {
return result, err
}
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
}
result_ptr := raw_data(result)
@@ -957,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
unreachable()
}
return result, err
return
}
+76
View File
@@ -0,0 +1,76 @@
package reflect
import "core:runtime"
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
if val == nil || it == nil {
return
}
ti := type_info_base(type_info_of(val.id))
#partial switch info in ti.variant {
case Type_Info_Pointer:
if ptr := (^rawptr)(val.data)^; ptr != nil {
return iterate_array(any{ptr, info.elem.id}, it)
}
case Type_Info_Array:
if it^ < info.count {
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
case Type_Info_Slice:
array := (^runtime.Raw_Slice)(val.data)
if it^ < array.len {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
case Type_Info_Dynamic_Array:
array := (^runtime.Raw_Dynamic_Array)(val.data)
if it^ < array.len {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
}
return
}
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
if val == nil || it == nil {
return
}
ti := type_info_base(type_info_of(val.id))
#partial switch info in ti.variant {
case Type_Info_Pointer:
if ptr := (^rawptr)(val.data)^; ptr != nil {
return iterate_map(any{ptr, info.elem.id}, it)
}
case Type_Info_Map:
if info.map_info == nil {
break
}
rm := (^runtime.Raw_Map)(val.data)
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
key_ptr := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
key.data = rawptr(key_ptr)
value.data = rawptr(value_ptr)
key.id = info.key.id
value.id = info.value.id
ok = true
break
}
}
}
return
}
-42
View File
@@ -1,42 +0,0 @@
package reflect
import "core:runtime"
import "core:mem"
_ :: runtime
_ :: mem
Map_Entry_Info :: struct($Key, $Value: typeid) {
hash: uintptr,
key: Key,
value: Value,
}
map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
m := m
rm := (^mem.Raw_Map)(&m)
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct)
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(Type_Info_Struct)
key_offset := entry_type.offsets[2]
value_offset := entry_type.offsets[3]
entry_size := uintptr(ed.elem_size)
entries = make(type_of(entries), rm.entries.len)
data := uintptr(rm.entries.data)
for i in 0..<rm.entries.len {
header := (^runtime.Map_Entry_Header)(data)
hash := header.hash
key := (^K)(data + key_offset)^
value := (^V)(data + value_offset)^
entries[i] = {hash, key, value}
data += entry_size
}
return entries
}
+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
}
+28 -7
View File
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
values: []Type_Info_Enum_Value,
}
Type_Info_Map :: struct {
key: ^Type_Info,
value: ^Type_Info,
generated_struct: ^Type_Info,
key_equal: Equal_Proc,
key_hasher: Hasher_Proc,
key: ^Type_Info,
value: ^Type_Info,
map_info: ^Map_Info,
}
Type_Info_Bit_Set :: struct {
elem: ^Type_Info,
@@ -394,9 +392,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: int, // 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 {
+18 -64
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_dynamic(transmute(Raw_Map)m, map_info(T), loc)
}
@@ -244,12 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
return
}
@(builtin)
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, cap)
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, capacity)
context.allocator = allocator
m: T
reserve_map(&m, cap)
reserve_map(&m, capacity, loc)
return m
}
@(builtin)
@@ -285,36 +272,24 @@ 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(T))
}
@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(T), uint(capacity), loc)
}
}
/*
Shrinks the capacity of a map down to the current length, or the given capacity.
If `new_cap` is negative, then `len(m)` is used.
Returns false if `cap(m) < new_cap`, or the allocator report failure.
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
Shrinks the capacity of a map down to the current length.
*/
@builtin
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
if m != nil {
new_cap := new_cap if new_cap >= 0 else len(m)
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
did_shrink = err == nil
}
return
}
@@ -325,14 +300,10 @@ 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)
old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
if ok {
deleted_key = (^K)(old_k)^
deleted_value = (^V)(old_v)^
}
}
return
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
new_size := capacity * size_of(E)
allocator := a.allocator
new_data, err := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
if new_data == nil || err != nil {
return false
}
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
new_size := length * size_of(E)
allocator := a.allocator
new_data, err := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
if new_data == nil || err != nil {
return false
}
@@ -650,15 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
old_size := a.cap * size_of(E)
new_size := new_cap * size_of(E)
new_data, err := a.allocator.procedure(
a.allocator.data,
.Resize,
new_size,
align_of(E),
a.data,
old_size,
loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
if err != nil {
return
}
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value
h := __get_map_header_table(T)
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
return (^V)(uintptr(e) + h.value_offset)
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
}
File diff suppressed because it is too large Load Diff
+22 -26
View File
@@ -6,8 +6,8 @@ import "core:runtime"
_ :: intrinsics
_ :: runtime
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
keys = make(type_of(keys), len(m), allocator)
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K, err: runtime.Allocator_Error) {
keys = make(type_of(keys), len(m), allocator) or_return
i := 0
for key in m {
keys[i] = key
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
}
return
}
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) {
values = make(type_of(values), len(m), allocator)
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V, err: runtime.Allocator_Error) {
values = make(type_of(values), len(m), allocator) or_return
i := 0
for _, value in m {
values[i] = value
@@ -37,8 +37,8 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
}
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V)) {
entries = make(type_of(entries), len(m), allocator)
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
entries = make(type_of(entries), len(m), allocator) or_return
i := 0
for key, value in m {
entries[i].key = key
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
m := m
rm := (^runtime.Raw_Map)(&m)
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
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)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
key_offset := entry_type.offsets[2]
value_offset := entry_type.offsets[3]
entry_size := uintptr(ed.elem_size)
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
if info.map_info != nil {
entries = make(type_of(entries), len(m), allocator) or_return
entries = make(type_of(entries), rm.entries.len)
map_cap := uintptr(cap(m))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
entry_index := 0
for bucket_index in 0..<map_cap {
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
entries[entry_index].hash = hash
entries[entry_index].key = (^K)(key)^
entries[entry_index].value = (^V)(value)^
data := uintptr(rm.entries.data)
for i in 0..<rm.entries.len {
header := (^runtime.Map_Entry_Header)(data)
hash := header.hash
key := (^K)(data + key_offset)^
value := (^V)(data + value_offset)^
entries[i] = {hash, key, value}
data += entry_size
entry_index += 1
}
}
}
return
}
+2
View File
@@ -307,6 +307,8 @@ struct BuildContext {
bool disallow_rtti;
bool use_static_map_calls;
RelocMode reloc_mode;
bool disable_red_zone;
+37
View File
@@ -5370,6 +5370,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_type_map_info:
{
Operand op = {};
Type *bt = check_type(c, ce->args[0]);
Type *type = base_type(bt);
if (type == nullptr || type == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (!is_type_map(type)) {
gbString t = type_to_string(type);
error(ce->args[0], "Expected a map type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
add_map_key_type_dependencies(c, type);
operand->mode = Addressing_Value;
operand->type = t_map_info_ptr;
break;
}
case BuiltinProc_type_map_cell_info:
{
Operand op = {};
Type *bt = check_type(c, ce->args[0]);
Type *type = base_type(bt);
if (type == nullptr || type == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
operand->mode = Addressing_Value;
operand->type = t_map_cell_info_ptr;
break;
}
case BuiltinProc_constant_utf16_cstring:
{
String value = {};
+36 -7
View File
@@ -285,6 +285,37 @@ void error_operand_no_value(Operand *o) {
}
}
void add_map_get_dependencies(CheckerContext *c) {
if (build_context.use_static_map_calls) {
add_package_dependency(c, "runtime", "map_desired_position");
add_package_dependency(c, "runtime", "map_probe_distance");
} else {
add_package_dependency(c, "runtime", "__dynamic_map_get");
}
}
void add_map_set_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
if (t_map_set_proc == nullptr) {
Type *map_set_args[5] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr, /*value*/t_rawptr, /*#caller_location*/t_source_code_location};
t_map_set_proc = alloc_type_proc_from_types(map_set_args, gb_count_of(map_set_args), t_rawptr, false, ProcCC_Odin);
}
if (build_context.use_static_map_calls) {
add_package_dependency(c, "runtime", "__dynamic_map_check_grow");
add_package_dependency(c, "runtime", "map_insert_hash_dynamic");
} else {
add_package_dependency(c, "runtime", "__dynamic_map_set");
}
}
void add_map_reserve_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
}
void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
Scope *s = c->scope;
@@ -1364,8 +1395,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);
return true;
@@ -3246,7 +3275,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
check_assignment(c, x, yt->Map.key, str_lit("map 'not_in'"));
}
add_package_dependency(c, "runtime", "__dynamic_map_get");
add_map_get_dependencies(c);
} else if (is_type_bit_set(rhs_type)) {
Type *yt = base_type(rhs_type);
@@ -8557,8 +8586,8 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
if (build_context.no_dynamic_literals && cl->elems.count) {
error(node, "Compound literals of dynamic types have been disabled");
} else {
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
add_package_dependency(c, "runtime", "__dynamic_map_set");
add_map_reserve_dependencies(c);
add_map_set_dependencies(c);
}
break;
}
@@ -8994,8 +9023,8 @@ ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
o->type = t->Map.value;
o->expr = node;
add_package_dependency(c, "runtime", "__dynamic_map_get");
add_package_dependency(c, "runtime", "__dynamic_map_set");
add_map_get_dependencies(c);
add_map_set_dependencies(c);
return Expr_Expr;
}
+23 -65
View File
@@ -2176,70 +2176,36 @@ 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);
// 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 map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
i64 elem_sz = type_size_of(type);
/*
struct {
hash: uintptr,
next: int,
key: Key,
value: Value,
i64 len = 1;
if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
len = MAP_CACHE_LINE_SIZE / elem_sz;
}
*/
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;
i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
if (size_) *size_ = size;
if (len_) *len_ = len;
}
void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map);
init_map_entry_type(type);
if (type->Map.internal_type != nullptr) return;
GB_ASSERT(t_allocator != nullptr);
if (type->Map.lookup_result_type != nullptr) return;
Type *key = type->Map.key;
Type *value = type->Map.value;
GB_ASSERT(key != nullptr);
GB_ASSERT(value != nullptr);
Type *generated_struct_type = alloc_type_struct();
/*
struct {
hashes: []int;
entries: [dynamic]EntryType;
}
*/
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);
generated_struct_type->Struct.fields = fields;
type_set_offsets(generated_struct_type);
type->Map.internal_type = generated_struct_type;
type->Map.lookup_result_type = make_optional_ok_type(value);
type->Map.lookup_result_type = make_optional_ok_type(value);
}
void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
@@ -2255,35 +2221,27 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
}
if (is_type_simple_compare(key)) {
i64 sz = type_size_of(key);
if (1 <= sz && sz <= 16) {
char buf[20] = {};
gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
add_package_dependency(ctx, "runtime", buf);
return;
} else {
add_package_dependency(ctx, "runtime", "default_hasher_n");
return;
}
add_package_dependency(ctx, "runtime", "default_hasher");
return;
}
if (key->kind == Type_Struct) {
add_package_dependency(ctx, "runtime", "default_hasher_n");
add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Struct.fields) {
Entity *field = key->Struct.fields[i];
add_map_key_type_dependencies(ctx, field->type);
}
} else if (key->kind == Type_Union) {
add_package_dependency(ctx, "runtime", "default_hasher_n");
add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Union.variants) {
Type *v = key->Union.variants[i];
add_map_key_type_dependencies(ctx, v);
}
} else if (key->kind == Type_EnumeratedArray) {
add_package_dependency(ctx, "runtime", "default_hasher_n");
add_package_dependency(ctx, "runtime", "default_hasher");
add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
} else if (key->kind == Type_Array) {
add_package_dependency(ctx, "runtime", "default_hasher_n");
add_package_dependency(ctx, "runtime", "default_hasher");
add_map_key_type_dependencies(ctx, key->Array.elem);
}
}
+19 -9
View File
@@ -922,10 +922,13 @@ void init_universal(void) {
{
Type *equal_args[2] = {t_rawptr, t_rawptr};
t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
t_equal_proc = alloc_type_proc_from_types(equal_args, gb_count_of(equal_args), t_bool, false, ProcCC_Contextless);
Type *hasher_args[2] = {t_rawptr, t_uintptr};
t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
t_hasher_proc = alloc_type_proc_from_types(hasher_args, gb_count_of(hasher_args), t_uintptr, false, ProcCC_Contextless);
Type *map_get_args[3] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr};
t_map_get_proc = alloc_type_proc_from_types(map_get_args, gb_count_of(map_get_args), t_rawptr, false, ProcCC_Contextless);
}
// Constants
@@ -1933,7 +1936,8 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
init_map_internal_types(bt);
add_type_info_type_internal(c, bt->Map.key);
add_type_info_type_internal(c, bt->Map.value);
add_type_info_type_internal(c, bt->Map.internal_type);
add_type_info_type_internal(c, t_uintptr); // hash value
add_type_info_type_internal(c, t_allocator);
break;
case Type_Tuple:
@@ -2155,7 +2159,8 @@ void add_min_dep_type_info(Checker *c, Type *t) {
init_map_internal_types(bt);
add_min_dep_type_info(c, bt->Map.key);
add_min_dep_type_info(c, bt->Map.value);
add_min_dep_type_info(c, bt->Map.internal_type);
add_min_dep_type_info(c, t_uintptr); // hash value
add_min_dep_type_info(c, t_allocator);
break;
case Type_Tuple:
@@ -2838,16 +2843,21 @@ void init_core_source_code_location(Checker *c) {
return;
}
t_source_code_location = find_core_type(c, str_lit("Source_Code_Location"));
t_source_code_location_ptr = alloc_type_pointer(t_allocator);
t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
}
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"));
init_mem_allocator(c);
t_map_info = find_core_type(c, str_lit("Map_Info"));
t_map_cell_info = find_core_type(c, str_lit("Map_Cell_Info"));
t_raw_map = find_core_type(c, str_lit("Raw_Map"));
t_map_info_ptr = alloc_type_pointer(t_map_info);
t_map_cell_info_ptr = alloc_type_pointer(t_map_cell_info);
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
}
void init_preload(Checker *c) {
+6 -2
View File
@@ -277,6 +277,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
BuiltinProc_type_map_cell_info,
BuiltinProc__type_end,
@@ -570,8 +572,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+406 -83
View File
@@ -140,7 +140,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx) {
}
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(is_type_comparable(type));
@@ -157,8 +157,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
static u32 proc_index = 0;
char buf[16] = {};
isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
@@ -166,6 +166,9 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
map_set(&m->equal_procs, type, p);
lb_begin_procedure_body(p);
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1);
x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
@@ -173,6 +176,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
lbValue lhs = {x, pt};
lbValue rhs = {y, pt};
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+1, "nonnull");
lbBlock *block_same_ptr = lb_create_block(p, "same_ptr");
lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr");
@@ -277,28 +282,20 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
i64 sz = type_size_of(type);
if (1 <= sz && sz <= 16) {
char name[20] = {};
gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = data;
args[1] = seed;
return lb_emit_runtime_call(p, name, args);
}
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = data;
args[1] = seed;
args[2] = lb_const_int(p->module, t_int, type_size_of(type));
return lb_emit_runtime_call(p, "default_hasher_n", args);
return lb_emit_runtime_call(p, "default_hasher", args);
}
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value) {
LLVMAddCallSiteAttribute(ret_value.value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "alwaysinline"));
}
lbValue lb_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);
@@ -310,8 +307,8 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
static u32 proc_index = 0;
char buf[16] = {};
isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$hasher%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
@@ -320,16 +317,20 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1);
lbValue data = {x, t_rawptr};
lbValue seed = {y, t_uintptr};
LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull");
LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr);
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+0, "readonly");
if (is_type_simple_compare(type)) {
lbValue res = lb_simple_compare_hash(p, type, data, seed);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
return {p->value, p->type};
}
@@ -343,7 +344,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(type->Struct.offsets != nullptr);
i64 offset = type->Struct.offsets[i];
Entity *field = type->Struct.fields[i];
lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type);
lbValue field_hasher = lb_hasher_proc_for_type(m, field->type);
lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset));
args[0] = ptr;
@@ -356,11 +357,12 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
if (is_type_union_maybe_pointer(type)) {
Type *v = type->Union.variants[0];
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
args[0] = data;
args[1] = seed;
lbValue res = lb_emit_call(p, variant_hasher, args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
}
@@ -379,7 +381,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
Type *v = type->Union.variants[i];
lbValue case_tag = lb_const_union_tag(p->module, type, v);
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
args[0] = data;
args[1] = seed;
@@ -397,7 +399,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_addr_store(p, pres, seed);
auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem);
lbValue elem_hasher = lb_hasher_proc_for_type(m, type->Array.elem);
auto loop_data = lb_loop_start(p, cast(isize)type->Array.count, t_i32);
@@ -418,7 +420,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_addr_store(p, res, seed);
auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem);
lbValue elem_hasher = lb_hasher_proc_for_type(m, type->EnumeratedArray.elem);
auto loop_data = lb_loop_start(p, cast(isize)type->EnumeratedArray.count, t_i32);
@@ -439,12 +441,14 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
args[0] = data;
args[1] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else if (is_type_string(type)) {
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = data;
args[1] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else {
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
@@ -454,6 +458,279 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
}
lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(build_context.use_static_map_calls);
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_get_procs, type);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static u32 proc_index = 0;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_get-%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
map_set(&m->map_get_procs, type, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
if (build_context.ODIN_DEBUG) {
lb_add_attribute_to_proc(m, p->value, "noinline");
}
LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1);
LLVMValueRef z = LLVMGetParam(p->value, 2);
lbValue map_ptr = {x, t_rawptr};
lbValue h = {y, t_uintptr};
lbValue key_ptr = {z, t_rawptr};
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
lb_add_proc_attribute_at_index(p, 1+0, "readonly");
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
lbBlock *loop_block = lb_create_block(p, "loop");
lbBlock *hash_block = lb_create_block(p, "hash");
lbBlock *probe_block = lb_create_block(p, "probe");
lbBlock *increment_block = lb_create_block(p, "increment");
lbBlock *hash_compare_block = lb_create_block(p, "hash_compare");
lbBlock *key_compare_block = lb_create_block(p, "key_compare");
lbBlock *value_block = lb_create_block(p, "value");
lbBlock *nil_block = lb_create_block(p, "nil");
map_ptr = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
lbValue map = lb_emit_load(p, map_ptr);
lbValue length = lb_map_len(p, map);
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, length, lb_const_nil(m, t_int)), nil_block, hash_block);
lb_start_block(p, hash_block);
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
lbValue key = lb_emit_load(p, key_ptr);
lbAddr pos = lb_add_local_generated(p, t_uintptr, false);
lbAddr distance = lb_add_local_generated(p, t_uintptr, true);
lbValue capacity = lb_map_cap(p, map);
lbValue mask = lb_emit_conv(p, lb_emit_arith(p, Token_Sub, capacity, lb_const_int(m, t_int, 1), t_int), t_uintptr);
{
auto args = array_make<lbValue>(heap_allocator(), 2);
args[0] = map;
args[1] = h;
lb_addr_store(p, pos, lb_emit_runtime_call(p, "map_desired_position", args));
}
lbValue zero_uintptr = lb_const_int(m, t_uintptr, 0);
lbValue one_uintptr = lb_const_int(m, t_uintptr, 1);
lbValue ks = lb_map_data_uintptr(p, map);
lbValue vs = lb_map_cell_index_static(p, type->Map.key, ks, capacity);
lbValue hs = lb_map_cell_index_static(p, type->Map.value, vs, capacity);
ks = lb_emit_conv(p, ks, alloc_type_pointer(type->Map.key));
vs = lb_emit_conv(p, vs, alloc_type_pointer(type->Map.value));
hs = lb_emit_conv(p, hs, alloc_type_pointer(t_uintptr));
lb_emit_jump(p, loop_block);
lb_start_block(p, loop_block);
lbValue element_hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, lb_addr_load(p, pos)));
{
// if element_hash == 0 { return nil }
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, zero_uintptr), nil_block, probe_block);
}
lb_start_block(p, probe_block);
{
auto args = array_make<lbValue>(heap_allocator(), 3);
args[0] = map;
args[1] = element_hash;
args[2] = lb_addr_load(p, pos);
lbValue probe_distance = lb_emit_runtime_call(p, "map_probe_distance", args);
lbValue cond = lb_emit_comp(p, Token_Gt, lb_addr_load(p, distance), probe_distance);
lb_emit_if(p, cond, nil_block, hash_compare_block);
}
lb_start_block(p, hash_compare_block);
{
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, h), key_compare_block, increment_block);
}
lb_start_block(p, key_compare_block);
{
lbValue element_key = lb_map_cell_index_static(p, type->Map.key, ks, lb_addr_load(p, pos));
element_key = lb_emit_conv(p, element_key, ks.type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, lb_emit_load(p, element_key), key);
lb_emit_if(p, cond, value_block, increment_block);
}
lb_start_block(p, value_block);
{
lbValue element_value = lb_map_cell_index_static(p, type->Map.value, vs, lb_addr_load(p, pos));
element_value = lb_emit_conv(p, element_value, t_rawptr);
LLVMBuildRet(p->builder, element_value.value);
}
lb_start_block(p, increment_block);
{
lbValue pp = lb_addr_load(p, pos);
pp = lb_emit_arith(p, Token_Add, pp, one_uintptr, t_uintptr);
pp = lb_emit_arith(p, Token_And, pp, mask, t_uintptr);
lb_addr_store(p, pos, pp);
lb_emit_increment(p, distance.addr);
}
lb_emit_jump(p, loop_block);
lb_start_block(p, nil_block);
{
lbValue res = lb_const_nil(m, t_rawptr);
LLVMBuildRet(p->builder, res.value);
}
return {p->value, p->type};
}
void lb_debug_print(lbProcedure *p, String const &str) {
auto args = array_make<lbValue>(heap_allocator(), 1);
args[0] = lb_const_string(p->module, str);
lb_emit_runtime_call(p, "print_string", args);
}
lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(build_context.use_static_map_calls);
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_set_procs, type);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static u32 proc_index = 0;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
map_set(&m->map_set_procs, type, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "nounwind");
if (build_context.ODIN_DEBUG) {
lb_add_attribute_to_proc(m, p->value, "noinline");
}
lbValue map_ptr = {LLVMGetParam(p->value, 0), t_rawptr};
lbValue hash = {LLVMGetParam(p->value, 1), t_uintptr};
lbValue key_ptr = {LLVMGetParam(p->value, 2), t_rawptr};
lbValue value_ptr = {LLVMGetParam(p->value, 3), t_rawptr};
lbValue location_ptr = {LLVMGetParam(p->value, 4), t_source_code_location_ptr};
map_ptr = lb_emit_conv(p, map_ptr, alloc_type_pointer(type));
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
if (!are_types_identical(type->Map.key, type->Map.value)) {
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
}
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
lb_add_proc_attribute_at_index(p, 1+3, "nonnull");
if (!are_types_identical(type->Map.key, type->Map.value)) {
lb_add_proc_attribute_at_index(p, 1+3, "noalias");
}
lb_add_proc_attribute_at_index(p, 1+3, "readonly");
lb_add_proc_attribute_at_index(p, 1+4, "nonnull");
lb_add_proc_attribute_at_index(p, 1+4, "noalias");
lb_add_proc_attribute_at_index(p, 1+4, "readonly");
////
lbValue found_ptr = {};
{
lbValue map_get_proc = lb_map_get_proc_for_type(m, type);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = hash;
args[2] = key_ptr;
found_ptr = lb_emit_call(p, map_get_proc, args);
}
lbBlock *found_block = lb_create_block(p, "found");
lbBlock *check_grow_block = lb_create_block(p, "check-grow");
lbBlock *grow_fail_block = lb_create_block(p, "grow-fail");
lbBlock *insert_block = lb_create_block(p, "insert");
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, found_ptr), found_block, check_grow_block);
lb_start_block(p, found_block);
{
lb_mem_copy_non_overlapping(p, found_ptr, value_ptr, lb_const_int(m, t_int, type_size_of(type->Map.value)));
LLVMBuildRet(p->builder, lb_emit_conv(p, found_ptr, t_rawptr).value);
}
lb_start_block(p, check_grow_block);
lbValue map_info = lb_gen_map_info_ptr(p->module, type);
{
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = map_info;
args[2] = lb_emit_load(p, location_ptr);
lbValue grow_err = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, grow_err), grow_fail_block, insert_block);
lb_start_block(p, grow_fail_block);
LLVMBuildRet(p->builder, LLVMConstNull(lb_type(m, t_rawptr)));
}
lb_start_block(p, insert_block);
{
auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = map_info;
args[2] = hash;
args[3] = lb_emit_conv(p, key_ptr, t_uintptr);
args[4] = lb_emit_conv(p, value_ptr, t_uintptr);
lbValue result = lb_emit_runtime_call(p, "map_insert_hash_dynamic", args);
lb_emit_increment(p, lb_map_len_ptr(p, map_ptr));
LLVMBuildRet(p->builder, lb_emit_conv(p, result, t_rawptr).value);
}
return {p->value, p->type};
}
lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
if (found) {
@@ -500,51 +777,60 @@ 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) {
lbModule *m = p->module;
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
lbAddr *found = map_get(&m->map_cell_info_map, type);
if (found) {
return found->addr;
}
i64 size = 0, len = 0;
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;
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_cell_info};
lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
lb_make_global_private_const(addr);
map_set(&m->map_cell_info_map, type, addr);
return addr.addr;
}
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
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 found->addr;
}
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_ptr(m, map_type->Map.key).value;
LLVMValueRef value_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.value).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_hasher_proc_for_type(m, map_type->Map.key).value;
const_values[3] = lb_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 addr.addr;
}
lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
@@ -602,7 +888,7 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
lbValue hashed_key = lb_const_hash(p->module, key, key_type);
if (hashed_key.value == nullptr) {
lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type);
lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = key_ptr;
@@ -615,42 +901,68 @@ 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));
GB_ASSERT(map_type->kind == Type_Map);
lbValue ptr = {};
lbValue key_ptr = {};
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, map_type);
args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
args[3] = key_ptr;
lbValue hash = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
if (build_context.use_static_map_calls) {
lbValue map_get_proc = lb_map_get_proc_for_type(p->module, map_type);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = hash;
args[2] = key_ptr;
ptr = lb_emit_call(p, map_get_proc, args);
} else {
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_transmute(p, map_ptr, t_raw_map_ptr);
args[1] = lb_gen_map_info_ptr(p->module, map_type);
args[2] = hash;
args[3] = key_ptr;
ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
}
return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
}
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
lbValue const &map_key, lbValue const &map_value, Ast *node) {
void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
lbValue const &map_key, lbValue const &map_value, Ast *node) {
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 hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbValue value_ptr = lb_address_from_load_or_generate_local(p, v);
lbAddr value_addr = lb_add_local_generated(p, v.type, false);
lb_addr_store(p, value_addr, v);
if (build_context.use_static_map_calls) {
lbValue map_set_proc = lb_map_set_proc_for_type(p->module, map_type);
auto args = array_make<lbValue>(permanent_allocator(), 6);
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);
lb_emit_runtime_call(p, "__dynamic_map_set", args);
auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = hash;
args[2] = lb_emit_conv(p, key_ptr, t_rawptr);
args[3] = lb_emit_conv(p, value_ptr, t_rawptr);
args[4] = lb_emit_source_code_location_as_global(p, node);
lb_emit_call(p, map_set_proc, args);
} else {
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
args[1] = lb_gen_map_info_ptr(p->module, map_type);
args[2] = hash;
args[3] = lb_emit_conv(p, key_ptr, t_rawptr);
args[4] = lb_emit_conv(p, value_ptr, t_rawptr);
args[5] = lb_emit_source_code_location_as_global(p, node);
lb_emit_runtime_call(p, "__dynamic_map_set", args);
}
}
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
GB_ASSERT(!build_context.no_dynamic_literals);
String proc_name = {};
@@ -660,10 +972,10 @@ 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->module, 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);
return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
}
@@ -688,6 +1000,8 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
p->is_startup = true;
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "nounwind");
lb_begin_procedure_body(p);
lb_setup_type_info_data(p);
@@ -712,6 +1026,7 @@ lbProcedure *lb_create_objc_names(lbModule *main_module) {
}
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
lb_add_attribute_to_proc(p->module, p->value, "nounwind");
p->is_startup = true;
return p;
}
@@ -1198,6 +1513,14 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) {
lbProcedure *p = m->hasher_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p);
}
for_array(i, m->map_get_procs.entries) {
lbProcedure *p = m->map_get_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p);
}
for_array(i, m->map_set_procs.entries) {
lbProcedure *p = m->map_set_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p);
}
return 0;
}
+10 -7
View File
@@ -144,6 +144,8 @@ struct lbModule {
PtrMap<Type *, lbProcedure *> equal_procs;
PtrMap<Type *, lbProcedure *> hasher_procs;
PtrMap<Type *, lbProcedure *> map_get_procs;
PtrMap<Type *, lbProcedure *> map_set_procs;
u32 nested_type_name_guid;
@@ -160,7 +162,8 @@ struct lbModule {
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
PtrMap<Type *, lbAddr> map_header_table_map;
PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
};
struct lbGenerator {
@@ -422,8 +425,6 @@ lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
lbValue lb_map_entries(lbProcedure *p, lbValue value);
lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value);
lbValue lb_map_len(lbProcedure *p, lbValue value);
lbValue lb_map_cap(lbProcedure *p, lbValue value);
lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
@@ -447,10 +448,12 @@ String lb_get_const_string(lbModule *m, lbValue value);
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
@@ -461,8 +464,8 @@ lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedu
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
+13 -4
View File
@@ -283,19 +283,28 @@ lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
return lb_emit_source_code_location_const(p, proc_name, pos);
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lb_make_global_private_const(addr);
return lb_addr_load(p, addr);
return addr.addr;
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
lbValue loc = lb_emit_source_code_location_const(p, node);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lb_make_global_private_const(addr);
return lb_addr_load(p, addr);
return addr.addr;
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, node));
}
+2 -1
View File
@@ -671,7 +671,8 @@ void lb_debug_complete_types(lbModule *m) {
break;
case Type_Map:
bt = bt->Map.internal_type;
GB_ASSERT(t_raw_map != nullptr);
bt = base_type(t_raw_map);
/*fallthrough*/
case Type_Struct:
if (file == nullptr) {
+4 -3
View File
@@ -2215,7 +2215,7 @@ lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbVa
args[2] = lb_const_int(p->module, t_int, type_size_of(type));
res = lb_emit_runtime_call(p, "memory_equal", args);
} else {
lbValue value = lb_get_equal_proc_for_type(p->module, type);
lbValue value = lb_equal_proc_for_type(p->module, type);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
@@ -4131,7 +4131,8 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
}
GB_ASSERT(!build_context.no_dynamic_literals);
lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
for_array(field_index, cl->elems) {
Ast *elem = cl->elems[field_index];
@@ -4139,7 +4140,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
lbValue key = lb_build_expr(p, fv->field);
lbValue value = lb_build_expr(p, fv->value);
lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem);
lb_internal_dynamic_map_set(p, v.addr, type, key, value, elem);
}
break;
}
+7 -34
View File
@@ -67,6 +67,8 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->function_type_map, a);
map_init(&m->equal_procs, a);
map_init(&m->hasher_procs, a);
map_init(&m->map_get_procs, a);
map_init(&m->map_set_procs, a);
array_init(&m->procedures_to_generate, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values, a);
@@ -75,7 +77,8 @@ 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);
map_init(&m->map_cell_info_map, a, 0);
}
@@ -725,7 +728,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
return;
} else if (addr.kind == lbAddr_Map) {
lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return;
} else if (addr.kind == lbAddr_Context) {
lbAddr old_addr = lb_find_or_generate_context_ptr(p);
@@ -1931,38 +1934,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Map:
init_map_internal_types(type);
{
Type *internal_type = type->Map.internal_type;
GB_ASSERT(internal_type->kind == Type_Struct);
m->internal_type_level -= 1;
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);
LLVMTypeRef entries_fields[] = {
lb_type(m, t_rawptr), // data
lb_type(m, t_int), // len
lb_type(m, t_int), // cap
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);
}
GB_ASSERT(t_raw_map != nullptr);
return lb_type_internal(m, t_raw_map);
case Type_Struct:
{
+10 -2
View File
@@ -586,6 +586,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
// defer x = ... // defer is executed after the `defer`
// return // the values returned should be zeroed
// }
// NOTE(bill): REALLY, don't even bother.
lbAddr res = lb_add_local(p, e->type, e);
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
@@ -2319,10 +2320,17 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_type_equal_proc:
return lb_get_equal_proc_for_type(p->module, ce->args[0]->tav.type);
return lb_equal_proc_for_type(p->module, ce->args[0]->tav.type);
case BuiltinProc_type_hasher_proc:
return lb_get_hasher_proc_for_type(p->module, ce->args[0]->tav.type);
return lb_hasher_proc_for_type(p->module, ce->args[0]->tav.type);
case BuiltinProc_type_map_info:
return lb_gen_map_info_ptr(p->module, ce->args[0]->tav.type);
case BuiltinProc_type_map_cell_info:
return lb_gen_map_cell_info_ptr(p->module, ce->args[0]->tav.type);
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
+124 -13
View File
@@ -354,16 +354,6 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
}
break;
}
case Type_Map: {
lbValue entries = lb_map_entries_ptr(p, expr);
lbValue elem = lb_emit_struct_ep(p, entries, 0);
elem = lb_emit_load(p, elem);
lbValue entry = lb_emit_ptr_offset(p, elem, idx);
idx = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 3));
break;
}
case Type_Struct: {
GB_ASSERT(is_type_soa_struct(expr_type));
break;
@@ -380,6 +370,129 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
if (done_) *done_ = done;
}
lbValue lb_map_cell_index_static(lbProcedure *p, Type *type, lbValue cells_ptr, lbValue index) {
i64 size, len;
i64 elem_sz = type_size_of(type);
map_cell_size_and_len(type, &size, &len);
index = lb_emit_conv(p, index, t_uintptr);
if (size == len*elem_sz) {
lbValue elems_ptr = lb_emit_conv(p, cells_ptr, alloc_type_pointer(type));
return lb_emit_ptr_offset(p, elems_ptr, index);
}
lbValue cell_index = {};
lbValue data_index = {};
lbValue size_const = lb_const_int(p->module, t_uintptr, size);
lbValue len_const = lb_const_int(p->module, t_uintptr, len);
if (is_power_of_two(len)) {
u64 log2_len = floor_log2(cast(u64)len);
cell_index = log2_len == 0 ? index : lb_emit_arith(p, Token_Shr, index, lb_const_int(p->module, t_uintptr, log2_len), t_uintptr);
data_index = lb_emit_arith(p, Token_And, index, lb_const_int(p->module, t_uintptr, len-1), t_uintptr);
} else {
cell_index = lb_emit_arith(p, Token_Quo, index, len_const, t_uintptr);
data_index = lb_emit_arith(p, Token_Mod, index, len_const, t_uintptr);
}
lbValue elems_ptr = lb_emit_conv(p, cells_ptr, t_uintptr);
lbValue cell_offset = lb_emit_arith(p, Token_Mul, size_const, cell_index, t_uintptr);
elems_ptr = lb_emit_arith(p, Token_Add, elems_ptr, cell_offset, t_uintptr);
elems_ptr = lb_emit_conv(p, elems_ptr, alloc_type_pointer(type));
return lb_emit_ptr_offset(p, elems_ptr, data_index);
}
void lb_map_kvh_data_static(lbProcedure *p, lbValue map_value, lbValue *ks_, lbValue *vs_, lbValue *hs_) {
lbValue capacity = lb_map_cap(p, map_value);
lbValue ks = lb_map_data_uintptr(p, map_value);
lbValue vs = {};
lbValue hs = {};
if (ks_) *ks_ = ks;
if (vs_) *vs_ = vs;
if (hs_) *hs_ = hs;
}
lbValue lb_map_hash_is_valid(lbProcedure *p, lbValue hash) {
// N :: size_of(uintptr)*8 - 1
// (hash != 0) & (hash>>N == 0)
u64 top_bit_index = cast(u64)(type_size_of(t_uintptr)*8 - 1);
lbValue shift_amount = lb_const_int(p->module, t_uintptr, top_bit_index);
lbValue zero = lb_const_int(p->module, t_uintptr, 0);
lbValue not_empty = lb_emit_comp(p, Token_NotEq, hash, zero);
lbValue not_deleted = lb_emit_arith(p, Token_Shr, hash, shift_amount, t_uintptr);
not_deleted = lb_emit_comp(p, Token_CmpEq, not_deleted, zero);
return lb_emit_arith(p, Token_And, not_deleted, not_empty, t_uintptr);
}
void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type,
lbValue *val_, lbValue *key_, lbBlock **loop_, lbBlock **done_) {
lbModule *m = p->module;
Type *type = base_type(type_deref(expr.type));
GB_ASSERT(type->kind == Type_Map);
lbValue idx = {};
lbBlock *loop = nullptr;
lbBlock *done = nullptr;
lbBlock *body = nullptr;
lbBlock *hash_check = nullptr;
lbAddr index = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, index, lb_const_int(m, t_int, cast(u64)-1));
loop = lb_create_block(p, "for.index.loop");
lb_emit_jump(p, loop);
lb_start_block(p, loop);
lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
lb_addr_store(p, index, incr);
hash_check = lb_create_block(p, "for.index.hash_check");
body = lb_create_block(p, "for.index.body");
done = lb_create_block(p, "for.index.done");
lbValue map_value = lb_emit_load(p, expr);
lbValue capacity = lb_map_cap(p, map_value);
lbValue cond = lb_emit_comp(p, Token_Lt, incr, capacity);
lb_emit_if(p, cond, hash_check, done);
lb_start_block(p, hash_check);
idx = lb_addr_load(p, index);
lbValue ks = lb_map_data_uintptr(p, map_value);
lbValue vs = lb_emit_conv(p, lb_map_cell_index_static(p, type->Map.key, ks, capacity), alloc_type_pointer(type->Map.value));
lbValue hs = lb_emit_conv(p, lb_map_cell_index_static(p, type->Map.value, vs, capacity), alloc_type_pointer(t_uintptr));
// NOTE(bill): no need to use lb_map_cell_index_static for that hashes
// since it will always be packed without padding into the cells
lbValue hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, idx));
lbValue hash_cond = lb_map_hash_is_valid(p, hash);
lb_emit_if(p, hash_cond, body, loop);
lb_start_block(p, body);
lbValue key_ptr = lb_map_cell_index_static(p, type->Map.key, ks, idx);
lbValue val_ptr = lb_map_cell_index_static(p, type->Map.value, vs, idx);
lbValue key = lb_emit_load(p, key_ptr);
lbValue val = lb_emit_load(p, val_ptr);
if (val_) *val_ = val;
if (key_) *key_ = key;
if (loop_) *loop_ = loop;
if (done_) *done_ = done;
}
void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
@@ -749,9 +862,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
if (is_type_pointer(type_deref(map.type))) {
map = lb_emit_load(p, map);
}
lbValue entries_ptr = lb_map_entries_ptr(p, map);
lbValue count_ptr = lb_emit_struct_ep(p, entries_ptr, 1);
lb_build_range_indexed(p, map, val1_type, count_ptr, &val, &key, &loop, &done);
lb_build_range_map(p, map, val1_type, &val, &key, &loop, &done);
break;
}
case Type_Array: {
+4 -8
View File
@@ -666,7 +666,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
vals[3] = lb_get_equal_proc_for_type(m, t).value;
vals[3] = lb_equal_proc_for_type(m, t).value;
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
@@ -702,7 +702,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
vals[6] = is_raw_union.value;
vals[7] = is_custom_align.value;
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
vals[8] = lb_get_equal_proc_for_type(m, t).value;
vals[8] = lb_equal_proc_for_type(m, t).value;
}
@@ -788,15 +788,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_Map: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
init_map_internal_types(t);
lbValue gst = lb_type_info(m, t->Map.internal_type);
LLVMValueRef vals[5] = {
LLVMValueRef vals[3] = {
lb_type_info(m, t->Map.key).value,
lb_type_info(m, t->Map.value).value,
gst.value,
lb_get_equal_proc_for_type(m, t->Map.key).value,
lb_get_hasher_proc_for_type(m, t->Map.key).value
lb_gen_map_info_ptr(p->module, t).value
};
lbValue res = {};
+51 -44
View File
@@ -203,26 +203,19 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
if (is_type_uintptr(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_internally_pointer_like(src) && is_type_uintptr(dst)) {
} else if (is_type_internally_pointer_like(src) && is_type_uintptr(dst)) {
res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_integer(src) && is_type_internally_pointer_like(dst)) {
} else if (is_type_integer(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
return res;
} else if (is_type_internally_pointer_like(src) && is_type_integer(dst)) {
res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_internally_pointer_like(src) && is_type_internally_pointer_like(dst)) {
} else if (is_type_internally_pointer_like(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
}
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
} else if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
} else if (is_type_array_like(src) && is_type_simd_vector(dst)) {
@@ -239,9 +232,11 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
ap = lb_emit_conv(p, ap, alloc_type_pointer(value.type));
lb_emit_store(p, ap, value);
return lb_addr_load(p, addr);
}
if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
} else if (is_type_map(src) && are_types_identical(t_raw_map, t)) {
res.value = value.value;
res.type = t;
return res;
} else if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
lbValue s = lb_address_from_load_or_generate_local(p, value);
lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
return lb_emit_load(p, d);
@@ -990,14 +985,13 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
}
} else if (is_type_map(t)) {
init_map_internal_types(t);
Type *itp = alloc_type_pointer(t->Map.internal_type);
Type *itp = alloc_type_pointer(t_raw_map);
s = lb_emit_transmute(p, s, itp);
Type *gst = t->Map.internal_type;
GB_ASSERT(gst->kind == Type_Struct);
switch (index) {
case 0: result_type = get_struct_field_type(gst, 0); break;
case 1: result_type = get_struct_field_type(gst, 1); break;
case 0: result_type = get_struct_field_type(t_raw_map, 0); break;
case 1: result_type = get_struct_field_type(t_raw_map, 1); break;
case 2: result_type = get_struct_field_type(t_raw_map, 2); break;
}
} else if (is_type_array(t)) {
return lb_emit_array_epi(p, s, index);
@@ -1130,10 +1124,10 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
case Type_Map:
{
init_map_internal_types(t);
Type *gst = t->Map.internal_type;
switch (index) {
case 0: result_type = get_struct_field_type(gst, 0); break;
case 1: result_type = get_struct_field_type(gst, 1); break;
case 0: result_type = get_struct_field_type(t_raw_map, 0); break;
case 1: result_type = get_struct_field_type(t_raw_map, 1); break;
case 2: result_type = get_struct_field_type(t_raw_map, 2); break;
}
}
break;
@@ -1439,34 +1433,47 @@ lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) {
return lb_emit_struct_ev(p, da, 3);
}
lbValue lb_map_entries(lbProcedure *p, lbValue value) {
Type *t = base_type(value.type);
GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
init_map_internal_types(t);
i32 index = 1;
lbValue entries = lb_emit_struct_ev(p, value, index);
return entries;
}
lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value) {
Type *t = base_type(type_deref(value.type));
GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
init_map_internal_types(t);
i32 index = 1;
lbValue entries = lb_emit_struct_ep(p, value, index);
return entries;
}
lbValue lb_map_len(lbProcedure *p, lbValue value) {
lbValue entries = lb_map_entries(p, value);
return lb_dynamic_array_len(p, entries);
GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
lbValue len = lb_emit_struct_ev(p, value, 1);
return lb_emit_conv(p, len, t_int);
}
lbValue lb_map_len_ptr(lbProcedure *p, lbValue map_ptr) {
Type *type = map_ptr.type;
GB_ASSERT(is_type_pointer(type));
type = type_deref(type);
GB_ASSERT_MSG(is_type_map(type) || are_types_identical(type, t_raw_map), "%s", type_to_string(type));
return lb_emit_struct_ep(p, map_ptr, 1);
}
lbValue lb_map_cap(lbProcedure *p, lbValue value) {
lbValue entries = lb_map_entries(p, value);
return lb_dynamic_array_cap(p, entries);
GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
lbValue zero = lb_const_int(p->module, t_uintptr, 0);
lbValue one = lb_const_int(p->module, t_uintptr, 1);
lbValue mask = lb_const_int(p->module, t_uintptr, MAP_CACHE_LINE_SIZE-1);
lbValue data = lb_emit_struct_ev(p, value, 0);
lbValue log2_cap = lb_emit_arith(p, Token_And, data, mask, t_uintptr);
lbValue cap = lb_emit_arith(p, Token_Shl, one, log2_cap, t_uintptr);
lbValue cmp = lb_emit_comp(p, Token_CmpEq, data, zero);
return lb_emit_conv(p, lb_emit_select(p, cmp, zero, cap), t_int);
}
lbValue lb_map_data_uintptr(lbProcedure *p, lbValue value) {
GB_ASSERT(is_type_map(value.type) || are_types_identical(value.type, t_raw_map));
lbValue data = lb_emit_struct_ev(p, value, 0);
u64 mask_value = 0;
if (build_context.word_size == 4) {
mask_value = 0xfffffffful & ~(MAP_CACHE_LINE_SIZE-1);
} else {
mask_value = 0xffffffffffffffffull & ~(MAP_CACHE_LINE_SIZE-1);
}
lbValue mask = lb_const_int(p->module, t_uintptr, mask_value);
return lb_emit_arith(p, Token_And, data, mask, t_uintptr);
}
lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
Type *t = base_type(value.type);
bool is_ptr = false;
+6
View File
@@ -638,6 +638,7 @@ enum BuildFlagKind {
BuildFlag_StrictStyleInitOnly,
BuildFlag_ForeignErrorProcedures,
BuildFlag_DisallowRTTI,
BuildFlag_UseStaticMapCalls,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -814,6 +815,8 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_UseStaticMapCalls, str_lit("use-static-map-calls"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
@@ -1414,6 +1417,9 @@ bool parse_build_flags(Array<String> args) {
case BuildFlag_DisallowRTTI:
build_context.disallow_rtti = true;
break;
case BuildFlag_UseStaticMapCalls:
build_context.use_static_map_calls = true;
break;
case BuildFlag_DefaultToNilAllocator:
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+15 -16
View File
@@ -226,8 +226,6 @@ struct TypeProc {
TYPE_KIND(Map, struct { \
Type *key; \
Type *value; \
Type *entry_type; \
Type *internal_type; \
Type *lookup_result_type; \
}) \
TYPE_KIND(Struct, TypeStruct) \
@@ -685,13 +683,18 @@ 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_raw_map = nullptr;
gb_global Type *t_map_info_ptr = nullptr;
gb_global Type *t_map_cell_info_ptr = nullptr;
gb_global Type *t_raw_map_ptr = nullptr;
gb_global Type *t_equal_proc = nullptr;
gb_global Type *t_hasher_proc = nullptr;
gb_global Type *t_map_get_proc = nullptr;
gb_global Type *t_map_set_proc = nullptr;
gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
@@ -1926,7 +1929,7 @@ bool is_type_valid_for_keys(Type *t) {
if (is_type_untyped(t)) {
return false;
}
return is_type_comparable(t);
return type_size_of(t) > 0 && is_type_comparable(t);
}
bool is_type_valid_bit_set_elem(Type *t) {
@@ -3333,8 +3336,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);
@@ -3345,15 +3346,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;
}
@@ -3798,11 +3796,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;
+6
View File
@@ -0,0 +1,6 @@
ODIN=../../odin
all: map_test
map_test:
$(ODIN) run test_map.odin -file -vet -strict-style -o:minimal
+4
View File
@@ -0,0 +1,4 @@
@echo off
set PATH_TO_ODIN==..\..\odin
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal
rem -define:SEED=42
+382
View File
@@ -0,0 +1,382 @@
package test_internal_map
import "core:fmt"
import "core:intrinsics"
import "core:math/rand"
import "core:mem"
import "core:os"
import "core:testing"
seed: u64
ENTRY_COUNTS := []int{11, 101, 1_001, 10_001, 100_001, 1_000_001}
@test
map_insert_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0)
for entries in ENTRY_COUNTS {
fmt.printf("[map_insert_random_key_value] Testing %v entries.\n", entries)
m: map[i64]i64
defer delete(m)
unique_keys := 0
r := rand.create(seed + seed_incr)
for _ in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
if k not_in m {
unique_keys += 1
}
m[k] = v
}
key_count := 0
for k in m {
key_count += 1
}
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m)))
// Reset randomizer and verify
r = rand.create(seed + seed_incr)
num_fails := 0
for _ in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
cond := m[k] == v
if !cond {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
}
}
seed_incr += 1
}
}
@test
map_update_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0)
for entries in ENTRY_COUNTS {
fmt.printf("[map_update_random_key_value] Testing %v entries.\n", entries)
m: map[i64]i64
defer delete(m)
unique_keys := 0
r := rand.create(seed + seed_incr)
for _ in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
if k not_in m {
unique_keys += 1
}
m[k] = v
}
key_count := 0
for k in m {
key_count += 1
}
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m)))
half_entries := entries / 2
// Reset randomizer and update half the entries
r = rand.create(seed + seed_incr)
for _ in 0..<half_entries {
k := rand.int63(&r)
v := rand.int63(&r)
m[k] = v + 42
}
// Reset randomizer and verify
r = rand.create(seed + seed_incr)
num_fails := 0
for i in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
diff := i64(42) if i < half_entries else i64(0)
cond := m[k] == (v + diff)
if !cond {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
}
}
seed_incr += 1
}
}
@test
map_delete_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0)
for entries in ENTRY_COUNTS {
fmt.printf("[map_delete_random_key_value] Testing %v entries.\n", entries)
m: map[i64]i64
defer delete(m)
unique_keys := 0
r := rand.create(seed + seed_incr)
for _ in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
if k not_in m {
unique_keys += 1
}
m[k] = v
}
key_count := 0
for k in m {
key_count += 1
}
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m)))
half_entries := entries / 2
// Reset randomizer and delete half the entries
r = rand.create(seed + seed_incr)
for _ in 0..<half_entries {
k := rand.int63(&r)
_ = rand.int63(&r)
delete_key(&m, k)
}
// Reset randomizer and verify
r = rand.create(seed + seed_incr)
num_fails := 0
for i in 0..<entries {
k := rand.int63(&r)
v := rand.int63(&r)
if i < half_entries {
if k in m {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted, got %v", k, m[k]))
}
} else {
if k not_in m {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] = %v", k, v))
} else if m[k] != v {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
}
}
}
seed_incr += 1
}
}
@test
set_insert_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0)
for entries in ENTRY_COUNTS {
fmt.printf("[set_insert_random_key_value] Testing %v entries.\n", entries)
m: map[i64]struct{}
defer delete(m)
unique_keys := 0
r := rand.create(seed + seed_incr)
for _ in 0..<entries {
k := rand.int63(&r)
if k not_in m {
unique_keys += 1
}
m[k] = {}
}
key_count := 0
for k in m {
key_count += 1
}
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m)))
// Reset randomizer and verify
r = rand.create(seed + seed_incr)
num_fails := 0
for _ in 0..<entries {
k := rand.int63(&r)
cond := k in m
if !cond {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] to exist", k))
}
}
seed_incr += 1
}
}
@test
set_delete_random_key_value :: proc(t: ^testing.T) {
seed_incr := u64(0)
for entries in ENTRY_COUNTS {
fmt.printf("[set_delete_random_key_value] Testing %v entries.\n", entries)
m: map[i64]struct{}
defer delete(m)
unique_keys := 0
r := rand.create(seed + seed_incr)
for _ in 0..<entries {
k := rand.int63(&r)
if k not_in m {
unique_keys += 1
}
m[k] = {}
}
key_count := 0
for k in m {
key_count += 1
}
expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
expect(t, len(m) == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v", unique_keys, len(m)))
half_entries := entries / 2
// Reset randomizer and delete half the entries
r = rand.create(seed + seed_incr)
for _ in 0..<half_entries {
k := rand.int63(&r)
delete_key(&m, k)
}
// Reset randomizer and verify
r = rand.create(seed + seed_incr)
num_fails := 0
for i in 0..<entries {
k := rand.int63(&r)
if i < half_entries {
if k in m {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted", k))
}
} else {
if k not_in m {
num_fails += 1
if num_fails > 5 {
fmt.println("... and more")
break
}
expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] to exist", k))
}
}
}
seed_incr += 1
}
}
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
main :: proc() {
t := testing.T{}
// Allow tests to be repeatable
SEED :: #config(SEED, -1)
when SEED > 0 {
seed = u64(SEED)
} else {
seed = u64(intrinsics.read_cycle_counter())
}
fmt.println("Initialized seed to", seed)
mem_track_test(&t, map_insert_random_key_value)
mem_track_test(&t, map_update_random_key_value)
mem_track_test(&t, map_delete_random_key_value)
mem_track_test(&t, set_insert_random_key_value)
mem_track_test(&t, set_delete_random_key_value)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}
mem_track_test :: proc(t: ^testing.T, test: proc(t: ^testing.T)) {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
test(t)
expect(t, len(track.allocation_map) == 0, "Expected no leaks.")
expect(t, len(track.bad_free_array) == 0, "Expected no leaks.")
for _, leak in track.allocation_map {
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
}
for bad_free in track.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
}
}
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}