diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 6b0c3eb84..dfbe0d053 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -1,11 +1,7 @@ // This is the runtime code required by the compiler // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order -package runtime - -import "intrinsics" -_ :: intrinsics; - +// // Naming Conventions: // In general, Ada_Case for types and snake_case for values // @@ -16,12 +12,13 @@ _ :: intrinsics; // Procedures: snake_case // Local Variables: snake_case // Constant Variables: SCREAMING_SNAKE_CASE - - +// // IMPORTANT NOTE(bill): `type_info_of` cannot be used within a // #shared_global_scope due to the internals of the compiler. // This could change at a later date if the all these data structures are // implemented within the compiler rather than in this "preload" file +// +package runtime // NOTE(bill): This must match the compiler's Calling_Convention :: enum u8 { @@ -87,7 +84,7 @@ Type_Info_Enumerated_Array :: struct { }; Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}; Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}; -Type_Info_Tuple :: struct { // Only really used for procedures +Type_Info_Tuple :: struct { // Only used for procedures parameters and results types: []^Type_Info, names: []string, }; @@ -237,9 +234,6 @@ args__: []cstring; // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) -@builtin -Maybe :: union(T: typeid) #maybe {T}; - Source_Code_Location :: struct { file_path: string, @@ -521,13 +515,6 @@ __init_context :: proc "contextless" (c: ^Context) { c.logger.data = nil; } -@thread_local global_default_temp_allocator_data: Default_Temp_Allocator; - -@builtin -init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { - default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator); -} - default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) { print_caller_location(loc); @@ -540,1224 +527,3 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code print_byte('\n'); debug_trap(); } - - - - -@builtin -copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int { - n := max(0, min(len(dst), len(src))); - if n > 0 { - mem_copy(raw_data(dst), raw_data(src), n*size_of(E)); - } - return n; -} -@builtin -copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int { - n := max(0, min(len(dst), len(src))); - if n > 0 { - mem_copy(raw_data(dst), raw_data(src), n); - } - return n; -} -@builtin -copy :: proc{copy_slice, copy_from_string}; - - - -@builtin -unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { - bounds_check_error_loc(loc, index, len(array)); - n := len(array)-1; - if index != n { - array[index] = array[n]; - } - pop(array); -} - -@builtin -ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { - bounds_check_error_loc(loc, index, len(array)); - if index+1 < len(array) { - copy(array[index:], array[index+1:]); - } - pop(array); -} - -@builtin -remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) { - slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)); - n := max(hi-lo, 0); - if n > 0 { - if hi != len(array) { - copy(array[lo:], array[hi:]); - } - (^Raw_Dynamic_Array)(array).len -= n; - } -} - - -@builtin -pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc); - res = array[len(array)-1]; - (^Raw_Dynamic_Array)(array).len -= 1; - return res; -} - - -@builtin -pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { - if len(array) == 0 { - return; - } - res, ok = array[len(array)-1], true; - (^Raw_Dynamic_Array)(array).len -= 1; - return; -} - -@builtin -pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc); - res = array[0]; - if len(array) > 1 { - copy(array[0:], array[1:]); - } - (^Raw_Dynamic_Array)(array).len -= 1; - return res; -} - -@builtin -pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { - if len(array) == 0 { - return; - } - res, ok = array[0], true; - if len(array) > 1 { - copy(array[0:], array[1:]); - } - (^Raw_Dynamic_Array)(array).len -= 1; - return; -} - - -@builtin -clear :: proc{clear_dynamic_array, clear_map}; - -@builtin -reserve :: proc{reserve_dynamic_array, reserve_map}; - -@builtin -resize :: proc{resize_dynamic_array}; - - -@builtin -free :: proc{mem_free}; - -@builtin -free_all :: proc{mem_free_all}; - - - -@builtin -delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) { - mem_free(raw_data(str), allocator, loc); -} -@builtin -delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) { - mem_free((^byte)(str), allocator, loc); -} -@builtin -delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) { - mem_free(raw_data(array), array.allocator, loc); -} -@builtin -delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) { - mem_free(raw_data(array), allocator, loc); -} -@builtin -delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) { - raw := transmute(Raw_Map)m; - delete_slice(raw.hashes); - mem_free(raw.entries.data, raw.entries.allocator, loc); -} - - -@builtin -delete :: proc{ - delete_string, - delete_cstring, - delete_dynamic_array, - delete_slice, - delete_map, -}; - - -@builtin -new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T { - ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); - if ptr != nil { ptr^ = T{}; } - return ptr; -} - -@builtin -new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T { - ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); - if ptr != nil { ptr^ = data; } - return ptr; -} - -make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T { - make_slice_error_loc(loc, len); - data := mem_alloc(size_of(E)*len, alignment, allocator, loc); - if data == nil && size_of(E) != 0 { - return nil; - } - // mem_zero(data, size_of(E)*len); - s := Raw_Slice{data, len}; - return transmute(T)s; -} - -@builtin -make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { - return make_aligned(T, len, align_of(E), allocator, loc); -} - -@builtin -make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T { - return make_dynamic_array_len_cap(T, 0, 16, allocator, loc); -} - -@builtin -make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { - return make_dynamic_array_len_cap(T, len, len, allocator, loc); -} - -@builtin -make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T { - make_dynamic_array_error_loc(loc, len, cap); - data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc); - s := Raw_Dynamic_Array{data, len, cap, allocator}; - if data == nil && size_of(E) != 0 { - s.len, s.cap = 0, 0; - } - // mem_zero(data, size_of(E)*cap); - return transmute(T)s; -} - -@builtin -make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T { - make_map_expr_error_loc(loc, cap); - context.allocator = allocator; - - m: T; - reserve_map(&m, cap); - return m; -} - -@builtin -make :: proc{ - make_slice, - make_dynamic_array, - make_dynamic_array_len, - make_dynamic_array_len_cap, - make_map, -}; - - - -@builtin -clear_map :: inline 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] = -1; - } -} - -@builtin -reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { - if m != nil { - __dynamic_map_reserve(__get_map_header(m), capacity); - } -} - -@builtin -delete_key :: proc(m: ^$T/map[$K]$V, key: K) { - if m != nil { - __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key)); - } -} - - - -@builtin -append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { - if array == nil { - return; - } - - arg_len := 1; - - if cap(array) < len(array)+arg_len { - cap := 2 * cap(array) + max(8, arg_len); - _ = reserve(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 0 { - a := (^Raw_Dynamic_Array)(array); - if size_of(E) != 0 { - data := (^E)(a.data); - assert(data != nil); - val := arg; - mem_copy(ptr_offset(data, a.len), &val, size_of(E)); - } - a.len += arg_len; - } -} -@builtin -append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { - if array == nil { - return; - } - - arg_len := len(args); - if arg_len <= 0 { - return; - } - - - if cap(array) < len(array)+arg_len { - cap := 2 * cap(array) + max(8, arg_len); - _ = reserve(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 0 { - a := (^Raw_Dynamic_Array)(array); - if size_of(E) != 0 { - data := (^E)(a.data); - assert(data != nil); - mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); - } - a.len += arg_len; - } -} -@builtin -append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { - args := transmute([]E)arg; - append_elems(array=array, args=args, loc=loc); -} - -@builtin -reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - - old_cap := cap(array); - if capacity <= old_cap { - return true; - } - - if array.allocator.procedure == nil { - array.allocator = context.allocator; - } - assert(array.allocator.procedure != nil); - - - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return true; - } - - cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr)); - assert(cap_ptr^ == old_cap); - - - old_size := 0; - new_size := 0; - - max_align := 0; - for i in 0.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - arg_copy := arg; - arg_ptr := &arg_copy; - - max_align := 0; - for i in 0.. 0 { - ti := type_info_of(typeid_of(T)); - ti = type_info_base(ti); - si := &ti.variant.(Type_Info_Struct); - field_count := uintptr(len(si.offsets) - 3); - - if field_count == 0 { - return; - } - - data := (^rawptr)(array)^; - - len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); - - - soa_offset := 0; - item_offset := 0; - - args_ptr := &args[0]; - - max_align := 0; - for i in 0.. (ok: bool) #no_bounds_check { - if array == nil { - return; - } - n := len(array); - m :: 1; - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - array[index] = arg; - } - ok = true; - } - return; -} - -@builtin -insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - when size_of(E) != 0 { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - } - ok = true; - } - return; -} - -@builtin -insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { - if array == nil { - return; - } - if len(args) == 0 { - ok = true; - return; - } - - n := len(array); - m := len(args); - resize(array, n+m, loc); - if n+m <= len(array) { - copy(array[index+m:], array[index:]); - copy(array[index:], args); - ok = true; - } - return; -} - -@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; - - - - -@builtin -clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { - if array != nil { - (^Raw_Dynamic_Array)(array).len = 0; - } -} - -@builtin -reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if capacity <= a.cap { - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := capacity * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.cap = capacity; - return true; -} - -@builtin -resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { - if array == nil { - return false; - } - a := (^Raw_Dynamic_Array)(array); - - if length <= a.cap { - a.len = max(length, 0); - return true; - } - - if a.allocator.procedure == nil { - a.allocator = context.allocator; - } - assert(a.allocator.procedure != nil); - - old_size := a.cap * size_of(E); - new_size := length * size_of(E); - allocator := a.allocator; - - new_data := allocator.procedure( - allocator.data, .Resize, new_size, align_of(E), - a.data, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - a.data = new_data; - a.len = length; - a.cap = length; - return true; -} - - - -@builtin -incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ |= {elem}; - return s^; -} -@builtin -incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ |= {elem}; - } - return s^; -} -@builtin -incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ |= other; - return s^; -} -@builtin -excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { - s^ &~= {elem}; - return s^; -} -@builtin -excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { - for elem in elems { - s^ &~= {elem}; - } - return s^; -} -@builtin -excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { - s^ &~= other; - return s^; -} - -@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; -@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; - - -@builtin -card :: proc(s: $S/bit_set[$E; $U]) -> int { - when size_of(S) == 1 { - foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } - return int(count_ones(transmute(u8)s)); - } else when size_of(S) == 2 { - foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } - return int(count_ones(transmute(u16)s)); - } else when size_of(S) == 4 { - foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } - return int(count_ones(transmute(u32)s)); - } else when size_of(S) == 8 { - foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } - return int(count_ones(transmute(u64)s)); - } else when size_of(S) == 16 { - foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } - return int(count_ones(transmute(u128)s)); - } else { - #panic("Unhandled card bit_set size"); - } -} - - - -@builtin -raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { - return (^E)(a); -} -@builtin -raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { - ptr := (transmute(Raw_Slice)s).data; - return (^E)(ptr); -} -@builtin -raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { - ptr := (transmute(Raw_Dynamic_Array)s).data; - return (^E)(ptr); -} -@builtin -raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { - return (transmute(Raw_String)s).data; -} - -@builtin -raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; - - - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -assert :: proc(condition: bool, message := "", loc := #caller_location) { - if !condition { - proc(message: string, loc: Source_Code_Location) { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("runtime assertion", message, loc); - }(message, loc); - } -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -panic :: proc(message: string, loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("panic", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unimplemented :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("not yet implemented", message, loc); -} - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unreachable :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - if message != "" { - p("internal error", message, loc); - } else { - p("internal error", "entered unreachable code", loc); - } -} - - -// Dynamic Array - - -__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { - array := (^Raw_Dynamic_Array)(array_); - array.allocator = context.allocator; - assert(array.allocator.procedure != nil); - - if cap > 0 { - __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); - array.len = len; - } -} - -__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written - // assuming that appending/reserving will set the allocator, if it is not already set. - if array.allocator.procedure == nil { - array.allocator = context.allocator; - } - assert(array.allocator.procedure != nil); - - if cap <= array.cap { - return true; - } - - old_size := array.cap * elem_size; - new_size := cap * elem_size; - allocator := array.allocator; - - new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); - if new_data != nil || elem_size == 0 { - array.data = new_data; - array.cap = cap; - return true; - } - return false; -} - -__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { - array := (^Raw_Dynamic_Array)(array_); - - ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); - if ok { - array.len = len; - } - return ok; -} - - -__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, - items: rawptr, item_count: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - if items == nil { - return 0; - } - if item_count <= 0 { - return 0; - } - - - ok := true; - if array.cap <= array.len+item_count { - cap := 2 * array.cap + max(8, item_count); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - - mem_copy(rawptr(data), items, elem_size * item_count); - array.len += item_count; - return array.len; -} - -__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { - array := (^Raw_Dynamic_Array)(array_); - - ok := true; - if array.cap <= array.len+1 { - cap := 2 * array.cap + max(8, 1); - ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); - } - // TODO(bill): Better error handling for failed reservation - if !ok { - return array.len; - } - - assert(array.data != nil); - data := uintptr(array.data) + uintptr(elem_size*array.len); - mem_zero(rawptr(data), elem_size); - array.len += 1; - return array.len; -} - - - - -// Map - -__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { - header := Map_Header{m = (^Raw_Map)(m)}; - Entry :: struct { - key: Map_Key, - next: int, - value: V, - }; - - header.is_key_string = intrinsics.type_is_string(K); - header.entry_size = int(size_of(Entry)); - header.entry_align = int(align_of(Entry)); - header.value_offset = uintptr(offset_of(Entry, value)); - header.value_size = int(size_of(V)); - return header; -} - -__get_map_key :: proc "contextless" (k: $K) -> Map_Key { - key := k; - map_key: Map_Key; - - T :: intrinsics.type_core_type(K); - - when intrinsics.type_is_integer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } - else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } - else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled integer size"); } - } else when intrinsics.type_is_rune(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64((^rune)(&key)^); - } else when intrinsics.type_is_pointer(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - map_key.key.val = u64(uintptr((^rawptr)(&key)^)); - } else when intrinsics.type_is_float(T) { - map_key.hash = default_hash_ptr(&key, size_of(T)); - - sz :: 8*size_of(T); - when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } - else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } - else { #panic("Unhandled float size"); } - } else when intrinsics.type_is_string(T) { - #assert(T == string); - str := (^string)(&key)^; - map_key.hash = default_hash_string(str); - map_key.key.str = str; - } else { - #panic("Unhandled map key type"); - } - - return map_key; -} - -_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { - h: u64 = seed; - for b in data { - h = (h ~ u64(b)) * 0x100000001b3; - } - return h; -} - - -default_hash :: inline proc "contextless" (data: []byte) -> u64 { - return _fnv64a(data); -} -default_hash_string :: inline proc "contextless" (s: string) -> u64 { - return default_hash(transmute([]byte)(s)); -} -default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { - s := Raw_Slice{data, size}; - return default_hash(transmute([]byte)(s)); -} - - -source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { - hash := _fnv64a(transmute([]byte)s.file_path); - hash = hash ~ (u64(s.line) * 0x100000001b3); - hash = hash ~ (u64(s.column) * 0x100000001b3); - return hash; -} - - - -__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { - array := (^Raw_Slice)(array_); - - if new_count < array.len { - return true; - } - - assert(allocator.procedure != nil); - - old_size := array.len*size_of(T); - new_size := new_count*size_of(T); - - new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc); - if new_data == nil { - return false; - } - array.data = new_data; - array.len = new_count; - return true; -} - -__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { - __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc); - - old_len := len(m.hashes); - __slice_resize(&m.hashes, cap, m.entries.allocator, loc); - for i in old_len.. rawptr { - index := __dynamic_map_find(h, key).entry_index; - if index >= 0 { - data := uintptr(__dynamic_map_get_entry(h, index)); - return rawptr(data + h.value_offset); - } - return nil; -} - -__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { - index: int; - assert(value != nil); - - if len(h.m.hashes) == 0 { - __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc); - __dynamic_map_grow(h, loc); - } - - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - index = fr.entry_index; - } else { - index = __dynamic_map_add_entry(h, key, loc); - if fr.entry_prev >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_prev); - entry.next = index; - } else { - h.m.hashes[fr.hash_index] = index; - } - } - { - e := __dynamic_map_get_entry(h, index); - e.key = key; - val := (^byte)(uintptr(e) + h.value_offset); - mem_copy(val, value, h.value_size); - } - - if __dynamic_map_full(h) { - __dynamic_map_grow(h, loc); - } -} - - -__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { - // TODO(bill): Determine an efficient growing rate - new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP); - __dynamic_map_rehash(h, new_count, loc); -} - -__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { - return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; -} - - -__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { - if a.hash == b.hash { - if h.is_key_string { - return a.key.str == b.key.str; - } else { - return a.key.val == b.key.val; - } - return true; - } - return false; -} - -__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { - fr := Map_Find_Result{-1, -1, -1}; - if n := u64(len(m.hashes)); n > 0 { - fr.hash_index = int(key.hash % n); - fr.entry_index = m.hashes[fr.hash_index]; - for fr.entry_index >= 0 { - entry := __dynamic_map_get_entry(h, fr.entry_index); - if __dynamic_map_hash_equal(h, entry.key, key) { - return fr; - } - fr.entry_prev = fr.entry_index; - fr.entry_index = entry.next; - } - } - return fr; -} - -__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { - prev := m.entries.len; - c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); - if c != prev { - end := __dynamic_map_get_entry(h, c-1); - end.key = key; - end.next = -1; - } - return prev; -} - -__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { - fr := __dynamic_map_find(h, key); - if fr.entry_index >= 0 { - __dynamic_map_erase(h, fr); - } -} - -__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { - assert(0 <= index && index < m.entries.len); - return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); -} - -__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { - if fr.entry_prev < 0 { - m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; - } else { - prev := __dynamic_map_get_entry(h, fr.entry_prev); - curr := __dynamic_map_get_entry(h, fr.entry_index); - prev.next = curr.next; - } - if (fr.entry_index == m.entries.len-1) { - // NOTE(bill): No need to do anything else, just pop - } else { - old := __dynamic_map_get_entry(h, fr.entry_index); - end := __dynamic_map_get_entry(h, m.entries.len-1); - mem_copy(old, end, entry_size); - - if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { - last_entry := __dynamic_map_get_entry(h, last.entry_prev); - last_entry.next = fr.entry_index; - } else { - m.hashes[last.hash_index] = fr.entry_index; - } - } - - // TODO(bill): Is this correct behaviour? - m.entries.len -= 1; -} diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin new file mode 100644 index 000000000..0d430345e --- /dev/null +++ b/core/runtime/core_builtin.odin @@ -0,0 +1,823 @@ +package runtime + +@builtin +Maybe :: union(T: typeid) #maybe {T}; + +@thread_local global_default_temp_allocator_data: Default_Temp_Allocator; + +@builtin +init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { + default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator); +} + + +@builtin +copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int { + n := max(0, min(len(dst), len(src))); + if n > 0 { + mem_copy(raw_data(dst), raw_data(src), n*size_of(E)); + } + return n; +} +@builtin +copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int { + n := max(0, min(len(dst), len(src))); + if n > 0 { + mem_copy(raw_data(dst), raw_data(src), n); + } + return n; +} +@builtin +copy :: proc{copy_slice, copy_from_string}; + + + +@builtin +unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { + bounds_check_error_loc(loc, index, len(array)); + n := len(array)-1; + if index != n { + array[index] = array[n]; + } + pop(array); +} + +@builtin +ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) { + bounds_check_error_loc(loc, index, len(array)); + if index+1 < len(array) { + copy(array[index:], array[index+1:]); + } + pop(array); +} + +@builtin +remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) { + slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)); + n := max(hi-lo, 0); + if n > 0 { + if hi != len(array) { + copy(array[lo:], array[hi:]); + } + (^Raw_Dynamic_Array)(array).len -= n; + } +} + + +@builtin +pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, "", loc); + res = array[len(array)-1]; + (^Raw_Dynamic_Array)(array).len -= 1; + return res; +} + + +@builtin +pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return; + } + res, ok = array[len(array)-1], true; + (^Raw_Dynamic_Array)(array).len -= 1; + return; +} + +@builtin +pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, "", loc); + res = array[0]; + if len(array) > 1 { + copy(array[0:], array[1:]); + } + (^Raw_Dynamic_Array)(array).len -= 1; + return res; +} + +@builtin +pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return; + } + res, ok = array[0], true; + if len(array) > 1 { + copy(array[0:], array[1:]); + } + (^Raw_Dynamic_Array)(array).len -= 1; + return; +} + + +@builtin +clear :: proc{clear_dynamic_array, clear_map}; + +@builtin +reserve :: proc{reserve_dynamic_array, reserve_map}; + +@builtin +resize :: proc{resize_dynamic_array}; + + +@builtin +free :: proc{mem_free}; + +@builtin +free_all :: proc{mem_free_all}; + + + +@builtin +delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) { + mem_free(raw_data(str), allocator, loc); +} +@builtin +delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) { + mem_free((^byte)(str), allocator, loc); +} +@builtin +delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) { + mem_free(raw_data(array), array.allocator, loc); +} +@builtin +delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) { + mem_free(raw_data(array), allocator, loc); +} +@builtin +delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) { + raw := transmute(Raw_Map)m; + delete_slice(raw.hashes); + mem_free(raw.entries.data, raw.entries.allocator, loc); +} + + +@builtin +delete :: proc{ + delete_string, + delete_cstring, + delete_dynamic_array, + delete_slice, + delete_map, +}; + + +@builtin +new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T { + ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); + if ptr != nil { ptr^ = T{}; } + return ptr; +} + +@builtin +new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T { + ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc)); + if ptr != nil { ptr^ = data; } + return ptr; +} + +make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T { + make_slice_error_loc(loc, len); + data := mem_alloc(size_of(E)*len, alignment, allocator, loc); + if data == nil && size_of(E) != 0 { + return nil; + } + // mem_zero(data, size_of(E)*len); + s := Raw_Slice{data, len}; + return transmute(T)s; +} + +@builtin +make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { + return make_aligned(T, len, align_of(E), allocator, loc); +} + +@builtin +make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T { + return make_dynamic_array_len_cap(T, 0, 16, allocator, loc); +} + +@builtin +make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T { + return make_dynamic_array_len_cap(T, len, len, allocator, loc); +} + +@builtin +make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T { + make_dynamic_array_error_loc(loc, len, cap); + data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc); + s := Raw_Dynamic_Array{data, len, cap, allocator}; + if data == nil && size_of(E) != 0 { + s.len, s.cap = 0, 0; + } + // mem_zero(data, size_of(E)*cap); + return transmute(T)s; +} + +@builtin +make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T { + make_map_expr_error_loc(loc, cap); + context.allocator = allocator; + + m: T; + reserve_map(&m, cap); + return m; +} + +@builtin +make :: proc{ + make_slice, + make_dynamic_array, + make_dynamic_array_len, + make_dynamic_array_len_cap, + make_map, +}; + + + +@builtin +clear_map :: inline 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] = -1; + } +} + +@builtin +reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { + if m != nil { + __dynamic_map_reserve(__get_map_header(m), capacity); + } +} + +@builtin +delete_key :: proc(m: ^$T/map[$K]$V, key: K) { + if m != nil { + __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key)); + } +} + + + +@builtin +append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { + if array == nil { + return; + } + + arg_len := 1; + + if cap(array) < len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len); + _ = reserve(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 0 { + a := (^Raw_Dynamic_Array)(array); + if size_of(E) != 0 { + data := (^E)(a.data); + assert(data != nil); + val := arg; + mem_copy(ptr_offset(data, a.len), &val, size_of(E)); + } + a.len += arg_len; + } +} +@builtin +append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { + if array == nil { + return; + } + + arg_len := len(args); + if arg_len <= 0 { + return; + } + + + if cap(array) < len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len); + _ = reserve(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 0 { + a := (^Raw_Dynamic_Array)(array); + if size_of(E) != 0 { + data := (^E)(a.data); + assert(data != nil); + mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); + } + a.len += arg_len; + } +} +@builtin +append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { + args := transmute([]E)arg; + append_elems(array=array, args=args, loc=loc); +} + +@builtin +reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + + old_cap := cap(array); + if capacity <= old_cap { + return true; + } + + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return true; + } + + cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr)); + assert(cap_ptr^ == old_cap); + + + old_size := 0; + new_size := 0; + + max_align := 0; + for i in 0.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + arg_copy := arg; + arg_ptr := &arg_copy; + + max_align := 0; + for i in 0.. 0 { + ti := type_info_of(typeid_of(T)); + ti = type_info_base(ti); + si := &ti.variant.(Type_Info_Struct); + field_count := uintptr(len(si.offsets) - 3); + + if field_count == 0 { + return; + } + + data := (^rawptr)(array)^; + + len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr)); + + + soa_offset := 0; + item_offset := 0; + + args_ptr := &args[0]; + + max_align := 0; + for i in 0.. (ok: bool) #no_bounds_check { + if array == nil { + return; + } + n := len(array); + m :: 1; + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + array[index] = arg; + } + ok = true; + } + return; +} + +@builtin +insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + when size_of(E) != 0 { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + } + ok = true; + } + return; +} + +@builtin +insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { + if array == nil { + return; + } + if len(args) == 0 { + ok = true; + return; + } + + n := len(array); + m := len(args); + resize(array, n+m, loc); + if n+m <= len(array) { + copy(array[index+m:], array[index:]); + copy(array[index:], args); + ok = true; + } + return; +} + +@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}; + + + + +@builtin +clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { + if array != nil { + (^Raw_Dynamic_Array)(array).len = 0; + } +} + +@builtin +reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if capacity <= a.cap { + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := capacity * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.cap = capacity; + return true; +} + +@builtin +resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { + if array == nil { + return false; + } + a := (^Raw_Dynamic_Array)(array); + + if length <= a.cap { + a.len = max(length, 0); + return true; + } + + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } + assert(a.allocator.procedure != nil); + + old_size := a.cap * size_of(E); + new_size := length * size_of(E); + allocator := a.allocator; + + new_data := allocator.procedure( + allocator.data, .Resize, new_size, align_of(E), + a.data, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + a.data = new_data; + a.len = length; + a.cap = length; + return true; +} + + + +@builtin +incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ |= {elem}; + return s^; +} +@builtin +incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ |= {elem}; + } + return s^; +} +@builtin +incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ |= other; + return s^; +} +@builtin +excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S { + s^ &~= {elem}; + return s^; +} +@builtin +excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S { + for elem in elems { + s^ &~= {elem}; + } + return s^; +} +@builtin +excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S { + s^ &~= other; + return s^; +} + +@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}; +@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}; + + +@builtin +card :: proc(s: $S/bit_set[$E; $U]) -> int { + when size_of(S) == 1 { + foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- } + return int(count_ones(transmute(u8)s)); + } else when size_of(S) == 2 { + foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- } + return int(count_ones(transmute(u16)s)); + } else when size_of(S) == 4 { + foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- } + return int(count_ones(transmute(u32)s)); + } else when size_of(S) == 8 { + foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- } + return int(count_ones(transmute(u64)s)); + } else when size_of(S) == 16 { + foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- } + return int(count_ones(transmute(u128)s)); + } else { + #panic("Unhandled card bit_set size"); + } +} + + + +@builtin +raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E { + return (^E)(a); +} +@builtin +raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E { + ptr := (transmute(Raw_Slice)s).data; + return (^E)(ptr); +} +@builtin +raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E { + ptr := (transmute(Raw_Dynamic_Array)s).data; + return (^E)(ptr); +} +@builtin +raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 { + return (transmute(Raw_String)s).data; +} + +@builtin +raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}; + + + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +assert :: proc(condition: bool, message := "", loc := #caller_location) { + if !condition { + proc(message: string, loc: Source_Code_Location) { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("runtime assertion", message, loc); + }(message, loc); + } +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +panic :: proc(message: string, loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("panic", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unimplemented :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("not yet implemented", message, loc); +} + +@builtin +@(disabled=ODIN_DISABLE_ASSERT) +unreachable :: proc(message := "", loc := #caller_location) -> ! { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + if message != "" { + p("internal error", message, loc); + } else { + p("internal error", "entered unreachable code", loc); + } +} diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin new file mode 100644 index 000000000..55289bbe4 --- /dev/null +++ b/core/runtime/dynamic_array_internal.odin @@ -0,0 +1,100 @@ +package runtime + +__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { + array := (^Raw_Dynamic_Array)(array_); + array.allocator = context.allocator; + assert(array.allocator.procedure != nil); + + if cap > 0 { + __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc); + array.len = len; + } +} + +__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written + // assuming that appending/reserving will set the allocator, if it is not already set. + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + if cap <= array.cap { + return true; + } + + old_size := array.cap * elem_size; + new_size := cap * elem_size; + allocator := array.allocator; + + new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc); + if new_data != nil || elem_size == 0 { + array.data = new_data; + array.cap = cap; + return true; + } + return false; +} + +__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { + array := (^Raw_Dynamic_Array)(array_); + + ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); + if ok { + array.len = len; + } + return ok; +} + + +__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, + items: rawptr, item_count: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + if items == nil { + return 0; + } + if item_count <= 0 { + return 0; + } + + + ok := true; + if array.cap <= array.len+item_count { + cap := 2 * array.cap + max(8, item_count); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + + mem_copy(rawptr(data), items, elem_size * item_count); + array.len += item_count; + return array.len; +} + +__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { + array := (^Raw_Dynamic_Array)(array_); + + ok := true; + if array.cap <= array.len+1 { + cap := 2 * array.cap + max(8, 1); + ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc); + } + // TODO(bill): Better error handling for failed reservation + if !ok { + return array.len; + } + + assert(array.data != nil); + data := uintptr(array.data) + uintptr(elem_size*array.len); + mem_zero(rawptr(data), elem_size); + array.len += 1; + return array.len; +} diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin new file mode 100644 index 000000000..b4148e74e --- /dev/null +++ b/core/runtime/dynamic_map_internal.odin @@ -0,0 +1,303 @@ +package runtime + +import "intrinsics" +_ :: intrinsics; + +__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { + header := Map_Header{m = (^Raw_Map)(m)}; + Entry :: struct { + key: Map_Key, + next: int, + value: V, + }; + + header.is_key_string = intrinsics.type_is_string(K); + header.entry_size = int(size_of(Entry)); + header.entry_align = int(align_of(Entry)); + header.value_offset = uintptr(offset_of(Entry, value)); + header.value_size = int(size_of(V)); + return header; +} + +__get_map_key :: proc "contextless" (k: $K) -> Map_Key { + key := k; + map_key: Map_Key; + + T :: intrinsics.type_core_type(K); + + when intrinsics.type_is_integer(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + + sz :: 8*size_of(T); + when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); } + else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); } + else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } + else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } + else { #panic("Unhandled integer size"); } + } else when intrinsics.type_is_rune(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + map_key.key.val = u64((^rune)(&key)^); + } else when intrinsics.type_is_pointer(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + map_key.key.val = u64(uintptr((^rawptr)(&key)^)); + } else when intrinsics.type_is_float(T) { + map_key.hash = default_hash_ptr(&key, size_of(T)); + + sz :: 8*size_of(T); + when sz == 32 { map_key.key.val = u64((^u32)(&key)^); } + else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); } + else { #panic("Unhandled float size"); } + } else when intrinsics.type_is_string(T) { + #assert(T == string); + str := (^string)(&key)^; + map_key.hash = default_hash_string(str); + map_key.key.str = str; + } else { + #panic("Unhandled map key type"); + } + + return map_key; +} + +_fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 { + h: u64 = seed; + for b in data { + h = (h ~ u64(b)) * 0x100000001b3; + } + return h; +} + + +default_hash :: inline proc "contextless" (data: []byte) -> u64 { + return _fnv64a(data); +} +default_hash_string :: inline proc "contextless" (s: string) -> u64 { + return default_hash(transmute([]byte)(s)); +} +default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 { + s := Raw_Slice{data, size}; + return default_hash(transmute([]byte)(s)); +} + + +source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { + hash := _fnv64a(transmute([]byte)s.file_path); + hash = hash ~ (u64(s.line) * 0x100000001b3); + hash = hash ~ (u64(s.column) * 0x100000001b3); + return hash; +} + + + +__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool { + array := (^Raw_Slice)(array_); + + if new_count < array.len { + return true; + } + + assert(allocator.procedure != nil); + + old_size := array.len*size_of(T); + new_size := new_count*size_of(T); + + new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc); + if new_data == nil { + return false; + } + array.data = new_data; + array.len = new_count; + return true; +} + +__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) { + __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc); + + old_len := len(m.hashes); + __slice_resize(&m.hashes, cap, m.entries.allocator, loc); + for i in old_len.. rawptr { + index := __dynamic_map_find(h, key).entry_index; + if index >= 0 { + data := uintptr(__dynamic_map_get_entry(h, index)); + return rawptr(data + h.value_offset); + } + return nil; +} + +__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check { + index: int; + assert(value != nil); + + if len(h.m.hashes) == 0 { + __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc); + __dynamic_map_grow(h, loc); + } + + fr := __dynamic_map_find(h, key); + if fr.entry_index >= 0 { + index = fr.entry_index; + } else { + index = __dynamic_map_add_entry(h, key, loc); + if fr.entry_prev >= 0 { + entry := __dynamic_map_get_entry(h, fr.entry_prev); + entry.next = index; + } else { + h.m.hashes[fr.hash_index] = index; + } + } + { + e := __dynamic_map_get_entry(h, index); + e.key = key; + val := (^byte)(uintptr(e) + h.value_offset); + mem_copy(val, value, h.value_size); + } + + if __dynamic_map_full(h) { + __dynamic_map_grow(h, loc); + } +} + + +__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) { + // TODO(bill): Determine an efficient growing rate + new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP); + __dynamic_map_rehash(h, new_count, loc); +} + +__dynamic_map_full :: inline proc(using h: Map_Header) -> bool { + return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; +} + + +__dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool { + if a.hash == b.hash { + if h.is_key_string { + return a.key.str == b.key.str; + } else { + return a.key.val == b.key.val; + } + return true; + } + return false; +} + +__dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check { + fr := Map_Find_Result{-1, -1, -1}; + if n := u64(len(m.hashes)); n > 0 { + fr.hash_index = int(key.hash % n); + fr.entry_index = m.hashes[fr.hash_index]; + for fr.entry_index >= 0 { + entry := __dynamic_map_get_entry(h, fr.entry_index); + if __dynamic_map_hash_equal(h, entry.key, key) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry.next; + } + } + return fr; +} + +__dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int { + prev := m.entries.len; + c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc); + if c != prev { + end := __dynamic_map_get_entry(h, c-1); + end.key = key; + end.next = -1; + } + return prev; +} + +__dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) { + fr := __dynamic_map_find(h, key); + if fr.entry_index >= 0 { + __dynamic_map_erase(h, fr); + } +} + +__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header { + assert(0 <= index && index < m.entries.len); + return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size)); +} + +__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check { + if fr.entry_prev < 0 { + m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next; + } else { + prev := __dynamic_map_get_entry(h, fr.entry_prev); + curr := __dynamic_map_get_entry(h, fr.entry_index); + prev.next = curr.next; + } + if (fr.entry_index == m.entries.len-1) { + // NOTE(bill): No need to do anything else, just pop + } else { + old := __dynamic_map_get_entry(h, fr.entry_index); + end := __dynamic_map_get_entry(h, m.entries.len-1); + mem_copy(old, end, entry_size); + + if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { + last_entry := __dynamic_map_get_entry(h, last.entry_prev); + last_entry.next = fr.entry_index; + } else { + m.hashes[last.hash_index] = fr.entry_index; + } + } + + // TODO(bill): Is this correct behaviour? + m.entries.len -= 1; +}