diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2020-12-06 00:49:48 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-06 00:49:48 +0000 |
| commit | f0683c910231513db9adab83f7c2fca9dd8d2613 (patch) | |
| tree | 2539634b5b71caf5148d8927c9298ba20bad5246 /core/runtime | |
| parent | 54fbdabc380905a925ab5e922749fa2b1ccb2621 (diff) | |
| parent | ca4657fd31b9efc7ab52f7e1b6f4145d5ed28fb7 (diff) | |
Merge branch 'master' into parser-experiments
Diffstat (limited to 'core/runtime')
| -rw-r--r-- | core/runtime/core.odin | 1314 | ||||
| -rw-r--r-- | core/runtime/core_builtin.odin | 838 | ||||
| -rw-r--r-- | core/runtime/dynamic_array_internal.odin | 100 | ||||
| -rw-r--r-- | core/runtime/dynamic_map_internal.odin | 394 | ||||
| -rw-r--r-- | core/runtime/error_checks.odin | 61 | ||||
| -rw-r--r-- | core/runtime/internal.odin | 41 | ||||
| -rw-r--r-- | core/runtime/internal_linux.odin | 135 | ||||
| -rw-r--r-- | core/runtime/internal_windows.odin | 184 | ||||
| -rw-r--r-- | core/runtime/print.odin | 2 | ||||
| -rw-r--r-- | core/runtime/procs_windows_amd64.odin | 11 |
10 files changed, 1678 insertions, 1402 deletions
diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 7556a3645..2b8871f04 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 { @@ -45,6 +42,11 @@ Platform_Endianness :: enum u8 { Big = 2, } +// Procedure type to test whether two values of the same type are equal +Equal_Proc :: distinct proc "contextless" (rawptr, rawptr) -> bool; +// Procedure type to hash a value, default seed value is 0 +Hasher_Proc :: distinct proc "contextless" (data: rawptr, seed: uintptr = 0) -> uintptr; + Type_Info_Struct_Soa_Kind :: enum u8 { None = 0, Fixed = 1, @@ -53,7 +55,12 @@ Type_Info_Struct_Soa_Kind :: enum u8 { } // Variant Types -Type_Info_Named :: struct {name: string, base: ^Type_Info}; +Type_Info_Named :: struct { + name: string, + base: ^Type_Info, + pkg: string, + loc: Source_Code_Location, +}; Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}; Type_Info_Rune :: struct {}; Type_Info_Float :: struct {endianness: Platform_Endianness}; @@ -87,10 +94,11 @@ 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, }; + Type_Info_Struct :: struct { types: []^Type_Info, names: []string, @@ -100,6 +108,9 @@ Type_Info_Struct :: struct { is_packed: bool, is_raw_union: bool, custom_align: bool, + + equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + // These are only set iff this structure is an SOA structure soa_kind: Type_Info_Struct_Soa_Kind, soa_base_type: ^Type_Info, @@ -122,6 +133,8 @@ Type_Info_Map :: struct { key: ^Type_Info, value: ^Type_Info, generated_struct: ^Type_Info, + key_equal: Equal_Proc, + key_hasher: Hasher_Proc, }; Type_Info_Bit_Field :: struct { names: []string, @@ -152,9 +165,16 @@ Type_Info_Relative_Slice :: struct { base_integer: ^Type_Info, }; +Type_Info_Flag :: enum u8 { + Comparable = 0, + Simple_Compare = 1, +}; +Type_Info_Flags :: distinct bit_set[Type_Info_Flag; u32]; + Type_Info :: struct { size: int, align: int, + flags: Type_Info_Flags, id: typeid, variant: union { @@ -237,15 +257,11 @@ 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, line, column: int, procedure: string, - hash: u64, } Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location); @@ -320,6 +336,9 @@ Context :: struct { user_data: any, user_ptr: rawptr, user_index: int, + + // Internal use only + _internal: rawptr, } @@ -345,47 +364,6 @@ Raw_Map :: struct { entries: Raw_Dynamic_Array, } -INITIAL_MAP_CAP :: 16; - -Map_Key :: struct { - hash: u64, - /* NOTE(bill) - size_of(Map_Key) == 16 Bytes on 32-bit systems - size_of(Map_Key) == 24 Bytes on 64-bit systems - - This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems - however, this is probably not a huge problem in terms of memory usage - */ - key: struct #raw_union { - str: string, - val: u64, - }, -} - -Map_Find_Result :: struct { - hash_index: int, - entry_prev: int, - entry_index: int, -} - -Map_Entry_Header :: struct { - key: Map_Key, - next: int, -/* - value: Value_Type, -*/ -} - -Map_Header :: struct { - m: ^Raw_Map, - is_key_string: bool, - - entry_size: int, - entry_align: int, - - value_offset: uintptr, - value_size: int, -} ///////////////////////////// // Init Startup Procedures // @@ -521,13 +499,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 +511,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..<field_count { - type := si.types[i].variant.(Type_Info_Pointer).elem; - max_align = max(max_align, type.align); - - old_size = align_forward_int(old_size, type.align); - new_size = align_forward_int(new_size, type.align); - - old_size += type.size * old_cap; - new_size += type.size * capacity; - } - - old_size = align_forward_int(old_size, max_align); - new_size = align_forward_int(new_size, max_align); - - old_data := (^rawptr)(array)^; - - new_data := array.allocator.procedure( - array.allocator.data, .Alloc, new_size, max_align, - nil, old_size, 0, loc, - ); - if new_data == nil { - return false; - } - - - cap_ptr^ = capacity; - - old_offset := 0; - new_offset := 0; - for i in 0..<field_count { - type := si.types[i].variant.(Type_Info_Pointer).elem; - max_align = max(max_align, type.align); - - old_offset = align_forward_int(old_offset, type.align); - new_offset = align_forward_int(new_offset, type.align); - - new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset)); - old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset)); - - mem_copy(new_data_elem, old_data_elem, type.size * old_cap); - - (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem; - - old_offset += type.size * old_cap; - new_offset += type.size * capacity; - } - - array.allocator.procedure( - array.allocator.data, .Free, 0, max_align, - old_data, old_size, 0, loc, - ); - - return true; -} - -@builtin -append_soa_elem :: proc(array: ^$T/#soa[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_soa(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 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..<field_count { - type := si.types[i].variant.(Type_Info_Pointer).elem; - max_align = max(max_align, type.align); - - soa_offset = align_forward_int(soa_offset, type.align); - item_offset = align_forward_int(item_offset, type.align); - - dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^)); - src := rawptr(uintptr(arg_ptr) + uintptr(item_offset)); - mem_copy(dst, src, type.size); - - soa_offset += type.size * cap(array); - item_offset += type.size; - } - - len_ptr^ += arg_len; - } -} - -@builtin -append_soa_elems :: proc(array: ^$T/#soa[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_soa(array, cap, loc); - } - arg_len = min(cap(array)-len(array), arg_len); - if arg_len > 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..<field_count { - type := si.types[i].variant.(Type_Info_Pointer).elem; - max_align = max(max_align, type.align); - - soa_offset = align_forward_int(soa_offset, type.align); - item_offset = align_forward_int(item_offset, type.align); - - dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^); - src := uintptr(args_ptr) + uintptr(item_offset); - for j in 0..<arg_len { - d := rawptr(dst + uintptr(j*type.size)); - s := rawptr(src + uintptr(j*size_of(E))); - mem_copy(d, s, type.size); - } - - soa_offset += type.size * cap(array); - item_offset += type.size; - } - - len_ptr^ += arg_len; - } -} - -@builtin -append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) { - for arg in args { - append(array = array, args = transmute([]E)(arg), loc = loc); - } -} - - -@builtin append :: proc{append_elem, append_elems, append_elem_string}; -@builtin append_soa :: proc{append_soa_elem, append_soa_elems}; - -@builtin -append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) { - if array == nil { - return; - } - resize(array, len(array)+1); -} - - -@builtin -insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (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..<len(m.hashes) { - m.hashes[i] = -1; - } - -} -__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check { - new_header: Map_Header = header; - nm := Raw_Map{}; - nm.entries.allocator = m.entries.allocator; - new_header.m = &nm; - - c := context; - if m.entries.allocator.procedure != nil { - c.allocator = m.entries.allocator; - } - context = c; - - __dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc); - __slice_resize(&nm.hashes, new_count, m.entries.allocator, loc); - for i in 0 ..< new_count { - nm.hashes[i] = -1; - } - - for i in 0 ..< m.entries.len { - if len(nm.hashes) == 0 { - __dynamic_map_grow(new_header, loc); - } - - entry_header := __dynamic_map_get_entry(header, i); - data := uintptr(entry_header); - - fr := __dynamic_map_find(new_header, entry_header.key); - j := __dynamic_map_add_entry(new_header, entry_header.key, loc); - if fr.entry_prev < 0 { - nm.hashes[fr.hash_index] = j; - } else { - e := __dynamic_map_get_entry(new_header, fr.entry_prev); - e.next = j; - } - - e := __dynamic_map_get_entry(new_header, j); - e.next = fr.entry_index; - ndata := uintptr(e); - mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size); - - if __dynamic_map_full(new_header) { - __dynamic_map_grow(new_header, loc); - } - } - delete(m.hashes, m.entries.allocator, loc); - free(m.entries.data, m.entries.allocator, loc); - header.m^ = nm; -} - -__dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> 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..8a1be60d9 --- /dev/null +++ b/core/runtime/core_builtin.odin @@ -0,0 +1,838 @@ +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, +}; + + +// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value +// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator +@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; +} + +// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only) +// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the +// type of its argument, not a pointer to it. +// Make uses the specified allocator, default is context.allocator, default is context.allocator +@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); + } +} + +// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. +// If m is nil, or there is no such element, this procedure is a no-op +@builtin +delete_key :: proc(m: ^$T/map[$K]$V, key: K) { + if m != nil { + key := key; + __dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&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; + } +} + +// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type +@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..<field_count { + type := si.types[i].variant.(Type_Info_Pointer).elem; + max_align = max(max_align, type.align); + + old_size = align_forward_int(old_size, type.align); + new_size = align_forward_int(new_size, type.align); + + old_size += type.size * old_cap; + new_size += type.size * capacity; + } + + old_size = align_forward_int(old_size, max_align); + new_size = align_forward_int(new_size, max_align); + + old_data := (^rawptr)(array)^; + + new_data := array.allocator.procedure( + array.allocator.data, .Alloc, new_size, max_align, + nil, old_size, 0, loc, + ); + if new_data == nil { + return false; + } + + + cap_ptr^ = capacity; + + old_offset := 0; + new_offset := 0; + for i in 0..<field_count { + type := si.types[i].variant.(Type_Info_Pointer).elem; + max_align = max(max_align, type.align); + + old_offset = align_forward_int(old_offset, type.align); + new_offset = align_forward_int(new_offset, type.align); + + new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset)); + old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset)); + + mem_copy(new_data_elem, old_data_elem, type.size * old_cap); + + (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem; + + old_offset += type.size * old_cap; + new_offset += type.size * capacity; + } + + array.allocator.procedure( + array.allocator.data, .Free, 0, max_align, + old_data, old_size, 0, loc, + ); + + return true; +} + +@builtin +append_soa_elem :: proc(array: ^$T/#soa[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_soa(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 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..<field_count { + type := si.types[i].variant.(Type_Info_Pointer).elem; + max_align = max(max_align, type.align); + + soa_offset = align_forward_int(soa_offset, type.align); + item_offset = align_forward_int(item_offset, type.align); + + dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^)); + src := rawptr(uintptr(arg_ptr) + uintptr(item_offset)); + mem_copy(dst, src, type.size); + + soa_offset += type.size * cap(array); + item_offset += type.size; + } + + len_ptr^ += arg_len; + } +} + +@builtin +append_soa_elems :: proc(array: ^$T/#soa[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_soa(array, cap, loc); + } + arg_len = min(cap(array)-len(array), arg_len); + if arg_len > 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..<field_count { + type := si.types[i].variant.(Type_Info_Pointer).elem; + max_align = max(max_align, type.align); + + soa_offset = align_forward_int(soa_offset, type.align); + item_offset = align_forward_int(item_offset, type.align); + + dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^); + src := uintptr(args_ptr) + uintptr(item_offset); + for j in 0..<arg_len { + d := rawptr(dst + uintptr(j*type.size)); + s := rawptr(src + uintptr(j*size_of(E))); + mem_copy(d, s, type.size); + } + + soa_offset += type.size * cap(array); + item_offset += type.size; + } + + len_ptr^ += arg_len; + } +} + +// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type +@builtin +append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) { + for arg in args { + append(array = array, args = transmute([]E)(arg), loc = loc); + } +} + +// The append built-in procedure appends elements to the end of a dynamic array +@builtin append :: proc{append_elem, append_elems, append_elem_string}; + +// The append_soa built-in procedure appends elements to the end of an #soa dynamic array +@builtin append_soa :: proc{append_soa_elem, append_soa_elems}; + +@builtin +append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) { + if array == nil { + return; + } + resize(array, len(array)+1); +} + + +@builtin +insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (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..e880a043f --- /dev/null +++ b/core/runtime/dynamic_map_internal.odin @@ -0,0 +1,394 @@ +package runtime + +import "intrinsics" +_ :: intrinsics; + +INITIAL_MAP_CAP :: 16; + +// Temporary data structure for comparing hashes and keys +Map_Hash :: struct { + hash: uintptr, + key_ptr: rawptr, // address of Map_Entry_Header.key +} + +__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) { + hasher := intrinsics.type_hasher_proc(K); + map_hash.key_ptr = k; + map_hash.hash = hasher(k, 0); + return; +} + +__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) { + hash.hash = entry.hash; + hash.key_ptr = rawptr(uintptr(entry) + h.key_offset); + return; +} + + + +Map_Find_Result :: struct { + hash_index: int, + entry_prev: int, + entry_index: int, +} + +Map_Entry_Header :: struct { + hash: uintptr, + next: int, +/* + key: Key_Value, + value: Value_Type, +*/ +} + +Map_Header :: struct { + m: ^Raw_Map, + equal: Equal_Proc, + + entry_size: int, + entry_align: int, + + key_offset: uintptr, + key_size: int, + + value_offset: uintptr, + value_size: int, +} + +INITIAL_HASH_SEED :: 0xcbf29ce484222325; + +_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 { + h: u64 = seed; + for b in data { + h = (h ~ u64(b)) * 0x100000001b3; + } + return h; +} + +default_hash :: inline proc "contextless" (data: []byte) -> uintptr { + return uintptr(_fnv64a(data)); +} +default_hash_string :: inline proc "contextless" (s: string) -> uintptr { + return default_hash(transmute([]byte)(s)); +} +default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> uintptr { + s := Raw_Slice{data, size}; + return default_hash(transmute([]byte)(s)); +} + +@(private) +_default_hasher_const :: inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 { + h := u64(seed) + 0xcbf29ce484222325; + p := uintptr(data); + inline for _ in 0..<N { + b := u64((^byte)(p)^); + h = (h ~ b) * 0x100000001b3; + p += 1; + } + return uintptr(h); +} + +default_hasher_n :: inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + p := uintptr(data); + for _ in 0..<N { + b := u64((^byte)(p)^); + h = (h ~ b) * 0x100000001b3; + p += 1; + } + return uintptr(h); +} + +// NOTE(bill): There are loads of predefined ones to improve optimizations for small types + +default_hasher1 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 1); } +default_hasher2 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 2); } +default_hasher3 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 3); } +default_hasher4 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 4); } +default_hasher5 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 5); } +default_hasher6 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 6); } +default_hasher7 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 7); } +default_hasher8 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 8); } +default_hasher9 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 9); } +default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 10); } +default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 11); } +default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 12); } +default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 13); } +default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 14); } +default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 15); } +default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return inline _default_hasher_const(data, seed, 16); } + +default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + str := (^[]byte)(data)^; + for b in str { + h = (h ~ u64(b)) * 0x100000001b3; + } + return uintptr(h); +} +default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { + h := u64(seed) + 0xcbf29ce484222325; + ptr := (^uintptr)(data)^; + for (^byte)(ptr)^ != 0 { + b := (^byte)(ptr)^; + h = (h ~ u64(b)) * 0x100000001b3; + ptr += 1; + } + return uintptr(h); +} + + + +source_code_location_hash :: proc(s: Source_Code_Location) -> uintptr { + hash := _fnv64a(transmute([]byte)s.file_path); + hash = hash ~ (u64(s.line) * 0x100000001b3); + hash = hash ~ (u64(s.column) * 0x100000001b3); + return uintptr(hash); +} + + + +__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { + header := Map_Header{m = (^Raw_Map)(m)}; + Entry :: struct { + hash: uintptr, + next: int, + key: K, + value: V, + }; + + header.equal = intrinsics.type_equal_proc(K); + + header.entry_size = int(size_of(Entry)); + header.entry_align = int(align_of(Entry)); + + header.key_offset = uintptr(offset_of(Entry, key)); + header.key_size = int(size_of(K)); + + header.value_offset = uintptr(offset_of(Entry, value)); + header.value_size = int(size_of(V)); + + return header; +} + +__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..<len(m.hashes) { + m.hashes[i] = -1; + } + +} +__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check { + new_header: Map_Header = header; + nm := Raw_Map{}; + nm.entries.allocator = m.entries.allocator; + new_header.m = &nm; + + c := context; + if m.entries.allocator.procedure != nil { + c.allocator = m.entries.allocator; + } + context = c; + + new_count := new_count; + new_count = max(new_count, 2*m.entries.len); + + __dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc); + __slice_resize(&nm.hashes, new_count, m.entries.allocator, loc); + for i in 0 ..< new_count { + nm.hashes[i] = -1; + } + + for i in 0 ..< m.entries.len { + if len(nm.hashes) == 0 { + __dynamic_map_grow(new_header, loc); + } + + entry_header := __dynamic_map_get_entry(header, i); + entry_hash := __get_map_hash_from_entry(header, entry_header); + + fr := __dynamic_map_find(new_header, entry_hash); + j := __dynamic_map_add_entry(new_header, entry_hash, loc); + if fr.entry_prev < 0 { + nm.hashes[fr.hash_index] = j; + } else { + e := __dynamic_map_get_entry(new_header, fr.entry_prev); + e.next = j; + } + + e := __dynamic_map_get_entry(new_header, j); + __dynamic_map_copy_entry(header, e, entry_header); + e.next = fr.entry_index; + + if __dynamic_map_full(new_header) { + __dynamic_map_grow(new_header, loc); + } + } + + delete(m.hashes, m.entries.allocator, loc); + free(m.entries.data, m.entries.allocator, loc); + header.m^ = nm; +} + +__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr { + index := __dynamic_map_find(h, hash).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, hash: Map_Hash, 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, hash); + if fr.entry_index >= 0 { + index = fr.entry_index; + } else { + index = __dynamic_map_add_entry(h, hash, 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.hash = hash.hash; + + key := rawptr(uintptr(e) + h.key_offset); + mem_copy(key, hash.key_ptr, h.key_size); + + val := rawptr(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 "contextless" (using h: Map_Header) -> bool { + return int(0.75 * f64(len(m.hashes))) <= m.entries.cap; +} + + +__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool { + if a.hash == b.hash { + return h.equal(a.key_ptr, b.key_ptr); + } + return false; +} + +__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check { + fr := Map_Find_Result{-1, -1, -1}; + if n := uintptr(len(m.hashes)); n > 0 { + fr.hash_index = int(hash.hash % n); + fr.entry_index = m.hashes[fr.hash_index]; + for fr.entry_index >= 0 { + entry := __dynamic_map_get_entry(h, fr.entry_index); + entry_hash := __get_map_hash_from_entry(h, entry); + if __dynamic_map_hash_equal(h, entry_hash, hash) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry.next; + } + } + return fr; +} + +__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, 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.hash = hash.hash; + mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size); + end.next = -1; + } + return prev; +} + +__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) { + fr := __dynamic_map_find(h, hash); + 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_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) { + mem_copy(new, old, h.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); + __dynamic_map_copy_entry(h, old, end); + + old_hash := __get_map_hash_from_entry(h, old); + + if last := __dynamic_map_find(h, old_hash); 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; + } + } + + m.entries.len -= 1; +} diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index b1bc0b646..38d6641ab 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -23,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index } handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Index "); print_i64(i64(index)); print_string(" is out of bounds range 0:"); @@ -36,7 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid slice indices: "); print_i64(i64(lo)); print_string(":"); @@ -67,7 +67,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int, } handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid dynamic array values: "); print_i64(i64(low)); print_string(":"); @@ -87,7 +87,7 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column } handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) { context = default_context(); - print_caller_location(Source_Code_Location{file, line, column, "", 0}); + print_caller_location(Source_Code_Location{file, line, column, ""}); print_string(" Invalid type assertion from "); print_typeid(from); print_string(" to "); @@ -98,6 +98,59 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column handle_error(file, line, column, from, to); } +type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid, from_data: rawptr) { + if ok { + return; + } + + variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid { + if id == nil || data == nil { + return id; + } + ti := type_info_base(type_info_of(id)); + #partial switch v in ti.variant { + case Type_Info_Any: + return (^any)(data).id; + case Type_Info_Union: + tag_ptr := uintptr(data) + v.tag_offset; + idx := 0; + switch v.tag_type.size { + case 1: idx = int((^u8)(tag_ptr)^) - 1; + case 2: idx = int((^u16)(tag_ptr)^) - 1; + case 4: idx = int((^u32)(tag_ptr)^) - 1; + case 8: idx = int((^u64)(tag_ptr)^) - 1; + case 16: idx = int((^u128)(tag_ptr)^) - 1; + } + if idx < 0 { + return nil; + } else if idx < len(v.variants) { + return v.variants[idx].id; + } + } + return id; + } + + handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid, from_data: rawptr) { + context = default_context(); + + actual := variant_type(from, from_data); + + print_caller_location(Source_Code_Location{file, line, column, ""}); + print_string(" Invalid type assertion from "); + print_typeid(from); + print_string(" to "); + print_typeid(to); + if actual != from { + print_string(", actual type: "); + print_typeid(actual); + } + print_byte('\n'); + type_assertion_trap(); + } + handle_error(file, line, column, from, to, from_data); +} + + make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) { if 0 <= len { return; diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 8662d045f..27cd3e767 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -93,18 +93,18 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { when ODIN_USE_LLVM_API { when size_of(rawptr) == 8 { @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } else { when size_of(rawptr) == 8 { @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } else { @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } } } @@ -121,18 +121,18 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r when ODIN_USE_LLVM_API { when size_of(rawptr) == 8 { @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } else { @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; } } else { when size_of(rawptr) == 8 { @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } else { @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; + llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---; } } } @@ -180,9 +180,16 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = } return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc); } - - +memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool { + return memory_compare(a, b, n) == 0; +} memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check { + switch { + case a == b: return 0; + case a == nil: return -1; + case b == nil: return +1; + } + x := uintptr(a); y := uintptr(b); n := uintptr(n); @@ -389,45 +396,45 @@ string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) { return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4; } -@(default_calling_convention = "c") +@(default_calling_convention = "none") foreign { @(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 --- @(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 --- } abs_f32 :: inline proc "contextless" (x: f32) -> f32 { foreign { - @(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 --- + @(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 --- } return _abs(x); } abs_f64 :: inline proc "contextless" (x: f64) -> f64 { foreign { - @(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 --- + @(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 --- } return _abs(x); } min_f32 :: proc(a, b: f32) -> f32 { foreign { - @(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 --- + @(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 --- } return _min(a, b); } min_f64 :: proc(a, b: f64) -> f64 { foreign { - @(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 --- + @(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 --- } return _min(a, b); } max_f32 :: proc(a, b: f32) -> f32 { foreign { - @(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 --- + @(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 --- } return _max(a, b); } max_f64 :: proc(a, b: f64) -> f64 { foreign { - @(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 --- + @(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 --- } return _max(a, b); } diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin new file mode 100644 index 000000000..241ed0fdf --- /dev/null +++ b/core/runtime/internal_linux.odin @@ -0,0 +1,135 @@ +package runtime + +@(link_name="__umodti3") +umodti3 :: proc "c" (a, b: u128) -> u128 { + r: u128 = ---; + _ = udivmod128(a, b, &r); + return r; +} + + +@(link_name="__udivmodti4") +udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + return udivmod128(a, b, rem); +} + +@(link_name="__udivti3") +udivti3 :: proc "c" (a, b: u128) -> u128 { + return udivmodti4(a, b, nil); +} + + +@(link_name="__modti3") +modti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1); + s_b := b >> (128 - 1); + an := (a ~ s_a) - s_a; + bn := (b ~ s_b) - s_b; + + r: u128 = ---; + _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r); + return (transmute(i128)r ~ s_a) - s_a; +} + + +@(link_name="__divmodti4") +divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { + u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem); + return transmute(i128)u; +} + +@(link_name="__divti3") +divti3 :: proc "c" (a, b: i128) -> i128 { + u := udivmodti4(transmute(u128)a, transmute(u128)b, nil); + return transmute(i128)u; +} + + +@(link_name="__fixdfti") +fixdfti :: proc(a: u64) -> i128 { + significandBits :: 52; + typeWidth :: (size_of(u64)*8); + exponentBits :: (typeWidth - significandBits - 1); + maxExponent :: ((1 << exponentBits) - 1); + exponentBias :: (maxExponent >> 1); + + implicitBit :: (u64(1) << significandBits); + significandMask :: (implicitBit - 1); + signBit :: (u64(1) << (significandBits + exponentBits)); + absMask :: (signBit - 1); + exponentMask :: (absMask ~ significandMask); + + // Break a into sign, exponent, significand + aRep := a; + aAbs := aRep & absMask; + sign := i128(-1 if aRep & signBit != 0 else 1); + exponent := u64((aAbs >> significandBits) - exponentBias); + significand := u64((aAbs & significandMask) | implicitBit); + + // If exponent is negative, the result is zero. + if exponent < 0 { + return 0; + } + + // If the value is too large for the integer type, saturate. + if exponent >= size_of(i128) * 8 { + return max(i128) if sign == 1 else min(i128); + } + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if exponent < significandBits { + return sign * i128(significand >> (significandBits - exponent)); + } else { + return sign * (i128(significand) << (exponent - significandBits)); + } + +} + +@(default_calling_convention = "none") +foreign { + @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- +} + + +@(link_name="__floattidf") +floattidf :: proc(a: i128) -> f64 { + DBL_MANT_DIG :: 53; + if a == 0 { + return 0.0; + } + a := a; + N :: size_of(i128) * 8; + s := a >> (N-1); + a = (a ~ s) - s; + sd: = N - _clz_i128(a); // number of significant digits + e := u32(sd - 1); // exponent + if sd > DBL_MANT_DIG { + switch sd { + case DBL_MANT_DIG + 1: + a <<= 1; + case DBL_MANT_DIG + 2: + // okay + case: + a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0); + }; + + a |= i128((a & 4) != 0); + a += 1; + a >>= 2; + + if a & (1 << DBL_MANT_DIG) != 0 { + a >>= 1; + e += 1; + } + } else { + a <<= u128(DBL_MANT_DIG - sd); + } + fb: [2]u32; + fb[1] = (u32(s) & 0x80000000) | // sign + ((e + 1023) << 20) | // exponent + ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high + fb[1] = u32(a); // mantissa-low + return transmute(f64)fb; +} diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin index 241ed0fdf..10d2e2249 100644 --- a/core/runtime/internal_windows.odin +++ b/core/runtime/internal_windows.odin @@ -2,134 +2,134 @@ package runtime @(link_name="__umodti3") umodti3 :: proc "c" (a, b: u128) -> u128 { - r: u128 = ---; - _ = udivmod128(a, b, &r); - return r; + r: u128 = ---; + _ = udivmod128(a, b, &r); + return r; } @(link_name="__udivmodti4") udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - return udivmod128(a, b, rem); + return udivmod128(a, b, rem); } @(link_name="__udivti3") udivti3 :: proc "c" (a, b: u128) -> u128 { - return udivmodti4(a, b, nil); + return udivmodti4(a, b, nil); } @(link_name="__modti3") modti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1); - s_b := b >> (128 - 1); - an := (a ~ s_a) - s_a; - bn := (b ~ s_b) - s_b; - - r: u128 = ---; - _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r); - return (transmute(i128)r ~ s_a) - s_a; + s_a := a >> (128 - 1); + s_b := b >> (128 - 1); + an := (a ~ s_a) - s_a; + bn := (b ~ s_b) - s_b; + + r: u128 = ---; + _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r); + return (transmute(i128)r ~ s_a) - s_a; } @(link_name="__divmodti4") divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { - u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem); - return transmute(i128)u; + u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem); + return transmute(i128)u; } @(link_name="__divti3") divti3 :: proc "c" (a, b: i128) -> i128 { - u := udivmodti4(transmute(u128)a, transmute(u128)b, nil); - return transmute(i128)u; + u := udivmodti4(transmute(u128)a, transmute(u128)b, nil); + return transmute(i128)u; } @(link_name="__fixdfti") fixdfti :: proc(a: u64) -> i128 { - significandBits :: 52; - typeWidth :: (size_of(u64)*8); - exponentBits :: (typeWidth - significandBits - 1); - maxExponent :: ((1 << exponentBits) - 1); - exponentBias :: (maxExponent >> 1); - - implicitBit :: (u64(1) << significandBits); - significandMask :: (implicitBit - 1); - signBit :: (u64(1) << (significandBits + exponentBits)); - absMask :: (signBit - 1); - exponentMask :: (absMask ~ significandMask); - - // Break a into sign, exponent, significand - aRep := a; - aAbs := aRep & absMask; - sign := i128(-1 if aRep & signBit != 0 else 1); - exponent := u64((aAbs >> significandBits) - exponentBias); - significand := u64((aAbs & significandMask) | implicitBit); - - // If exponent is negative, the result is zero. - if exponent < 0 { - return 0; - } - - // If the value is too large for the integer type, saturate. - if exponent >= size_of(i128) * 8 { - return max(i128) if sign == 1 else min(i128); - } - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if exponent < significandBits { - return sign * i128(significand >> (significandBits - exponent)); - } else { - return sign * (i128(significand) << (exponent - significandBits)); - } + significandBits :: 52; + typeWidth :: (size_of(u64)*8); + exponentBits :: (typeWidth - significandBits - 1); + maxExponent :: ((1 << exponentBits) - 1); + exponentBias :: (maxExponent >> 1); + + implicitBit :: (u64(1) << significandBits); + significandMask :: (implicitBit - 1); + signBit :: (u64(1) << (significandBits + exponentBits)); + absMask :: (signBit - 1); + exponentMask :: (absMask ~ significandMask); + + // Break a into sign, exponent, significand + aRep := a; + aAbs := aRep & absMask; + sign := i128(-1 if aRep & signBit != 0 else 1); + exponent := u64((aAbs >> significandBits) - exponentBias); + significand := u64((aAbs & significandMask) | implicitBit); + + // If exponent is negative, the result is zero. + if exponent < 0 { + return 0; + } + + // If the value is too large for the integer type, saturate. + if exponent >= size_of(i128) * 8 { + return max(i128) if sign == 1 else min(i128); + } + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if exponent < significandBits { + return sign * i128(significand >> (significandBits - exponent)); + } else { + return sign * (i128(significand) << (exponent - significandBits)); + } } @(default_calling_convention = "none") foreign { - @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- + @(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 --- } @(link_name="__floattidf") floattidf :: proc(a: i128) -> f64 { - DBL_MANT_DIG :: 53; - if a == 0 { - return 0.0; - } - a := a; - N :: size_of(i128) * 8; - s := a >> (N-1); - a = (a ~ s) - s; - sd: = N - _clz_i128(a); // number of significant digits - e := u32(sd - 1); // exponent - if sd > DBL_MANT_DIG { - switch sd { - case DBL_MANT_DIG + 1: - a <<= 1; - case DBL_MANT_DIG + 2: - // okay - case: - a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | - i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0); - }; - - a |= i128((a & 4) != 0); - a += 1; - a >>= 2; - - if a & (1 << DBL_MANT_DIG) != 0 { - a >>= 1; - e += 1; - } - } else { - a <<= u128(DBL_MANT_DIG - sd); - } - fb: [2]u32; - fb[1] = (u32(s) & 0x80000000) | // sign - ((e + 1023) << 20) | // exponent - ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high - fb[1] = u32(a); // mantissa-low - return transmute(f64)fb; + DBL_MANT_DIG :: 53; + if a == 0 { + return 0.0; + } + a := a; + N :: size_of(i128) * 8; + s := a >> (N-1); + a = (a ~ s) - s; + sd: = N - _clz_i128(a); // number of significant digits + e := u32(sd - 1); // exponent + if sd > DBL_MANT_DIG { + switch sd { + case DBL_MANT_DIG + 1: + a <<= 1; + case DBL_MANT_DIG + 2: + // okay + case: + a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0); + }; + + a |= i128((a & 4) != 0); + a += 1; + a >>= 2; + + if a & (1 << DBL_MANT_DIG) != 0 { + a >>= 1; + e += 1; + } + } else { + a <<= u128(DBL_MANT_DIG - sd); + } + fb: [2]u32; + fb[1] = (u32(s) & 0x80000000) | // sign + ((e + 1023) << 20) | // exponent + ((u32(a) >> 32) & 0x000FFFFF); // mantissa-high + fb[1] = u32(a); // mantissa-low + return transmute(f64)fb; } diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 49b0404a0..88e8c9d9e 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -350,7 +350,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) { print_byte(']'); case Type_Info_Opaque: - print_string("opaque "); + print_string("#opaque "); print_type(info.elem); case Type_Info_Simd_Vector: diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index 8593d96f9..511b1866d 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -2,15 +2,14 @@ package runtime foreign import kernel32 "system:Kernel32.lib" -windows_trap_array_bounds :: proc "contextless" () -> ! { - DWORD :: u32; - ULONG_PTR :: uint; +@(private) +foreign kernel32 { + RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! --- +} +windows_trap_array_bounds :: proc "contextless" () -> ! { EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C; - foreign kernel32 { - RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! --- - } RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil); } |