diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index af4e20652..e639aadbd 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -185,6 +185,8 @@ type_is_simd_vector :: proc($T: typeid) -> bool --- type_is_matrix :: proc($T: typeid) -> bool --- type_is_fixed_capacity_dynamic_array :: proc($T: typeid) -> bool --- +type_is_internally_pointer_like :: proc($T: typeid) -> bool --- + type_has_nil :: proc($T: typeid) -> bool --- type_is_matrix_row_major :: proc($T: typeid) -> bool where type_is_matrix(T) --- @@ -215,6 +217,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- +type_proc_calling_convention :: proc($T: typeid) -> Odin_Calling_Convention where type_is_proc(T) --- + type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) --- type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) --- @@ -249,6 +253,8 @@ type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), t type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) --- + + // Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid' type_canonical_name :: proc($T: typeid) -> string --- diff --git a/base/runtime/core.odin b/base/runtime/core.odin index c1ba7aa3e..41d620bdd 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -41,7 +41,9 @@ Fast_Math_Flags :: intrinsics.Fast_Math_Flags // NOTE(bill): This must match the compiler's -Calling_Convention :: enum u8 { + +/* +enum u8 { Invalid = 0, Odin = 1, Contextless = 2, @@ -61,6 +63,8 @@ Calling_Convention :: enum u8 { Preserve_Most = 12, Preserve_All = 13, } +*/ +Calling_Convention :: type_of(ODIN_DEFAULT_CALLING_CONVENTION) Type_Info_Enum_Value :: distinct i64 @@ -296,7 +300,79 @@ when ODIN_OS == .Windows { dll_instance: rawptr } -// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) + +// This is safe to change. The log2 size of a cache-line. At minimum it has to +// be six though. Higher cache line sizes are permitted. +MAP_CACHE_LINE_LOG2 :: 6 + +// The size of a cache-line. +MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2 + +// The minimum cache-line size allowed by this implementation is 64 bytes since +// we need 6 bits in the base pointer to store the integer log2 capacity, which +// at maximum is 63. Odin uses signed integers to represent length and capacity, +// so only 63 bits are needed in the maximum case. +#assert(MAP_CACHE_LINE_SIZE >= 64) + +// Map_Cell type that packs multiple T in such a way to ensure that each T stays +// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0 +// +// This means a value of type T will never straddle a cache-line. +// +// When multiple Ts can fit in a single cache-line the data array will have more +// than one element. When it cannot, the data array will have one element and +// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE. +// +// We rely on the type system to do all the arithmetic and padding for us here. +// +// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit +// more involved as there now may be internal padding. The indexing now becomes +// +// N :: len(Map_Cell(T){}.data) +// i := index / N +// j := index % N +// cell[i].data[j] +// +// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some +// optimizations we can do to eliminate the need for any divisions as N will +// be bounded by [1, 64). +// +// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated +// as a regular array of T, which is the case for hashes. +Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) { + data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T, +} + +// So we can operate on a cell data structure at runtime without any type +// information, we have a simple table that stores some traits about the cell. +// +// 32-bytes on 64-bit +// 16-bytes on 32-bit +Map_Cell_Info :: struct { + size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits +} + +Map_Hash :: uintptr + +// When working with the type-erased structure at runtime we need information +// about the map to make working with it possible. This info structure stores +// that. +// +// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be +// modified after creation +// +// 32-bytes on 64-bit +// 16-bytes on 32-bit +Map_Info :: struct { + ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit + vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit + key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit + key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit +} + Source_Code_Location :: struct { diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index cdf0fbcab..8c522413f 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -2,6 +2,8 @@ package runtime import "base:intrinsics" +MAP_ENABLED :: !ODIN_BEDROCK + @builtin Maybe :: union($T: typeid) {T} @@ -65,7 +67,7 @@ when !NO_DEFAULT_TEMP_ALLOCATOR { // Initializes the global temporary allocator used as the default `context.temp_allocator`. // This is ignored when `NO_DEFAULT_TEMP_ALLOCATOR` is true. @(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR) -init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { +init_global_temporary_allocator :: proc "odin" (size: int, backup_allocator := context.allocator) { when !NO_DEFAULT_TEMP_ALLOCATOR { default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator) } @@ -387,7 +389,7 @@ pop_front_safe :: proc { @builtin clear :: proc{ clear_dynamic_array, - clear_map, + clear_map where MAP_ENABLED, clear_fixed_capacity_dynamic_array, clear_soa_dynamic_array, @@ -397,7 +399,7 @@ clear :: proc{ @builtin reserve :: proc{ reserve_dynamic_array, - reserve_map, + reserve_map where MAP_ENABLED, reserve_soa, } @@ -430,7 +432,7 @@ non_zero_resize :: proc{ @builtin shrink :: proc{ shrink_dynamic_array, - shrink_map, + shrink_map where MAP_ENABLED, } // `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation. @@ -471,14 +473,6 @@ delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc) } -// `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation. -// -// Note: Prefer the procedure group `delete`. -@builtin -delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { - return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc) -} - @builtin delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { @@ -489,6 +483,16 @@ delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := return mem_free((^u16)(str), allocator, loc) } +when MAP_ENABLED { + // `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation. + // + // Note: Prefer the procedure group `delete`. + @builtin + delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { + return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc) + } +} + // `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation. // // Note: Prefer `delete` over the specific `delete_*` procedures where possible. @@ -498,7 +502,7 @@ delete :: proc{ delete_cstring, delete_dynamic_array, delete_slice, - delete_map, + delete_map where MAP_ENABLED, delete_soa_slice, delete_soa_dynamic_array, delete_string16, @@ -597,29 +601,32 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali return } -// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. -// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. -// -// Note: Prefer using the procedure group `make`. -@(builtin, require_results) -make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) { - m.allocator = allocator - return m +when MAP_ENABLED { + // `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. + // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. + // + // Note: Prefer using the procedure group `make`. + @(builtin, require_results) + make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) { + m.allocator = allocator + return m + } + + // `make_map_cap` initializes a map with an allocator and allocates space using `capacity`. + // Like `new`, the first argument is a type, not a value. + // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. + // + // Note: Prefer using the procedure group `make`. + @(builtin, require_results) + make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error { + make_map_expr_error_loc(loc, capacity) + context.allocator = allocator + + err = reserve_map(&m, capacity, loc) + return + } } -// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`. -// Like `new`, the first argument is a type, not a value. -// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. -// -// Note: Prefer using the procedure group `make`. -@(builtin, require_results) -make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error { - make_map_expr_error_loc(loc, capacity) - context.allocator = allocator - - err = reserve_map(&m, capacity, loc) - return -} // `make_multi_pointer` allocates and initializes a multi-pointer. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // @@ -649,8 +656,8 @@ make :: proc{ make_dynamic_array, make_dynamic_array_len, make_dynamic_array_len_cap, - make_map, - make_map_cap, + make_map where MAP_ENABLED, + make_map_cap where MAP_ENABLED, make_multi_pointer, make_soa_slice, @@ -659,53 +666,54 @@ make :: proc{ make_soa_dynamic_array_len_cap, } +when MAP_ENABLED { + // `clear_map` will set the length of a passed map to `0` + // + // Note: Prefer the procedure group `clear` + @builtin + clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { + if m == nil { + return + } + map_clear_dynamic((^Raw_Map)(m), map_info(T)) + } -// `clear_map` will set the length of a passed map to `0` -// -// Note: Prefer the procedure group `clear` -@builtin -clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { - if m == nil { + // `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`). + // + // Note: Prefer the procedure group `reserve` + @builtin + reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { + return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) + } + + // Shrinks the capacity of a map down to the current length. + // + // Note: Prefer the procedure group `shrink` + @builtin + shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { + if m != nil { + return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc) + } return } - map_clear_dynamic((^Raw_Map)(m), map_info(T)) -} -// `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`). -// -// Note: Prefer the procedure group `reserve` -@builtin -reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { - return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) -} - -// Shrinks the capacity of a map down to the current length. -// -// Note: Prefer the procedure group `shrink` -@builtin -shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { - if m != nil { - return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc) - } - return -} - -// 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 -// It is safe to use `delete_key` while iterating a map. -// But if you iterate across a map and insert a new key, it could resize which means you are not iterating across all of the elements. -@builtin -delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) { - if m != nil { - key := key - old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key)) - if ok { - deleted_key = (^K)(old_k)^ - deleted_value = (^V)(old_v)^ + // 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 + // It is safe to use `delete_key` while iterating a map. + // But if you iterate across a map and insert a new key, it could resize which means you are not iterating across all of the elements. + @builtin + delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) { + if m != nil { + key := key + old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key)) + if ok { + deleted_key = (^K)(old_k)^ + deleted_value = (^V)(old_v)^ + } } + return } - return } _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { @@ -731,6 +739,29 @@ _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, a return } +_append_elem_ptr :: #force_no_inline proc(array: ^Raw_Dynamic_Array, arg: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { + if array == nil { + return + } + + if array.cap < array.len+1 { + // Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY. + cap := max(2 * array.cap, DEFAULT_DYNAMIC_ARRAY_CAPACITY) + + // do not 'or_return' here as it could be a partial success + err = _reserve_dynamic_array_unsafe(array, size_of(rawptr), align_of(rawptr), cap, should_zero, loc) + } + if array.cap-array.len > 0 { + data := ([^]rawptr)(array.data) + assert(data != nil, loc=loc) + data[array.len] = arg + array.len += 1 + num_appended = 1 + } + return +} + + // `append_elem` appends an element to the end of a dynamic array. @builtin append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { @@ -740,9 +771,11 @@ append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller } (^Raw_Dynamic_Array)(array).len += 1 return 1, nil + } else when intrinsics.type_is_internally_pointer_like(E) { + return _append_elem_ptr((^Raw_Dynamic_Array)(array), rawptr(arg), should_zero=true, loc=loc) } else when ODIN_OPTIMIZATION_MODE <= .Size { arg := arg - return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, true, loc=loc) + return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, should_zero=true, loc=loc) } else { if array == nil { return @@ -754,7 +787,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller cap := max(2 * arr.cap, DEFAULT_DYNAMIC_ARRAY_CAPACITY) // do not 'or_return' here as it could be a partial success - err = _reserve_dynamic_array_unsafe(arr, size_of(E), align_of(E), cap, true, loc) + err = _reserve_dynamic_array_unsafe(arr, size_of(E), align_of(E), cap, should_zero=true, loc=loc) } if arr.cap-arr.len > 0 { // NOTE(bill, 2026-06-19): When this is in the hot path with -o:speed or -o:aggressive enabled, @@ -777,9 +810,34 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc : when size_of(E) == 0 { (^Raw_Dynamic_Array)(array).len += 1 return 1, nil - } else { + } else when intrinsics.type_is_internally_pointer_like(E) { + return _append_elem_ptr((^Raw_Dynamic_Array)(array), rawptr(arg), should_zero=false, loc=loc) + } else when ODIN_OPTIMIZATION_MODE <= .Size { arg := arg - return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, false, loc=loc) + return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, should_zero=false, loc=loc) + } else { + if array == nil { + return + } + arg := arg + arr := (^Raw_Dynamic_Array)(array) + if arr.cap < arr.len+1 { + // Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY. + cap := max(2 * arr.cap, DEFAULT_DYNAMIC_ARRAY_CAPACITY) + + // do not 'or_return' here as it could be a partial success + err = _reserve_dynamic_array_unsafe(arr, size_of(E), align_of(E), cap, should_zero=false, loc=loc) + } + if arr.cap-arr.len > 0 { + // NOTE(bill, 2026-06-19): When this is in the hot path with -o:speed or -o:aggressive enabled, + // this code path cannot rely on type erasure and `mem_copy_non_overlapping`. + // So directly inlining the call and storing the argument like this helps the optimize a lot + assert(arr.data != nil, loc=loc) + ([^]E)(arr.data)[arr.len] = arg + arr.len += 1 + num_appended = 1 + } + return } } @@ -1525,53 +1583,54 @@ _shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem return true, nil } -@builtin -map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (value_ptr: ^V, err: Allocator_Error) #optional_allocator_error { - key, value := key, value - value_ptr_raw, err_set :=__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) - return (^V)(value_ptr_raw), err_set -} - -// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ. -// - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found -// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize -// - `found_previous` will be true a previous key was found -@(builtin, require_results) -map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) { - key, value := key, value - kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) - if kp != nil { - prev_key = (^K)(kp)^ - found_previous = true +when MAP_ENABLED { + @builtin + map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { + key, value := key, value + return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)) } - value_ptr = (^V)(vp) - return -} -/* -Retrieves a pointer to the key and value for a possibly just inserted entry into the map. + // Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ. + // - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found + // - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize + // - `found_previous` will be true a previous key was found + @(builtin, require_results) + map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) { + key, value := key, value + kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) + if kp != nil { + prev_key = (^K)(kp)^ + found_previous = true + } + value_ptr = (^V)(vp) + return + } -If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`. -Otherwise the existing entry is left untouched and pointers to its key and value are returned. + /* + Retrieves a pointer to the key and value for a possibly just inserted entry into the map. -If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned. + If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`. + Otherwise the existing entry is left untouched and pointers to its key and value are returned. -If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`. + If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned. -WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key. -If that is not the case you will corrupt the map. -*/ -@(builtin, require_results) -map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) { - key := key - zero: V + If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`. - _key_ptr, _value_ptr: rawptr - _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc) + WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key. + If that is not the case you will corrupt the map. + */ + @(builtin, require_results) + map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) { + key := key + zero: V - key_ptr = (^K)(_key_ptr) - value_ptr = (^V)(_value_ptr) - return + _key_ptr, _value_ptr: rawptr + _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc) + + key_ptr = (^K)(_key_ptr) + value_ptr = (^V)(_value_ptr) + return + } } diff --git a/base/runtime/default_temporary_allocator.odin b/base/runtime/default_temporary_allocator.odin index 2017570bb..857463912 100644 --- a/base/runtime/default_temporary_allocator.odin +++ b/base/runtime/default_temporary_allocator.odin @@ -7,7 +7,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { // `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`. Default_Temp_Allocator :: struct {} - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {} + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) {} default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {} @@ -30,7 +30,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { arena: Arena, } - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) { + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) { _ = arena_init(&s.arena, uint(size), backing_allocator) } diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin index cc38da598..c9b3668c3 100644 --- a/base/runtime/dynamic_map_internal.odin +++ b/base/runtime/dynamic_map_internal.odin @@ -1,3 +1,4 @@ +#+build !bedrock package runtime import "base:intrinsics" @@ -47,60 +48,6 @@ MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements // Has to be less than 100% though. #assert(MAP_LOAD_FACTOR < 100) -// This is safe to change. The log2 size of a cache-line. At minimum it has to -// be six though. Higher cache line sizes are permitted. -MAP_CACHE_LINE_LOG2 :: 6 - -// The size of a cache-line. -MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2 - -// The minimum cache-line size allowed by this implementation is 64 bytes since -// we need 6 bits in the base pointer to store the integer log2 capacity, which -// at maximum is 63. Odin uses signed integers to represent length and capacity, -// so only 63 bits are needed in the maximum case. -#assert(MAP_CACHE_LINE_SIZE >= 64) - -// Map_Cell type that packs multiple T in such a way to ensure that each T stays -// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0 -// -// This means a value of type T will never straddle a cache-line. -// -// When multiple Ts can fit in a single cache-line the data array will have more -// than one element. When it cannot, the data array will have one element and -// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE. -// -// We rely on the type system to do all the arithmetic and padding for us here. -// -// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit -// more involved as there now may be internal padding. The indexing now becomes -// -// N :: len(Map_Cell(T){}.data) -// i := index / N -// j := index % N -// cell[i].data[j] -// -// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some -// optimizations we can do to eliminate the need for any divisions as N will -// be bounded by [1, 64). -// -// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated -// as a regular array of T, which is the case for hashes. -Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) { - data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T, -} - -// So we can operate on a cell data structure at runtime without any type -// information, we have a simple table that stores some traits about the cell. -// -// 32-bytes on 64-bit -// 16-bytes on 32-bit -Map_Cell_Info :: struct { - size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits -} - // map_cell_info :: proc "contextless" ($T: typeid) -> ^Map_Cell_Info {...} map_cell_info :: intrinsics.type_map_cell_info @@ -226,8 +173,6 @@ map_data :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr { } -Map_Hash :: uintptr - TOMBSTONE_MASK :: 1<<(size_of(Map_Hash)*8 - 1) // Procedure to check if a slot is empty for a given hash. This is represented @@ -288,23 +233,6 @@ map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Ha return (slot - uintptr(hash)) & (capacity - 1) // NOTE(bill): this is equivalent to the above, but less operations } -// When working with the type-erased structure at runtime we need information -// about the map to make working with it possible. This info structure stores -// that. -// -// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be -// modified after creation -// -// 32-bytes on 64-bit -// 16-bytes on 32-bit -Map_Info :: struct { - ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit - vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit - key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit - key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit -} - - // The Map_Info structure is basically a pseudo-table of information for a given K and V pair. // map_info :: proc "contextless" ($T: typeid/map[$K]$V) -> ^Map_Info {...} map_info :: intrinsics.type_map_info diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin index f63ff3793..f02ead242 100644 --- a/base/runtime/entry_unix.odin +++ b/base/runtime/entry_unix.odin @@ -9,13 +9,15 @@ when ODIN_BUILD_MODE == .Dynamic { @(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/) _odin_entry_point :: proc "c" () { context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() } @(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/) _odin_exit_point :: proc "c" () { context = default_context() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + } } @(link_name="main", linkage="strong", require) main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { @@ -42,9 +44,9 @@ when ODIN_BUILD_MODE == .Dynamic { _start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } intrinsics.syscall(SYS_exit, 0) unreachable() } @@ -53,9 +55,9 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } diff --git a/base/runtime/entry_windows.odin b/base/runtime/entry_windows.odin index dc8e9b82c..d3b38bc9c 100644 --- a/base/runtime/entry_windows.odin +++ b/base/runtime/entry_windows.odin @@ -16,10 +16,10 @@ when ODIN_BUILD_MODE == .Dynamic { switch dll_forward_reason { case .Process_Attach: - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() case .Process_Detach: - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } case .Thread_Attach: break case .Thread_Detach: @@ -35,18 +35,18 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } else when ODIN_NO_CRT { @(link_name="mainCRTStartup", linkage="strong", require) mainCRTStartup :: proc "system" () -> i32 { context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } else { @@ -54,9 +54,9 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 2b9cdae62..e08d0e01d 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1164,217 +1164,6 @@ extendhfsf2 :: proc "c" (value: __float16) -> f32 { return gnu_h2f_ieee(value) } - - -@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -floattidf :: proc "c" (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 - intrinsics.count_leading_zeros(a) // number of significant digits - e := i32(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 & (i128(1) << DBL_MANT_DIG) != 0 { - a >>= 1 - e += 1 - } - } else { - a <<= u128(DBL_MANT_DIG - sd) & 127 - } - fb: [2]u32 - fb[1] = (u32(s) & 0x80000000) | // sign - (u32(e + 1023) << 20) | // exponent - u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high - fb[0] = u32(a) // mantissa-low - return transmute(f64)fb -} - - -@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -floattidf_unsigned :: proc "c" (a: u128) -> f64 { - DBL_MANT_DIG :: 53 - if a == 0 { - return 0.0 - } - a := a - N :: size_of(u128) * 8 - sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits - e := i32(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 = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | - u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) - } - - a |= u128((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] = (0) | // sign - u32((e + 1023) << 20) | // exponent - u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high - fb[0] = u32(a) // mantissa-low - return transmute(f64)fb -} - - - -@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 { - // TODO(bill): implement `fixunsdfti` correctly - x := u64(a) - return u128(x) -} - -@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 { - // TODO(bill): implement `fixunsdfdi` correctly - x := i64(a) - return i128(x) -} - - - - -@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -umodti3 :: proc "c" (a, b: u128) -> u128 { - r: u128 = --- - _ = udivmod128(a, b, &r) - return r -} - - -@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - return udivmod128(a, b, rem) -} - -when !IS_WASM { - @(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) - udivti3 :: proc "c" (a, b: u128) -> u128 { - return udivmodti4(a, b, nil) - } -} - - -@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -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(u128(an), u128(bn), &r) - return (i128(r) ~ s_a) - s_a -} - - -@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { - s_a := a >> (128 - 1) // -1 if negative or 0 - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a // absolute - bn := (b ~ s_b) - s_b - - s_b ~= s_a // quotient sign - u_s_b := u128(s_b) - u_s_a := u128(s_a) - - r: u128 = --- - u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative - rem^ = i128((r ~ u_s_a) - u_s_a) - return u -} - -@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -divti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1) // -1 if negative or 0 - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a // absolute - bn := (b ~ s_b) - s_b - - s_a ~= s_b // quotient sign - u_s_a := u128(s_a) - - return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative -} - - -@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixdfti :: proc "c" (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)) - } - -} - - when .Address in ODIN_SANITIZER_FLAGS { foreign { @(require) diff --git a/base/runtime/internal_i128.odin b/base/runtime/internal_i128.odin new file mode 100644 index 000000000..f4a0f90ed --- /dev/null +++ b/base/runtime/internal_i128.odin @@ -0,0 +1,217 @@ +#+vet !cast +#+build !bedrock +package runtime + +import "base:intrinsics" + +@(private="file") +IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + +@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +floattidf :: proc "c" (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 - intrinsics.count_leading_zeros(a) // number of significant digits + e := i32(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 & (i128(1) << DBL_MANT_DIG) != 0 { + a >>= 1 + e += 1 + } + } else { + a <<= u128(DBL_MANT_DIG - sd) & 127 + } + fb: [2]u32 + fb[1] = (u32(s) & 0x80000000) | // sign + (u32(e + 1023) << 20) | // exponent + u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high + fb[0] = u32(a) // mantissa-low + return transmute(f64)fb +} + + +@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +floattidf_unsigned :: proc "c" (a: u128) -> f64 { + DBL_MANT_DIG :: 53 + if a == 0 { + return 0.0 + } + a := a + N :: size_of(u128) * 8 + sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits + e := i32(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 = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) + } + + a |= u128((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] = (0) | // sign + u32((e + 1023) << 20) | // exponent + u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high + fb[0] = u32(a) // mantissa-low + return transmute(f64)fb +} + + + +@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 { + // TODO(bill): implement `fixunsdfti` correctly + x := u64(a) + return u128(x) +} + +@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 { + // TODO(bill): implement `fixunsdfdi` correctly + x := i64(a) + return i128(x) +} + + + + +@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +umodti3 :: proc "c" (a, b: u128) -> u128 { + r: u128 = --- + _ = udivmod128(a, b, &r) + return r +} + + +@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + return udivmod128(a, b, rem) +} + +when !IS_WASM { + @(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) + udivti3 :: proc "c" (a, b: u128) -> u128 { + return udivmodti4(a, b, nil) + } +} + + +@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +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(u128(an), u128(bn), &r) + return (i128(r) ~ s_a) - s_a +} + + +@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { + s_a := a >> (128 - 1) // -1 if negative or 0 + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a // absolute + bn := (b ~ s_b) - s_b + + s_b ~= s_a // quotient sign + u_s_b := u128(s_b) + u_s_a := u128(s_a) + + r: u128 = --- + u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative + rem^ = i128((r ~ u_s_a) - u_s_a) + return u +} + +@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +divti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1) // -1 if negative or 0 + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a // absolute + bn := (b ~ s_b) - s_b + + s_a ~= s_b // quotient sign + u_s_a := u128(s_a) + + return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative +} + + +@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixdfti :: proc "c" (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)) + } + +} + diff --git a/base/runtime/udivmod128.odin b/base/runtime/udivmod128.odin index 8cc70df55..107dc605d 100644 --- a/base/runtime/udivmod128.odin +++ b/base/runtime/udivmod128.odin @@ -1,3 +1,4 @@ +#+build !bedrock package runtime import "base:intrinsics" diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 77cc7db85..6b6d8aae9 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -113,6 +113,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call } clone_value :: proc(value: Value, allocator := context.allocator) -> Value { + value := value context.allocator = allocator #partial switch &v in value { diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 40c320115..dcd126230 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1806,11 +1806,14 @@ dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, align } return memory, err } - n := align_formula(size, max(a.minimum_alignment, alignment)) + actual_alignment := max(a.minimum_alignment, alignment) + n := align_formula(size, actual_alignment) if n > a.block_size { return nil, .Invalid_Argument } - if a.bytes_left < n { + memory := align_forward(a.current_pos, uintptr(actual_alignment)) + margin := int(uintptr(memory) - uintptr(a.current_pos)) + if a.bytes_left < margin + n { err := _dynamic_arena_cycle_new_block(a, alignment, loc) if err != nil { return nil, err @@ -1818,10 +1821,11 @@ dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, align if a.current_block == nil { return nil, .Out_Of_Memory } + margin = 0 + memory = a.current_pos } - memory := a.current_pos - a.current_pos = ([^]byte)(a.current_pos)[n:] - a.bytes_left -= n + a.current_pos = ([^]byte)(memory)[n:] + a.bytes_left -= margin + n result := ([^]byte)(memory)[:size] // ensure_poisoned(result) // sanitizer.address_unpoison(result) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 2339e0d6e..5e681728d 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1792,6 +1792,13 @@ is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag { advance_token(p) return .Using case .Hash: + if tok := peek_token(p); tok.kind == .Ident { + switch tok.text { + case "simd", "type", "row_major", "column_major", "sparse", "soa": + return .Invalid + } + } + tok: tokenizer.Token advance_token(p) tok = p.curr_tok @@ -2546,6 +2553,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { elem := parse_expr(p, false) + + if p.curr_tok.kind == .Where { + tok_where := expect_token(p, .Where) + cond := parse_expr(p, false) + + be := ast.new(ast.Binary_Expr, elem.pos, end_pos(p.prev_tok)) + be.left = elem + be.op = tok_where + be.right = cond + elem = be + } append(&args, elem) allow_token(p, .Comma) or_break diff --git a/core/text/regex/compiler/compiler.odin b/core/text/regex/compiler/compiler.odin index dbfe3fe1c..3b38cc1b8 100644 --- a/core/text/regex/compiler/compiler.odin +++ b/core/text/regex/compiler/compiler.odin @@ -43,9 +43,16 @@ Node_Match_All_And_Escape :: parser.Node_Match_All_And_Escape Opcode :: virtual_machine.Opcode Program :: [dynamic]Opcode -JUMP_SIZE :: size_of(Opcode) + 1 * size_of(u16) -SPLIT_SIZE :: size_of(Opcode) + 2 * size_of(u16) +Jump :: virtual_machine.Jump +Split :: virtual_machine.Split +Wait_For_Byte :: virtual_machine.Wait_For_Byte +Wait_For_Rune :: virtual_machine.Wait_For_Rune +Wait_For_Rune_Class :: virtual_machine.Wait_For_Rune_Class +Wait_For_Rune_Class_Negated :: virtual_machine.Wait_For_Rune_Class_Negated +Save :: virtual_machine.Save +JUMP_SIZE :: size_of(Jump) +SPLIT_SIZE :: size_of(Split) Compiler :: struct { flags: common.Flags, @@ -141,15 +148,13 @@ map_all_classes :: proc(tree: Node, collection: ^[dynamic]Rune_Class_Data) { append_raw :: #force_inline proc(code: ^Program, data: $T) { // NOTE: This is system-dependent endian. - for b in transmute([size_of(T)]byte)data { - append(code, cast(Opcode)b) - } + data := transmute([size_of(T)]Opcode)data + append(code, ..data[:]) } inject_raw :: #force_inline proc(code: ^Program, start: int, data: $T) { // NOTE: This is system-dependent endian. - for b, i in transmute([size_of(T)]byte)data { - inject_at(code, start + i, cast(Opcode)b) - } + data := transmute([size_of(T)]Opcode)data + inject_at(code, start, ..data[:]) } @require_results @@ -220,8 +225,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) if specific.capture && .No_Capture not_in c.flags { - inject_at(&code, 0, Opcode.Save) - inject_at(&code, 1, Opcode(2 * specific.capture_id)) + save := Save{.Save, Opcode(2 * specific.capture_id)} + inject_raw(&code, 0, save) append(&code, Opcode.Save) append(&code, Opcode(2 * specific.capture_id + 1)) @@ -236,9 +241,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { // Avoiding duplicate allocation by reusing `left`. code = left - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + left_len + JUMP_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + left_len + JUMP_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(len(right) + JUMP_SIZE)) @@ -259,9 +263,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + original_len + JUMP_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + original_len + JUMP_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(-original_len - SPLIT_SIZE)) @@ -270,9 +273,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE + original_len + JUMP_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE + original_len + JUMP_SIZE), i16(SPLIT_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(-original_len - SPLIT_SIZE)) @@ -359,17 +361,15 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + original_len)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + original_len)} + inject_raw(&code, 0, split) case ^Node_Optional_Non_Greedy: code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE + original_len)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE + original_len), i16(SPLIT_SIZE)} + inject_raw(&code, 0, split) case ^Node_Match_All_And_Escape: append(&code, Opcode.Match_All_And_Escape) @@ -412,32 +412,28 @@ compile :: proc(tree: Node, flags: common.Flags) -> (code: Program, class_data: seek_loop: for opcode, pc in virtual_machine.iterate_opcodes(&iter) { #partial switch opcode { case .Byte: - inject_at(&code, pc_open, Opcode.Wait_For_Byte) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Byte{.Wait_For_Byte, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Byte) break optimize_opening case .Rune: operand := intrinsics.unaligned_load(cast(^rune)&code[pc+1]) - inject_at(&code, pc_open, Opcode.Wait_For_Rune) - pc_open += size_of(Opcode) - inject_raw(&code, pc_open, operand) - pc_open += size_of(rune) + wait := Wait_For_Rune{.Wait_For_Rune, operand} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune) break optimize_opening case .Rune_Class: - inject_at(&code, pc_open, Opcode.Wait_For_Rune_Class) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Rune_Class{.Wait_For_Rune_Class, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune_Class) break optimize_opening case .Rune_Class_Negated: - inject_at(&code, pc_open, Opcode.Wait_For_Rune_Class_Negated) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Rune_Class_Negated{.Wait_For_Rune_Class_Negated, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune_Class_Negated) break optimize_opening case .Save: @@ -452,27 +448,21 @@ compile :: proc(tree: Node, flags: common.Flags) -> (code: Program, class_data: } // `.*?` - inject_at(&code, pc_open, Opcode.Split) - pc_open += size_of(byte) - inject_raw(&code, pc_open, i16(SPLIT_SIZE + size_of(byte) + JUMP_SIZE)) - pc_open += size_of(i16) - inject_raw(&code, pc_open, i16(SPLIT_SIZE)) - pc_open += size_of(i16) - - inject_at(&code, pc_open, Opcode.Wildcard) - pc_open += size_of(byte) - - inject_at(&code, pc_open, Opcode.Jump) - pc_open += size_of(byte) - inject_raw(&code, pc_open, i16(-size_of(byte) - SPLIT_SIZE)) - pc_open += size_of(i16) - + split := Split{.Split, i16(SPLIT_SIZE + size_of(byte) + JUMP_SIZE), i16(SPLIT_SIZE)} + jump := Jump{.Jump, i16(-size_of(byte) - SPLIT_SIZE)} + pack := struct { + a: Split, + b: Opcode, + c: Jump, + } { split, Opcode.Wildcard, jump } + inject_raw(&code, pc_open, pack) + pc_open += size_of(Split) + size_of(byte) + size_of(Jump) } if .No_Capture not_in flags { // `(` - inject_at(&code, pc_open, Opcode.Save) - inject_at(&code, pc_open + size_of(byte), Opcode(0x00)) + save := Save{.Save, Opcode(0x00)} + inject_raw(&code, pc_open, save) // `)` append(&code, Opcode.Save); append(&code, Opcode(0x01)) diff --git a/core/text/regex/virtual_machine/virtual_machine.odin b/core/text/regex/virtual_machine/virtual_machine.odin index ab2e9515c..3751e0974 100644 --- a/core/text/regex/virtual_machine/virtual_machine.odin +++ b/core/text/regex/virtual_machine/virtual_machine.odin @@ -49,6 +49,35 @@ Opcode :: enum u8 { Wait_For_Rune_Class_Negated = 0x14, // | u8 Match_All_And_Escape = 0x15, // | } +Jump :: struct #packed { + opcode: Opcode, + target: i16, +} +Split :: struct #packed { + opcode: Opcode, + left: i16, + right: i16, +} +Wait_For_Byte :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Wait_For_Rune :: struct #packed { + opcode: Opcode, + operand: rune, +} +Wait_For_Rune_Class :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Wait_For_Rune_Class_Negated :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Save :: struct #packed { + opcode: Opcode, + operand: Opcode, +} Thread :: struct { pc: int, diff --git a/src/array.cpp b/src/array.cpp index ec2c97d0e..9cf7c6ce3 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -449,6 +449,20 @@ gb_internal void array_unordered_remove(Array *array, isize index) { array_pop(array); } +template +gb_internal void array_inject_at(Array *array, isize index, T value) { + GB_ASSERT(0 <= index); + + isize n = gb_max(array->count, index); + isize new_size = n+1; + array_resize(array, new_size); + + gb_memmove(array->data+index+1, array->data+index, gb_size_of(T)*(array->count-index-1)); + array->data[index] = value; +} + + + template diff --git a/src/big_int.cpp b/src/big_int.cpp index e2ebb5c76..a8ea2079b 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -296,8 +296,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x) { - int bits_used = (x->used-1) * MP_DIGIT_BIT; - return bits_used <= 64; + return mp_count_bits(x) <= 64; } gb_internal u64 big_int_to_u64(BigInt const *x) { @@ -432,6 +431,14 @@ gb_internal void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) { big_int_quo_rem(x, y, &q, z); big_int_dealloc(&q); } +gb_internal void big_int_mod_mod(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt q = {}; + big_int_rem(&q, x, y); + big_int_add(&q, &q, y); + big_int_rem(z, &q, y); + big_int_dealloc(&q); + +} gb_internal void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) { BigInt y0 = {}; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 2a40d0cf5..e6267bb3d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -617,6 +617,10 @@ struct BuildContext { isize max_error_count; + bool bedrock; + bool disable_non_constant_globals; + bool disable_init_fini; + u32 cmd_doc_flags; Array extra_packages; @@ -1852,8 +1856,10 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->no_entry_point = true; } else { if (bc->no_rtti) { - gb_printf_err("-no-rtti is only allowed on freestanding targets\n"); - gb_exit(1); + if (!bc->bedrock) { + gb_printf_err("-no-rtti is only allowed on freestanding targets or '-bedrock'\n"); + gb_exit(1); + } } } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 00b22e84b..90048f004 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -60,6 +60,8 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_raw_union, is_type_fixed_capacity_dynamic_array, + is_type_internally_pointer_like, + is_type_polymorphic_record_specialized, is_type_polymorphic_record_unspecialized, @@ -7060,6 +7062,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_simd_vector: case BuiltinProc_type_is_matrix: case BuiltinProc_type_is_raw_union: + case BuiltinProc_type_is_fixed_capacity_dynamic_array: + case BuiltinProc_type_is_internally_pointer_like: case BuiltinProc_type_is_specialized_polymorphic_record: case BuiltinProc_type_is_unspecialized_polymorphic_record: case BuiltinProc_type_has_nil: @@ -7789,6 +7793,28 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; + case BuiltinProc_type_proc_calling_convention: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Type *pt = base_type(operand->type); + GB_ASSERT(pt->kind == Type_Proc); + ProcCallingConvention cc = pt->Proc.calling_convention; + + operand->mode = Addressing_Constant; + operand->type = t_odin_calling_convention; + operand->value = exact_value_i64(cc); + } + + break; + + case BuiltinProc_type_polymorphic_record_parameter_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { @@ -8249,7 +8275,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Ast *call_expr = unparen_expr(ce->args[0]); Operand op = {}; check_expr_base(c, &op, ce->args[0], nullptr); - if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + if (op.mode != Addressing_Value || call_expr == nullptr || call_expr->kind != Ast_CallExpr) { error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); return false; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b74a43d62..edb9f4883 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1334,6 +1334,10 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->flags |= EntityFlag_Fini; } + if (build_context.disable_init_fini && (e->flags & (EntityFlag_Init|EntityFlag_Fini))) { + error(e->token, "@(init) and @(fini) have been disabled with '-disable-init-fini'"); + } + if (ac.set_cold) { e->flags |= EntityFlag_Cold; } @@ -1530,10 +1534,26 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str); gb_string_free(str); } - if (pt->calling_convention != default_calling_convention()) { - error(e->token, "Procedure 'main' cannot have a custom calling convention"); + if (build_context.bedrock) { + switch (pt->calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + // Okay + break; + default: + error(e->token, "Procedure 'main' cannot have a custom calling convention beyond \"odin\" and \"contextless\" with '-bedrock'"); + pt->calling_convention = ProcCC_Odin; + break; + } + + } else { + if (pt->calling_convention != default_calling_convention()) { + error(e->token, "Procedure 'main' cannot have a custom calling convention"); + } + pt->calling_convention = default_calling_convention(); + } - pt->calling_convention = default_calling_convention(); + if (e->pkg->kind == Package_Init) { if (ctx->info->entry_point != nullptr) { error(e->token, "Redeclaration of the entry pointer procedure 'main'"); @@ -1829,9 +1849,25 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D PtrSet entity_set = {}; ptr_set_init(&entity_set, 2*pg->args.count); - for (Ast *arg : pg->args) { + for (Ast *arg_ : pg->args) { + Ast *arg = arg_; Entity *e = nullptr; Operand o = {}; + if (arg->kind == Ast_BinaryExpr && arg->BinaryExpr.op.kind == Token_where) { + Ast *cond_expr = arg->BinaryExpr.right; + Operand cond = {}; + check_expr(ctx, &cond, cond_expr); + if (cond.mode != Addressing_Invalid) { + if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type) || cond.value.kind != ExactValue_Bool) { + error(arg, "Expected a constant binary expression for the 'where' clause"); + } else if (!cond.value.value_bool) { + continue; + } + } + + arg = arg->BinaryExpr.left; + } + if (arg->kind == Ast_Ident) { e = check_ident(ctx, &o, arg, nullptr, nullptr, true); } else if (arg->kind == Ast_SelectorExpr) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dd5e99128..5a95a5dbb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3421,6 +3421,8 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod x->expr = node; x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value)); + check_is_expressible(c, x, x->type); + return; } @@ -7840,7 +7842,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, break; } } - if (all_the_same) { + if (all_the_same && first_results != nullptr) { GB_ASSERT_MSG(is_type_tuple(first_results), "%s", type_to_string(first_results)); data.result_type = first_results; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 53dae5fd5..222222559 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1476,6 +1476,14 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ return; } + if (switch_kind == TypeSwitch_Union) { + if (is_addressed) { + if (x.mode != Addressing_Variable && !is_type_pointer(x.type)) { + error(lhs->Ident.token, "The element variable '%.*s' cannot be made addressable", LIT(lhs->Ident.token.string)); + } + } + } + Ast *nil_seen = nullptr; TypeSet seen = {}; diff --git a/src/check_type.cpp b/src/check_type.cpp index 128ce3ba5..212f7dbe4 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3068,6 +3068,10 @@ gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) { init_core_map_type(ctx->checker); init_map_internal_types(type); + + if (build_context.bedrock) { + error(node, "'map' is not a valid type when using '-bedrock'"); + } } gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) { @@ -3807,6 +3811,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T *type = alloc_type_dynamic_array(elem); } set_base_type(named_type, *type); + return true; case_end; diff --git a/src/checker.cpp b/src/checker.cpp index 0624c65ac..dd61f6fc3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1121,6 +1121,14 @@ gb_internal void init_universal(void) { // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { String const &name = basic_types[i].Basic.name; + if (build_context.bedrock) { + if ((basic_types[i].Basic.flags & BasicFlag_Integer) != 0 && + basic_types[i].Basic.size == 16) { + // disallow 128-bit integers + continue; + } + } + add_global_type_entity(name, &basic_types[i]); } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); @@ -1147,6 +1155,8 @@ gb_internal void init_universal(void) { add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME); + add_global_bool_constant("ODIN_BEDROCK", bc->bedrock); + { GlobalEnumValue values[Windows_Subsystem_COUNT] = { {"Unknown", Windows_Subsystem_UNKNOWN}, @@ -1303,6 +1313,32 @@ gb_internal void init_universal(void) { scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); } + { + GlobalEnumValue values[ProcCC_MAX] = { + {"Invalid", ProcCC_Invalid}, + {"Odin", ProcCC_Odin}, + {"Contextless", ProcCC_Contextless}, + {"CDecl", ProcCC_CDecl}, + {"Std_Call", ProcCC_StdCall}, + {"Fast_Call", ProcCC_FastCall}, + + {"None", ProcCC_None}, + {"Naked", ProcCC_Naked}, + + {"_", ProcCC_InlineAsm}, + + {"Win64", ProcCC_Win64}, + {"SysV", ProcCC_SysV}, + + {"PreserveNone", ProcCC_PreserveNone}, + {"PreserveMost", ProcCC_PreserveMost}, + {"PreserveAll", ProcCC_PreserveAll}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Calling_Convention"), values, gb_count_of(values), &t_odin_calling_convention, t_u8); + add_global_enum_constant(fields, "ODIN_DEFAULT_CALLING_CONVENTION", default_calling_convention()); + } + { int minimum_os_version = 0; if (build_context.minimum_os_version_string != "") { @@ -7670,6 +7706,14 @@ gb_internal void check_parsed_files(Checker *c) { Type *t = &basic_types[i]; if (t->Basic.size > 0 && (t->Basic.flags & BasicFlag_LLVM) == 0) { + if (build_context.bedrock) { + if ((t->Basic.flags & BasicFlag_Integer) != 0 && + t->Basic.size == 16) { + // disallow 128-bit integers + continue; + } + } + add_type_info_type(&c->builtin_ctx, t); } } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 049d29a2a..e492ca7b2 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -314,6 +314,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_raw_union, BuiltinProc_type_is_fixed_capacity_dynamic_array, + BuiltinProc_type_is_internally_pointer_like, BuiltinProc_type_is_specialized_polymorphic_record, BuiltinProc_type_is_unspecialized_polymorphic_record, @@ -353,6 +354,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_proc_parameter_type, BuiltinProc_type_proc_return_type, + BuiltinProc_type_proc_calling_convention, + BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, @@ -717,6 +720,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_raw_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_fixed_capacity_dynamic_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_internally_pointer_like"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_specialized_polymorphic_record"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_unspecialized_polymorphic_record"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -754,6 +759,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_proc_parameter_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_return_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_calling_convention"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fa26ec4b0..184a8f2e4 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -32,6 +32,22 @@ enum ExactValueKind { ExactValue_Count, }; +gb_global char const *exact_value_kind_string[ExactValue_Count] = { + "Invalid", + + "Bool", + "String", + "Integer", + "Float", + "Complex", + "Quaternion", + "Pointer", + "Compound", + "Procedure", + "Typeid", + "String16", +}; + struct ExactValue { ExactValueKind kind; union { @@ -780,7 +796,7 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E case Token_Quo: return exact_value_float(fmod(big_int_to_f64(a), big_int_to_f64(b))); case Token_QuoEq: big_int_quo(&c, a, b); break; // NOTE(bill): Integer division case Token_Mod: big_int_rem(&c, a, b); break; - case Token_ModMod: big_int_euclidean_mod(&c, a, b); break; + case Token_ModMod: big_int_mod_mod(&c, a, b); break; case Token_And: big_int_and(&c, a, b); break; case Token_Or: big_int_or(&c, a, b); break; case Token_Xor: big_int_xor(&c, a, b); break; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8c4fd9264..f9e049621 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1346,12 +1346,12 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { s = gb_string_append_length(s, "=", 1); if (!is_union) { - for( auto& f : base->Struct.fields ) { + for (auto &f : base->Struct.fields) { String field_type = lb_get_objc_type_encoding(f->type, pointer_depth); s = gb_string_append_length(s, field_type.text, field_type.len); } } else { - for( auto& v : base->Union.variants ) { + for (auto &v : base->Union.variants) { String variant_type = lb_get_objc_type_encoding(v, pointer_depth); s = gb_string_append_length(s, variant_type.text, variant_type.len); } @@ -1518,7 +1518,7 @@ gb_internal void lb_register_objc_thing( auto &tn = g.class_impl_type->Named.type_name->TypeName; Type *superclass = tn.objc_superclass; if (superclass != nullptr) { - auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name); + auto &superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name); lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call); GB_ASSERT(superclass_global.class_global.addr.value); } @@ -1571,6 +1571,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) { GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation); lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type); + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); } // Ensure classes that have been implicitly referenced through @@ -1595,12 +1596,18 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } for (auto pair : class_set) { - auto& tn = pair.type->Named.type_name->TypeName; + Entity *e = pair.type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + auto &tn = e->TypeName; Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type; lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } } for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - array_add( &referenced_classes, g ); + array_add(&referenced_classes, g); } // Add all class globals to a map so that we can look them up dynamically @@ -1618,21 +1625,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { lb_begin_procedure_body(p); // Register class globals, gathering classes that must be implemented - for (auto& kv : global_class_map) { + for (auto &kv : global_class_map) { lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass"); } // Prefetch selectors for implemented methods so that they can also be registered. - for (const auto& cd : class_impls) { - auto& g = cd.g; + for (auto const &cd : class_impls) { + auto &g = cd.g; Type *class_type = g.class_impl_type; - Array* methods = map_get(&m->info->objc_method_implementations, class_type); + Array *methods = map_get(&m->info->objc_method_implementations, class_type); if (!methods) { continue; } - for (const ObjcMethodData& md : *methods) { + for (ObjcMethodData const &md : *methods) { lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector); } } @@ -1655,11 +1662,17 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { map_set(&ivar_map, g.class_impl_type, g); } - for (const auto &cd : class_impls) { + for (auto const &cd : class_impls) { auto &g = cd.g; Type *class_type = g.class_impl_type; Type *class_ptr_type = alloc_type_pointer(class_type); + Entity *e = class_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } // Begin class registration: create class pair and update global reference lbValue class_value = {}; @@ -1667,11 +1680,11 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { { lbValue superclass_value = lb_const_nil(m, t_objc_Class); - auto& tn = class_type->Named.type_name->TypeName; + auto &tn = e->TypeName; Type *superclass = tn.objc_superclass; if (superclass != nullptr) { - auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name); + auto& superclass_global = string_map_must_get(&global_class_map, tn.objc_class_name); superclass_value = superclass_global.class_value; } @@ -1727,13 +1740,13 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } for (const ObjcMethodData &md : *methods) { - GB_ASSERT( md.proc_entity->kind == Entity_Procedure); + GB_ASSERT(md.proc_entity->kind == Entity_Procedure); Type *method_type = md.proc_entity->type; String proc_name = make_string_c("__$objc_method::"); proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name); proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::")); - proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name); + proc_name = concatenate_strings(permanent_allocator(), proc_name, md.ac.objc_name); wrapper_args.count = 2; wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; @@ -1934,7 +1947,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { ivar_addr = lb_addr(global); } - String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name; + Entity *e = g.class_impl_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + String class_name = e->TypeName.objc_class_name; lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value; args.count = 2; @@ -1948,6 +1964,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int); lb_addr_store(p, ivar_addr, ivar_offset_int); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } } lb_end_procedure_body(p); @@ -2072,6 +2092,10 @@ gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast } var.is_initialized = true; + + if (build_context.disable_non_constant_globals) { + error(e->token, "Non-constant initialization of a global variable is disallowed with '-disable_non_constant_globals'"); + } } return false; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 73e927e08..8cd069772 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -99,23 +99,69 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType return LLVMConstNull(dst); } - GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "%s vs %s", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src)); LLVMTypeKind kind = LLVMGetTypeKind(dst); switch (kind) { case LLVMPointerTypeKind: { + GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src), + cast(long long)lb_sizeof(dst), + cast(long long)lb_sizeof(src)); + return LLVMConstPointerCast(val, dst); } case LLVMStructTypeKind: { + GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src), + cast(long long)lb_sizeof(dst), + cast(long long)lb_sizeof(src)); + unsigned src_n = LLVMCountStructElementTypes(src); unsigned dst_n = LLVMCountStructElementTypes(dst); if (src_n != dst_n) goto failure; + // bool skip_cast = true; + // for (unsigned i = 0; i < dst_n; i++) { + // LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i)); + // LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i)); + // if (dt != st) { + // skip_cast = false; + // } + // if (dt == LLVMIntegerTypeKind) { + // continue; + // } + // if (dt != LLVMArrayTypeKind) { + // skip_cast = false; + // break; + // } + + // LLVMValueRef field_val = llvm_const_extract_value(m, val, i); + // if (field_val == nullptr) goto failure; + + // LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); + // LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + // if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) { + // skip_cast = true; + // continue; + // } + + // } + // if (skip_cast) { + // return val; + // } + LLVMValueRef *field_vals = temporary_alloc_array(dst_n); for (unsigned i = 0; i < dst_n; i++) { LLVMValueRef field_val = llvm_const_extract_value(m, val, i); if (field_val == nullptr) goto failure; LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i); + LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val); + + GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty), + cast(long long)lb_sizeof(dst_elem_ty), + cast(long long)lb_sizeof(src_elem_ty), + LLVMPrintTypeToString(dst), + LLVMPrintTypeToString(src) + ); + field_vals[i] = llvm_const_cast(m, field_val, dst_elem_ty, failure_); if (failure_ && *failure_) goto failure; } @@ -126,6 +172,9 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType return LLVMConstStructInContext(m->ctx, field_vals, dst_n, LLVMIsPackedStruct(dst)); } } + case LLVMArrayTypeKind: { + goto failure; + } } failure: @@ -911,6 +960,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty bool is_local = cc.allow_local && m->curr_procedure != nullptr; + if (is_type_union(type) && is_type_union_constantable(type)) { Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Union); @@ -945,21 +995,26 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty res.type = original_type; return res; } else { + LLVMValueRef values[4] = {}; + isize value_count = 0; + + // Payload + values[value_count++] = cv.value; unsigned tag_value = 1; if (bt->Union.kind == UnionType_no_nil) { tag_value = 0; } - LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false); - LLVMValueRef padding = nullptr; - isize value_count = 2; + // Tag + values[value_count++] = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);; + if (LLVMCountStructElementTypes(llvm_type) > 2) { - value_count = 3; - padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2)); + GB_ASSERT(LLVMCountStructElementTypes(llvm_type) == 3); + // Padding + values[value_count++] = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2)); } - LLVMValueRef values[3] = {cv.value, tag, padding}; res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count); res.type = original_type; return res; @@ -971,6 +1026,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty if (cl->elems.count == 0) { return lb_const_nil(m, original_type); } + value_type = type_of_expr(value.value_compound); } else if (value.kind == ExactValue_Invalid) { return lb_const_nil(m, original_type); } @@ -982,18 +1038,31 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty i64 block_size = bt->Union.variant_block_size; - if (are_types_identical(value_type, original_type)) { + while (are_types_identical(value_type, original_type)) { if (value.kind == ExactValue_Compound) { ast_node(cl, CompoundLit, value.value_compound); if (cl->elems.count == 0) { return lb_const_nil(m, original_type); } + + value_type = type_of_expr(value.value_compound); + if (!are_types_identical(value_type, original_type)) { + break; + } + + GB_PANIC("%s --> %s vs %s", + expr_to_string(value.value_compound), + temp_canonical_string(value_type), temp_canonical_string(original_type)); + } else if (value.kind == ExactValue_Invalid) { return lb_const_nil(m, original_type); } - GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type)); + GB_PANIC("(value.kind=%s) %s vs %s", + exact_value_kind_string[value.kind], + temp_canonical_string(value_type), temp_canonical_string(original_type)); } + // union_multiple_allow_compound:; lbValue cv = lb_const_value(m, value_type, value, value_type, cc); Type *variant_type = cv.type; @@ -1001,16 +1070,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty LLVMValueRef values[4] = {}; unsigned value_count = 0; - #if LLVM_VERSION_MAJOR == 14 + #if LLVM_VERSION_MAJOR == 14 LLVMTypeRef block_type = lb_type_internal_union_block_type(m, bt); values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type); - #else + #else values[value_count++] = cv.value; - if (type_size_of(variant_type) != block_size) { + if (block_size != type_size_of(variant_type)) { LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1); values[value_count++] = LLVMConstNull(padding_type); } - #endif + #endif Type *tag_type = union_tag_type(bt); LLVMTypeRef llvm_tag_type = lb_type(m, tag_type); @@ -1026,6 +1095,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } res.value = LLVMConstStructInContext(m->ctx, values, value_count, true); + return res; } } @@ -1609,7 +1679,17 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) { return lb_const_nil(m, original_type); } - if (cl->elems[0]->kind == Ast_FieldValue) { + if (are_types_identical(value.value_compound->tav.type, elem_type)) { + // Compound is of array item type; expand its value to all items in array. + LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); + + for (isize i = 0; i < type->Array.count; i++) { + values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; + } + + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); + return res; + } else if (cl->elems[0]->kind == Ast_FieldValue) { // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); @@ -1663,16 +1743,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); - return res; - } else if (are_types_identical(value.value_compound->tav.type, elem_type)) { - // Compound is of array item type; expand its value to all items in array. - LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); - - for (isize i = 0; i < type->Array.count; i++) { - values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; - } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); return res; } else { @@ -2103,7 +2173,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } if (is_constant) { - LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, tav.type, cc).value; + LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value; if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); } else if (is_local) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index e02f743ca..7eb32279e 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -6571,11 +6571,12 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { } else { item = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index); } + + // make sure it's ^T and not [^]T + item.type = alloc_type_multi_pointer_to_pointer(item.type); if (sub_sel.index.count > 0) { item = lb_emit_deep_field_gep(p, item, sub_sel); } - // make sure it's ^T and not [^]T - item.type = alloc_type_multi_pointer_to_pointer(item.type); return lb_addr(item); } else if (addr.kind == lbAddr_Swizzle) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index d0249172c..d79cf5ed7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2497,8 +2497,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef fields[] = {lb_type(m, type->Union.variants[0])}; return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); } + bool is_packed = false; - auto fields = array_make(temporary_allocator(), 0, 3); + auto fields = array_make(temporary_allocator(), 0, 4); if (is_type_union_maybe_pointer(type)) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); array_add(&fields, variant); @@ -2514,11 +2515,18 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); array_add(&fields, padding_type); } + is_packed = true; } else { LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type); LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); + // #if LLVM_VERSION_MAJOR > 14 + // { // NOTE(bill): always add a zero-byte pad to make inline constant unions work + // LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1); + // array_add(&fields, padding_type); + // } + // #endif array_add(&fields, tag_type); i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); i64 padding = size - used_size; @@ -2526,9 +2534,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); array_add(&fields, padding_type); } + is_packed = true; } - return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, false); + return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, is_packed); } break; diff --git a/src/main.cpp b/src/main.cpp index 51dade656..da0e6c5b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -429,6 +429,10 @@ enum BuildFlagKind { BuildFlag_BuildDiagnostics, + BuildFlag_Bedrock, + BuildFlag_DisableNonConstantGlobals, + BuildFlag_DisableInitFini, + // internal use only BuildFlag_InternalFastISel, BuildFlag_InternalIgnoreLazy, @@ -664,6 +668,10 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Bedrock, str_lit("bedrock"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableNonConstantGlobals, str_lit("disable-non-constant-globals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableInitFini, str_lit("disable-init-fini"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); @@ -1659,6 +1667,20 @@ gb_internal bool parse_build_flags(Array args) { build_context.build_diagnostics = true; break; + case BuildFlag_Bedrock: + build_context.bedrock = true; + build_context.no_rtti = true; + build_context.disable_non_constant_globals = true; + build_context.disable_init_fini = true; + break; + + case BuildFlag_DisableNonConstantGlobals: + build_context.disable_non_constant_globals = true; + break; + case BuildFlag_DisableInitFini: + build_context.disable_init_fini = true; + break; + case BuildFlag_InternalFastISel: build_context.fast_isel = true; break; @@ -2685,6 +2707,19 @@ gb_internal int print_show_help(String const arg0, String command, String option } } + if (check) { + if (print_flag("-bedrock")) { + print_usage_line(2, "Disables numerous features. List of disabled features:"); + print_usage_line(3, "`map` types"); + print_usage_line(3, "128-bit integer types"); + print_usage_line(3, "runtime type information (-no-rtti)"); + print_usage_line(3, "non-constant global variables (-disable-non-constant-globals)"); + print_usage_line(3, "@(init) @(fini) (-disable-init-fini)"); + print_usage_line(3, "Anything Objective-C related"); + print_usage_line(3, "The default paths to the library collections 'core' and 'vendor'"); + } + } + if (build) { if (print_flag("-build-mode:")) { print_usage_line(2, "Sets the build mode."); @@ -2751,7 +2786,15 @@ gb_internal int print_show_help(String const arg0, String command, String option if (print_flag("-disable-assert")) { print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); } + } + if (check) { + if (print_flag("-disable-init-fini")) { + print_usage_line(2, "Disables the ability to use @(init) and @(fini) procedures"); + } + } + + if (run_or_build) { if (print_flag("-disable-red-zone")) { print_usage_line(2, "Disables red zone on a supported freestanding target."); } @@ -2761,8 +2804,13 @@ gb_internal int print_show_help(String const arg0, String command, String option if (print_flag("-disallow-do")) { print_usage_line(2, "Disallows the 'do' keyword in the project."); } + + if (print_flag("-disable-non-constant-globals")) { + print_usage_line(2, "Disables any global variables which are not initialized with global constants"); + } } + if (doc) { if (print_flag("-doc-format")) { print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); @@ -3595,6 +3643,32 @@ gb_internal int strip_semicolons(Parser *parser) { return cast(int)failed; } +gb_internal void setup_bedrock_mode(void) { + if (!build_context.bedrock) { + return; + } + + bool seen_core = false; + bool seen_vendor = false; + for (isize i = 0; i < library_collections.count; /**/) { + if (!seen_core && library_collections[i].name == "core") { + array_ordered_remove(&library_collections, i); + seen_core = true; + continue; + } + + if (!seen_vendor && library_collections[i].name == "vendor") { + array_ordered_remove(&library_collections, i); + seen_vendor = true; + continue; + } + + i += 1; + } + + build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; +} + gb_internal void init_terminal(void) { TIME_SECTION("init terminal"); build_context.has_ansi_terminal_colours = false; @@ -3693,16 +3767,41 @@ int main(int arg_count, char const **arg_ptr) { String init_filename = {}; isize last_non_run_arg = args.count; + isize double_dash_pos = -1; for_array(i, args) { if (args[i] == "--") { + double_dash_pos = i; break; } + if (args[i] == "-help" || args[i] == "--help") { build_context.show_help = true; return print_show_help(args[0], command); } } + if (args.count > 2) { + // NOTE(bill): Allow for both `odin command path -flags` and `odin command -flags path` + // To do this, if the first argument after the command and last argument is NOT a flag, + // then put that last parameter first + isize end_arg = double_dash_pos >= 0 ? double_dash_pos : args.count-1; + if (args[1] == "bundle" && args.count > 4) { + if (string_starts_with(args[3], str_lit("-")) && + !string_starts_with(args[end_arg], str_lit("-"))) { + String possible_path = args[end_arg]; + array_ordered_remove(&args, end_arg); + array_inject_at(&args, 3, possible_path); + } + } else if (args.count > 3) { + if (string_starts_with(args[2], str_lit("-")) && + !string_starts_with(args[end_arg], str_lit("-"))) { + String possible_path = args[end_arg]; + array_ordered_remove(&args, end_arg); + array_inject_at(&args, 2, possible_path); + } + } + } + bool run_output = false; if (command == "run" || command == "test") { if (args.count < 3) { @@ -3874,6 +3973,10 @@ int main(int arg_count, char const **arg_ptr) { return print_show_help(args[0], command); } + if (build_context.bedrock) { + setup_bedrock_mode(); + } + if (init_filename.len > 0 && !build_context.show_help) { // The command must be build, run, test, check, or another that takes a directory or filename. if (!path_is_directory(init_filename)) { diff --git a/src/parser.cpp b/src/parser.cpp index 14258b5c6..03e9073ca 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2528,8 +2528,14 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { Ast *elem = parse_expr(f, false); - array_add(&args, elem); + if (f->curr_token.kind == Token_where) { + Token where = expect_token(f, Token_where); + Ast *cond = parse_expr(f, false); + elem = ast_binary_expr(f, where, elem, cond); + } + + array_add(&args, elem); if (!allow_field_separator(f)) { break; } @@ -4214,18 +4220,33 @@ gb_internal FieldFlag is_token_field_prefix(AstFile *f) { return FieldFlag_using; case Token_Hash: - advance_token(f); - switch (f->curr_token.kind) { - case Token_Ident: - for (i32 i = 0; i < gb_count_of(parse_field_prefix_mappings); i++) { - auto const &mapping = parse_field_prefix_mappings[i]; - if (mapping.token_kind == Token_Hash) { - if (f->curr_token.string == mapping.name) { - return mapping.flag; - } + { + // Check for types first before fields + Token tok = peek_token(f); + if (tok.kind == Token_Ident) { + if (tok.string == "simd" || + tok.string == "type" || + tok.string == "row_major" || + tok.string == "column_major" || + tok.string == "sparse" || + tok.string == "soa") { + return FieldFlag_Invalid; } } - break; + + advance_token(f); + switch (f->curr_token.kind) { + case Token_Ident: + for (i32 i = 0; i < gb_count_of(parse_field_prefix_mappings); i++) { + auto const &mapping = parse_field_prefix_mappings[i]; + if (mapping.token_kind == Token_Hash) { + if (f->curr_token.string == mapping.name) { + return mapping.flag; + } + } + } + break; + } } return FieldFlag_Unknown; } @@ -6389,6 +6410,11 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { continue; } + if (p == "bedrock") { + this_kind_correct = build_context.bedrock == !is_notted; + continue; + } + Subtarget subtarget = Subtarget_Invalid; String subtarget_str = {}; diff --git a/src/parser.hpp b/src/parser.hpp index 149cf6330..e423cb9b2 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -329,6 +329,9 @@ gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = { }; gb_internal ProcCallingConvention default_calling_convention(void) { + if (build_context.bedrock) { + // return ProcCC_Contextless; + } return ProcCC_Odin; } diff --git a/src/types.cpp b/src/types.cpp index 45486e6bb..d85f01c41 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -781,6 +781,9 @@ gb_global Type *t_c_va_list = nullptr; gb_global Type *t_c_va_list_ptr = nullptr; +gb_global Type *t_odin_calling_convention = nullptr; + + enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered OdinAtomicMemoryOrder_consume = 1, // monotonic @@ -2696,8 +2699,6 @@ gb_internal bool is_type_union_constantable(Type *type) { if (bt->Union.variants.count == 0) { return true; - } else if (bt->Union.variants.count == 1) { - return is_type_constant_type(bt->Union.variants[0]); } for (Type *v : bt->Union.variants) { diff --git a/tests/core/crypto/wycheproof/main.odin b/tests/core/crypto/wycheproof/main.odin index 654ac2a38..bfb4884cd 100644 --- a/tests/core/crypto/wycheproof/main.odin +++ b/tests/core/crypto/wycheproof/main.odin @@ -73,7 +73,7 @@ import "core:testing" // - crypto/legacy/md5 // - crypto/tuplehash -ARENA_SIZE :: 4 * 1024 * 1024 // There is no kill like overkill. +ARENA_SIZE :: 8 * 1024 * 1024 // There is no kill like overkill. BASE_PATH :: ODIN_ROOT + "tests/core/assets/Wycheproof" SUFFIX_TEST_JSON :: "_test.json" diff --git a/tests/core/crypto/wycheproof/pqc.odin b/tests/core/crypto/wycheproof/pqc.odin index d485f621f..07ce2d02c 100644 --- a/tests/core/crypto/wycheproof/pqc.odin +++ b/tests/core/crypto/wycheproof/pqc.odin @@ -494,6 +494,7 @@ test_mldsa :: proc(t: ^testing.T) { test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Group)) -> bool { FLAG_INTERNAL :: "Internal" + FLAG_RANDOMIZED :: "Randomized" dummy_rnd: [_mldsa.RNDBYTES]byte @@ -511,17 +512,31 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr priv_key: mldsa.Private_Key tg_len := len(test_group.tests) - if !testing.expectf( - t, - mldsa.private_key_set_bytes(&priv_key, params, seed), - "%s/Sign/%d: failed to set private key from seed: %s", - params_str, - tg_id, - test_group.private_seed, - ) { - num_ran += tg_len - num_failed += tg_len + switch len(test_group.public_key) { + case 0: + for &test_vector in test_group.tests { + num_ran += 1 + switch result_is_invalid(test_vector.result) { + case true: + num_passed += 1 + case false: + num_failed += 1 + } + } continue + case: + if !testing.expectf( + t, + mldsa.private_key_set_bytes(&priv_key, params, seed), + "%s/Sign/%d: failed to set private key from seed: %s", + params_str, + tg_id, + test_group.private_seed, + ) { + num_ran += tg_len + num_failed += tg_len + continue + } } pub_bytes := make([]byte, mldsa.PUBLIC_KEY_SIZES[params]) @@ -575,7 +590,7 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr ctx := common.hexbytes_decode(test_vector.ctx) msg := common.hexbytes_decode(test_vector.msg) - is_external_mu := slice.contains(test_vector.flags, FLAG_INTERNAL) + is_external_mu := slice.contains(test_vector.flags, FLAG_INTERNAL) || slice.contains(test_vector.flags, FLAG_RANDOMIZED) switch is_external_mu { case false: ok = mldsa.sign( @@ -586,11 +601,16 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr true, ) case true: + rnd := dummy_rnd[:] + if len(test_vector.rnd) != 0 { + rnd = common.hexbytes_decode(test_vector.rnd) + } + ok = _mldsa.dsa_sign_internal( sig, msg, ctx, - dummy_rnd[:], + rnd, &priv_key, common.hexbytes_decode(test_vector.mu), ) diff --git a/tests/core/crypto/wycheproof/schemas.odin b/tests/core/crypto/wycheproof/schemas.odin index 36fc22d10..2e72a1098 100644 --- a/tests/core/crypto/wycheproof/schemas.odin +++ b/tests/core/crypto/wycheproof/schemas.odin @@ -234,6 +234,7 @@ Mldsa_Test_Vector :: struct { tc_id: int `json:"tcId"`, comment: string `json:"comment"`, msg: common.Hex_Bytes `json:"msg"`, + rnd: common.Hex_Bytes `json:"rnd"`, ctx: common.Hex_Bytes `json:"ctx"`, mu: common.Hex_Bytes `json:"mu"`, sig: common.Hex_Bytes `json:"sig"`, diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 0b92f07ff..43a7136b7 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -36,6 +36,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin test ..\test_pr_6470.odin -define:TEST_EXPECT_FAILURE=true %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b ..\..\..\odin test ..\test_pr_6476.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_6484.odin -no-entry-point %COMMON% || exit /b +..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index f630738f9..3633a7606 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -73,6 +73,12 @@ else exit 1 fi $ODIN check ../test_issue_6484.odin -no-entry-point $COMMON +if [[ $($ODIN check ../test_issue_6874.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then + echo "SUCCESSFUL 1/1" +else + echo "SUCCESSFUL 0/1" + exit 1 +fi set +x diff --git a/tests/issues/test_issue_6874.odin b/tests/issues/test_issue_6874.odin new file mode 100644 index 000000000..946fef39d --- /dev/null +++ b/tests/issues/test_issue_6874.odin @@ -0,0 +1,35 @@ +// Test for issue #6874 https://github.com/odin-lang/Odin/issues/6874 + +package test_issues + +import "core:fmt" + +PersonData :: struct { + health: int, + age: int, +} + +MyUnion :: union { + f32, + int, + PersonData, +} + +change_union_data :: proc(data: MyUnion) { + switch &v in data { + case int: + v = 10 + case f32: + fmt.println("f32") + case PersonData: + fmt.println("PersonData") + fmt.println(v) + } +} + +main :: proc() { + val: MyUnion = int(12) + fmt.printfln("Before the call: %v", val) + change_union_data(val) + fmt.printfln("After the call: %v", val) +} diff --git a/vendor/sdl3/sdl3_joystick.odin b/vendor/sdl3/sdl3_joystick.odin index 78da26923..ea998d8f9 100644 --- a/vendor/sdl3/sdl3_joystick.odin +++ b/vendor/sdl3/sdl3_joystick.odin @@ -2,10 +2,6 @@ package sdl3 import "core:c" -@(link_prefix="SDL_") -foreign lib { - joystick_lock: ^Mutex -} Joystick :: struct {} JoystickID :: distinct Uint32 diff --git a/vendor/windows/GameInput/windows_game_input.odin b/vendor/windows/GameInput/windows_game_input.odin index 5a47d47ef..445a7c665 100644 --- a/vendor/windows/GameInput/windows_game_input.odin +++ b/vendor/windows/GameInput/windows_game_input.odin @@ -493,10 +493,10 @@ CallbackToken :: distinct u64 CURRENT_CALLBACK_TOKEN_VALUE :: CallbackToken(0xFFFFFFFFFFFFFFFF) INVALID_CALLBACK_TOKEN_VALUE :: CallbackToken(0x0000000000000000) -ReadingCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, reading: ^IGameInputReading, hasOverrunOccured: bool) -DeviceCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IGameInputDevice, timestamp: u64, currentStatus: DeviceStatus, previousStatus: DeviceStatus) -SystemButtonCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IGameInputDevice, timestamp: u64, currentButtons: SystemButtons, previousButtons: SystemButtons) -KeyboardLayoutCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IGameInputDevice, timestamp: u64, currentLayout: u32, previousLayout: u32) +ReadingCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, reading: ^IReading, hasOverrunOccured: bool) +DeviceCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IDevice, timestamp: u64, currentStatus: DeviceStatus, previousStatus: DeviceStatus) +SystemButtonCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IDevice, timestamp: u64, currentButtons: SystemButtons, previousButtons: SystemButtons) +KeyboardLayoutCallback :: #type proc "stdcall" (callbackToken: CallbackToken, ctx: rawptr, device: ^IDevice, timestamp: u64, currentLayout: u32, previousLayout: u32) KeyState :: struct { scanCode: u32, @@ -988,134 +988,134 @@ IGameInput :: struct #raw_union { IGameInput_VTable :: struct { using iunknown_vtable: IUnknown_VTable, GetCurrentTimestamp: proc "system" (this: ^IGameInput) -> u64, - GetCurrentReading: proc "system" (this: ^IGameInput, inputKind: Kind, device: ^IGameInputDevice, reading: ^^IGameInputReading) -> HRESULT, - GetNextReading: proc "system" (this: ^IGameInput, referenceReading: ^IGameInputReading, inputKind: Kind, device: ^IGameInputDevice, reading: ^^IGameInputReading) -> HRESULT, - GetPreviousReading: proc "system" (this: ^IGameInput, referenceReading: ^IGameInputReading, inputKind: Kind, device: ^IGameInputDevice, reading: ^^IGameInputReading) -> HRESULT, - GetTemporalReading: proc "system" (this: ^IGameInput, timestamp: u64, device: ^IGameInputDevice, reading: ^^IGameInputReading) -> HRESULT, - RegisterReadingCallback: proc "system" (this: ^IGameInput, device: ^IGameInputDevice, inputKind: Kind, analogThreshold: f32, ctx: rawptr, callbackFunc: ReadingCallback, callbackToken: ^CallbackToken) -> HRESULT, - RegisterDeviceCallback: proc "system" (this: ^IGameInput, device: ^IGameInputDevice, inputKind: Kind, statusFilter: DeviceStatus, enumerationKind: EnumerationKind, ctx: rawptr, callbackFunc: DeviceCallback, callbackToken: ^CallbackToken) -> HRESULT, - RegisterSystemButtonCallback: proc "system" (this: ^IGameInput, device: ^IGameInputDevice, buttonFilter: SystemButtons, ctx: rawptr, callbackFunc: SystemButtonCallback, callbackToken: ^CallbackToken) -> HRESULT, - RegisterKeyboardLayoutCallback: proc "system" (this: ^IGameInput, device: ^IGameInputDevice, ctx: rawptr, callbackFunc: KeyboardLayoutCallback, callbackToken: ^CallbackToken) -> HRESULT, + GetCurrentReading: proc "system" (this: ^IGameInput, inputKind: Kind, device: ^IDevice, reading: ^^IReading) -> HRESULT, + GetNextReading: proc "system" (this: ^IGameInput, referenceReading: ^IReading, inputKind: Kind, device: ^IDevice, reading: ^^IReading) -> HRESULT, + GetPreviousReading: proc "system" (this: ^IGameInput, referenceReading: ^IReading, inputKind: Kind, device: ^IDevice, reading: ^^IReading) -> HRESULT, + GetTemporalReading: proc "system" (this: ^IGameInput, timestamp: u64, device: ^IDevice, reading: ^^IReading) -> HRESULT, + RegisterReadingCallback: proc "system" (this: ^IGameInput, device: ^IDevice, inputKind: Kind, analogThreshold: f32, ctx: rawptr, callbackFunc: ReadingCallback, callbackToken: ^CallbackToken) -> HRESULT, + RegisterDeviceCallback: proc "system" (this: ^IGameInput, device: ^IDevice, inputKind: Kind, statusFilter: DeviceStatus, enumerationKind: EnumerationKind, ctx: rawptr, callbackFunc: DeviceCallback, callbackToken: ^CallbackToken) -> HRESULT, + RegisterSystemButtonCallback: proc "system" (this: ^IGameInput, device: ^IDevice, buttonFilter: SystemButtons, ctx: rawptr, callbackFunc: SystemButtonCallback, callbackToken: ^CallbackToken) -> HRESULT, + RegisterKeyboardLayoutCallback: proc "system" (this: ^IGameInput, device: ^IDevice, ctx: rawptr, callbackFunc: KeyboardLayoutCallback, callbackToken: ^CallbackToken) -> HRESULT, StopCallback: proc "system" (this: ^IGameInput, callbackToken: CallbackToken), UnregisterCallback: proc "system" (this: ^IGameInput, callbackToken: CallbackToken, timeoutInMicroseconds: u64) -> bool, - CreateDispatcher: proc "system" (this: ^IGameInput, dispatcher: ^^IGameInputDispatcher) -> HRESULT, - CreateAggregateDevice: proc "system" (this: ^IGameInput, kind: Kind, device: ^^IGameInputDevice) -> HRESULT, - FindDeviceFromId: proc "system" (this: ^IGameInput, value: ^APP_LOCAL_DEVICE_ID, device: ^^IGameInputDevice) -> HRESULT, - FindDeviceFromObject: proc "system" (this: ^IGameInput, value: ^IUnknown, device: ^^IGameInputDevice) -> HRESULT, - FindDeviceFromPlatformHandle: proc "system" (this: ^IGameInput, value: HANDLE, device: ^^IGameInputDevice) -> HRESULT, - FindDeviceFromPlatformString: proc "system" (this: ^IGameInput, value: win.LPCWSTR, device: ^^IGameInputDevice) -> HRESULT, + CreateDispatcher: proc "system" (this: ^IGameInput, dispatcher: ^^IDispatcher) -> HRESULT, + CreateAggregateDevice: proc "system" (this: ^IGameInput, kind: Kind, device: ^^IDevice) -> HRESULT, + FindDeviceFromId: proc "system" (this: ^IGameInput, value: ^APP_LOCAL_DEVICE_ID, device: ^^IDevice) -> HRESULT, + FindDeviceFromObject: proc "system" (this: ^IGameInput, value: ^IUnknown, device: ^^IDevice) -> HRESULT, + FindDeviceFromPlatformHandle: proc "system" (this: ^IGameInput, value: HANDLE, device: ^^IDevice) -> HRESULT, + FindDeviceFromPlatformString: proc "system" (this: ^IGameInput, value: win.LPCWSTR, device: ^^IDevice) -> HRESULT, EnableOemDeviceSupport: proc "system" (this: ^IGameInput, vendorId: u16, productId: u16, interfaceNumber: u8, collectionNumber: u8) -> HRESULT, SetFocusPolicy: proc "system" (this: ^IGameInput, policy: FocusPolicy), } -IGameInputReading_UUID_STRING :: "2156947A-E1FA-4DE0-A30B-D812931DBD8D" -IGameInputReading_UUID := &IID{0x2156947A, 0xE1FA, 0x4DE0, {0xA3, 0x0B, 0xD8, 0x12, 0x93, 0x1D, 0x0BD, 0x8D}} -IGameInputReading :: struct #raw_union { +IReading_UUID_STRING :: "2156947A-E1FA-4DE0-A30B-D812931DBD8D" +IReading_UUID := &IID{0x2156947A, 0xE1FA, 0x4DE0, {0xA3, 0x0B, 0xD8, 0x12, 0x93, 0x1D, 0x0BD, 0x8D}} +IReading :: struct #raw_union { #subtype iunknown: IUnknown, - using igameinputreading_vtable: ^IGameInputReading_VTable, + using igameinputreading_vtable: ^IReading_VTable, } -IGameInputReading_VTable :: struct { +IReading_VTable :: struct { using iunknown_vtable: IUnknown_VTable, - GetInputKind: proc "system" (this: ^IGameInputReading) -> Kind, - GetSequenceNumber: proc "system" (this: ^IGameInputReading, inputKind: Kind) -> u64, - GetTimestamp: proc "system" (this: ^IGameInputReading) -> u64, - GetDevice: proc "system" (this: ^IGameInputReading, device: ^^IGameInputDevice), - GetRawReport: proc "system" (this: ^IGameInputReading, report: ^^IGameInputRawDeviceReport) -> bool, - GetControllerAxisCount: proc "system" (this: ^IGameInputReading) -> u32, - GetControllerAxisState: proc "system" (this: ^IGameInputReading, stateArrayCount: u32, stateArray: [^]f32) -> u32, - GetControllerButtonCount: proc "system" (this: ^IGameInputReading) -> u32, - GetControllerButtonState: proc "system" (this: ^IGameInputReading, stateArrayCount: u32, stateArray: [^]bool) -> u32, - GetControllerSwitchCount: proc "system" (this: ^IGameInputReading) -> u32, - GetControllerSwitchState: proc "system" (this: ^IGameInputReading, stateArrayCount: u32, stateArray: [^]SwitchPosition) -> u32, - GetKeyCount: proc "system" (this: ^IGameInputReading) -> u32, - GetKeyState: proc "system" (this: ^IGameInputReading, stateArrayCount: u32, stateArray: [^]KeyState) -> u32, - GetMouseState: proc "system" (this: ^IGameInputReading, state: ^MouseState) -> bool, - GetTouchCount: proc "system" (this: ^IGameInputReading) -> u32, - GetTouchState: proc "system" (this: ^IGameInputReading, stateArrayCount: u32, stateArray: [^]TouchState) -> u32, - GetMotionState: proc "system" (this: ^IGameInputReading, state: ^MotionState) -> bool, - GetArcadeStickState: proc "system" (this: ^IGameInputReading, state: ^ArcadeStickState) -> bool, - GetFlightStickState: proc "system" (this: ^IGameInputReading, state: ^FlightStickState) -> bool, - GetGamepadState: proc "system" (this: ^IGameInputReading, state: ^GamepadState) -> bool, - GetRacingWheelState: proc "system" (this: ^IGameInputReading, state: ^RacingWheelState) -> bool, - GetUiNavigationState: proc "system" (this: ^IGameInputReading, state: ^UiNavigationState) -> bool, + GetInputKind: proc "system" (this: ^IReading) -> Kind, + GetSequenceNumber: proc "system" (this: ^IReading, inputKind: Kind) -> u64, + GetTimestamp: proc "system" (this: ^IReading) -> u64, + GetDevice: proc "system" (this: ^IReading, device: ^^IDevice), + GetRawReport: proc "system" (this: ^IReading, report: ^^IRawDeviceReport) -> bool, + GetControllerAxisCount: proc "system" (this: ^IReading) -> u32, + GetControllerAxisState: proc "system" (this: ^IReading, stateArrayCount: u32, stateArray: [^]f32) -> u32, + GetControllerButtonCount: proc "system" (this: ^IReading) -> u32, + GetControllerButtonState: proc "system" (this: ^IReading, stateArrayCount: u32, stateArray: [^]bool) -> u32, + GetControllerSwitchCount: proc "system" (this: ^IReading) -> u32, + GetControllerSwitchState: proc "system" (this: ^IReading, stateArrayCount: u32, stateArray: [^]SwitchPosition) -> u32, + GetKeyCount: proc "system" (this: ^IReading) -> u32, + GetKeyState: proc "system" (this: ^IReading, stateArrayCount: u32, stateArray: [^]KeyState) -> u32, + GetMouseState: proc "system" (this: ^IReading, state: ^MouseState) -> bool, + GetTouchCount: proc "system" (this: ^IReading) -> u32, + GetTouchState: proc "system" (this: ^IReading, stateArrayCount: u32, stateArray: [^]TouchState) -> u32, + GetMotionState: proc "system" (this: ^IReading, state: ^MotionState) -> bool, + GetArcadeStickState: proc "system" (this: ^IReading, state: ^ArcadeStickState) -> bool, + GetFlightStickState: proc "system" (this: ^IReading, state: ^FlightStickState) -> bool, + GetGamepadState: proc "system" (this: ^IReading, state: ^GamepadState) -> bool, + GetRacingWheelState: proc "system" (this: ^IReading, state: ^RacingWheelState) -> bool, + GetUiNavigationState: proc "system" (this: ^IReading, state: ^UiNavigationState) -> bool, } -IGameInputDevice_UUID_STRING :: "31DD86FB-4C1B-408A-868F-439B3CD47125" -IGameInputDevice_UUID := &IID{0x31DD86FB, 0x4C1B, 0x408A, {0x86, 0x8F, 0x43, 0x9B, 0x3C, 0xD4, 0x71, 0x25}} -IGameInputDevice :: struct #raw_union { +IDevice_UUID_STRING :: "31DD86FB-4C1B-408A-868F-439B3CD47125" +IDevice_UUID := &IID{0x31DD86FB, 0x4C1B, 0x408A, {0x86, 0x8F, 0x43, 0x9B, 0x3C, 0xD4, 0x71, 0x25}} +IDevice :: struct #raw_union { #subtype iunknown: IUnknown, - using igameinputdevice_vtable: ^IGameInputDevice_Vtable, + using igameinputdevice_vtable: ^IDevice_Vtable, } -IGameInputDevice_Vtable :: struct { +IDevice_Vtable :: struct { using iunknown_vtable: IUnknown_VTable, - GetDeviceInfo: proc "system" (this: ^IGameInputDevice) -> ^DeviceInfo, - GetDeviceStatus: proc "system" (this: ^IGameInputDevice) -> DeviceStatus, - GetBatteryState: proc "system" (this: ^IGameInputDevice, state: ^BatteryState), - CreateForceFeedbackEffect: proc "system" (this: ^IGameInputDevice, motorIndex: u32, params: ^ForceFeedbackParams, effect: ^^IGameInputForceFeedbackEffect) -> HRESULT, - IsForceFeedbackMotorPoweredOn: proc "system" (this: ^IGameInputDevice, motorIndex: u32) -> bool, - SetForceFeedbackMotorGain: proc "system" (this: ^IGameInputDevice, motorIndex: u32, masterGain: f32), - SetHapticMotorState: proc "system" (this: ^IGameInputDevice, motorIndex: u32, params: ^HapticFeedbackParams) -> HRESULT, - SetRumbleState: proc "system" (this: ^IGameInputDevice, params: ^RumbleParams), - SetInputSynchronizationState: proc "system" (this: ^IGameInputDevice, enabled: bool), - SendInputSynchronizationHint: proc "system" (this: ^IGameInputDevice), - PowerOff: proc "system" (this: ^IGameInputDevice), - CreateRawDeviceReport: proc "system" (this: ^IGameInputDevice, reportId: u32, reportKind: RawDeviceReportKind, report: ^^IGameInputRawDeviceReport) -> HRESULT, - GetRawDeviceFeature: proc "system" (this: ^IGameInputDevice, reportId: u32, report: ^^IGameInputRawDeviceReport) -> HRESULT, - SetRawDeviceFeature: proc "system" (this: ^IGameInputDevice, report: ^IGameInputRawDeviceReport) -> HRESULT, - SendRawDeviceOutput: proc "system" (this: ^IGameInputDevice, report: ^IGameInputRawDeviceReport) -> HRESULT, - SendRawDeviceOutputWithResponse: proc "system" (this: ^IGameInputDevice, requestReport: ^IGameInputRawDeviceReport, responseReport: ^^IGameInputRawDeviceReport) -> HRESULT, - ExecuteRawDeviceIoControl: proc "system" (this: ^IGameInputDevice, controlCode: u32, inputBufferSize: win.SIZE_T, inputBuffer: rawptr, outputBufferSize: win.SIZE_T, outputBuffer: rawptr, outputSize: ^win.SIZE_T) -> HRESULT, - AcquireExclusiveRawDeviceAccess: proc "system" (this: ^IGameInputDevice, timeoutInMicroseconds: u64) -> bool, - ReleaseExclusiveRawDeviceAccess: proc "system" (this: ^IGameInputDevice), + GetDeviceInfo: proc "system" (this: ^IDevice) -> ^DeviceInfo, + GetDeviceStatus: proc "system" (this: ^IDevice) -> DeviceStatus, + GetBatteryState: proc "system" (this: ^IDevice, state: ^BatteryState), + CreateForceFeedbackEffect: proc "system" (this: ^IDevice, motorIndex: u32, params: ^ForceFeedbackParams, effect: ^^IForceFeedbackEffect) -> HRESULT, + IsForceFeedbackMotorPoweredOn: proc "system" (this: ^IDevice, motorIndex: u32) -> bool, + SetForceFeedbackMotorGain: proc "system" (this: ^IDevice, motorIndex: u32, masterGain: f32), + SetHapticMotorState: proc "system" (this: ^IDevice, motorIndex: u32, params: ^HapticFeedbackParams) -> HRESULT, + SetRumbleState: proc "system" (this: ^IDevice, params: ^RumbleParams), + SetInputSynchronizationState: proc "system" (this: ^IDevice, enabled: bool), + SendInputSynchronizationHint: proc "system" (this: ^IDevice), + PowerOff: proc "system" (this: ^IDevice), + CreateRawDeviceReport: proc "system" (this: ^IDevice, reportId: u32, reportKind: RawDeviceReportKind, report: ^^IRawDeviceReport) -> HRESULT, + GetRawDeviceFeature: proc "system" (this: ^IDevice, reportId: u32, report: ^^IRawDeviceReport) -> HRESULT, + SetRawDeviceFeature: proc "system" (this: ^IDevice, report: ^IRawDeviceReport) -> HRESULT, + SendRawDeviceOutput: proc "system" (this: ^IDevice, report: ^IRawDeviceReport) -> HRESULT, + SendRawDeviceOutputWithResponse: proc "system" (this: ^IDevice, requestReport: ^IRawDeviceReport, responseReport: ^^IRawDeviceReport) -> HRESULT, + ExecuteRawDeviceIoControl: proc "system" (this: ^IDevice, controlCode: u32, inputBufferSize: win.SIZE_T, inputBuffer: rawptr, outputBufferSize: win.SIZE_T, outputBuffer: rawptr, outputSize: ^win.SIZE_T) -> HRESULT, + AcquireExclusiveRawDeviceAccess: proc "system" (this: ^IDevice, timeoutInMicroseconds: u64) -> bool, + ReleaseExclusiveRawDeviceAccess: proc "system" (this: ^IDevice), } -IGameInputDispatcher_UUID_STRING :: "415EED2E-98CB-42C2-8F28-B94601074E31" -IGameInputDispatcher_UUID := &IID{0x415EED2E, 0x98CB, 0x42C2, {0x8F, 0x28, 0xB9, 0x46, 0x01, 0x07, 0x4E, 0x31}} -IGameInputDispatcher :: struct #raw_union { +IDispatcher_UUID_STRING :: "415EED2E-98CB-42C2-8F28-B94601074E31" +IDispatcher_UUID := &IID{0x415EED2E, 0x98CB, 0x42C2, {0x8F, 0x28, 0xB9, 0x46, 0x01, 0x07, 0x4E, 0x31}} +IDispatcher :: struct #raw_union { #subtype iunknown: IUnknown, - using igameinputdispatcher_vtable: ^IGameInputDispatcher_Vtable, + using igameinputdispatcher_vtable: ^IDispatcher_Vtable, } -IGameInputDispatcher_Vtable :: struct { +IDispatcher_Vtable :: struct { using iunknown_vtable: IUnknown_VTable, - Dispatch: proc "system" (this: ^IGameInputDispatcher, quotaInMicroseconds: u64) -> bool, - OpenWaitHandle: proc "system" (this: ^IGameInputDispatcher, waitHandle: ^HANDLE) -> HRESULT, + Dispatch: proc "system" (this: ^IDispatcher, quotaInMicroseconds: u64) -> bool, + OpenWaitHandle: proc "system" (this: ^IDispatcher, waitHandle: ^HANDLE) -> HRESULT, } -IGameInputForceFeedbackEffect_UUID_STRING :: "51BDA05E-F742-45D9-B085-9444AE48381D" -IGameInputForceFeedbackEffect_UUID := &IID{0x51BDA05E, 0xF742, 0x45D9, {0xB0, 0x85, 0x94, 0x44, 0xAE, 0x48, 0x38, 0x1D}} -IGameInputForceFeedbackEffect :: struct #raw_union { +IForceFeedbackEffect_UUID_STRING :: "51BDA05E-F742-45D9-B085-9444AE48381D" +IForceFeedbackEffect_UUID := &IID{0x51BDA05E, 0xF742, 0x45D9, {0xB0, 0x85, 0x94, 0x44, 0xAE, 0x48, 0x38, 0x1D}} +IForceFeedbackEffect :: struct #raw_union { #subtype iunknown: IUnknown, - using igameinputforcefeedbackeffect_vtable: ^IGameInputForceFeedbackEffect_Vtable, + using igameinputforcefeedbackeffect_vtable: ^IForceFeedbackEffect_Vtable, } -IGameInputForceFeedbackEffect_Vtable :: struct { +IForceFeedbackEffect_Vtable :: struct { using iunknown_vtable: IUnknown_VTable, - GetDevice: proc "system" (this: ^IGameInputForceFeedbackEffect, device: ^^IGameInputDevice), - GetMotorIndex: proc "system" (this: ^IGameInputForceFeedbackEffect) -> u32, - GetGain: proc "system" (this: ^IGameInputForceFeedbackEffect) -> f32, - SetGain: proc "system" (this: ^IGameInputForceFeedbackEffect, gain: f32), - GetParams: proc "system" (this: ^IGameInputForceFeedbackEffect, params: ^ForceFeedbackParams), - SetParams: proc "system" (this: ^IGameInputForceFeedbackEffect, params: ^ForceFeedbackParams) -> bool, - GetState: proc "system" (this: ^IGameInputForceFeedbackEffect) -> FeedbackEffectState, - SetState: proc "system" (this: ^IGameInputForceFeedbackEffect, state: FeedbackEffectState), + GetDevice: proc "system" (this: ^IForceFeedbackEffect, device: ^^IDevice), + GetMotorIndex: proc "system" (this: ^IForceFeedbackEffect) -> u32, + GetGain: proc "system" (this: ^IForceFeedbackEffect) -> f32, + SetGain: proc "system" (this: ^IForceFeedbackEffect, gain: f32), + GetParams: proc "system" (this: ^IForceFeedbackEffect, params: ^ForceFeedbackParams), + SetParams: proc "system" (this: ^IForceFeedbackEffect, params: ^ForceFeedbackParams) -> bool, + GetState: proc "system" (this: ^IForceFeedbackEffect) -> FeedbackEffectState, + SetState: proc "system" (this: ^IForceFeedbackEffect, state: FeedbackEffectState), } -IGameInputRawDeviceReport_UUID_STRING :: "61F08CF1-1FFC-40CA-A2B8-E1AB8BC5B6DC" -IGameInputRawDeviceReport_UUID := &IID{0x61F08CF1, 0x1FFC, 0x40CA, {0xA2, 0xB8, 0xE1, 0xAB, 0x8B, 0xC5, 0xB6, 0xDC}} -IGameInputRawDeviceReport :: struct #raw_union { +IRawDeviceReport_UUID_STRING :: "61F08CF1-1FFC-40CA-A2B8-E1AB8BC5B6DC" +IRawDeviceReport_UUID := &IID{0x61F08CF1, 0x1FFC, 0x40CA, {0xA2, 0xB8, 0xE1, 0xAB, 0x8B, 0xC5, 0xB6, 0xDC}} +IRawDeviceReport :: struct #raw_union { #subtype iunknown: IUnknown, - using igameinputrawdevicereport_vtable: ^IGameInputRawDeviceReport_Vtable, + using igameinputrawdevicereport_vtable: ^IRawDeviceReport_Vtable, } -IGameInputRawDeviceReport_Vtable :: struct { +IRawDeviceReport_Vtable :: struct { using iunknown_vtable: IUnknown_VTable, - GetDevice: proc "system" (this: ^IGameInputRawDeviceReport, device: ^^IGameInputDevice), - GetReportInfo: proc "system" (this: ^IGameInputRawDeviceReport) -> ^RawDeviceReportInfo, - GetRawDataSize: proc "system" (this: ^IGameInputRawDeviceReport) -> win.SIZE_T, - GetRawData: proc "system" (this: ^IGameInputRawDeviceReport, bufferSize: win.SIZE_T, buffer: rawptr) -> win.SIZE_T, - SetRawData: proc "system" (this: ^IGameInputRawDeviceReport, bufferSize: win.SIZE_T, buffer: rawptr) -> bool, - GetItemValue: proc "system" (this: ^IGameInputRawDeviceReport, itemIndex: u32, value: ^u64) -> bool, - SetItemValue: proc "system" (this: ^IGameInputRawDeviceReport, itemIndex: u32, value: u64) -> bool, - ResetItemValue: proc "system" (this: ^IGameInputRawDeviceReport, itemIndex: u32) -> bool, - ResetAllItems: proc "system" (this: ^IGameInputRawDeviceReport) -> bool, + GetDevice: proc "system" (this: ^IRawDeviceReport, device: ^^IDevice), + GetReportInfo: proc "system" (this: ^IRawDeviceReport) -> ^RawDeviceReportInfo, + GetRawDataSize: proc "system" (this: ^IRawDeviceReport) -> win.SIZE_T, + GetRawData: proc "system" (this: ^IRawDeviceReport, bufferSize: win.SIZE_T, buffer: rawptr) -> win.SIZE_T, + SetRawData: proc "system" (this: ^IRawDeviceReport, bufferSize: win.SIZE_T, buffer: rawptr) -> bool, + GetItemValue: proc "system" (this: ^IRawDeviceReport, itemIndex: u32, value: ^u64) -> bool, + SetItemValue: proc "system" (this: ^IRawDeviceReport, itemIndex: u32, value: u64) -> bool, + ResetItemValue: proc "system" (this: ^IRawDeviceReport, itemIndex: u32) -> bool, + ResetAllItems: proc "system" (this: ^IRawDeviceReport) -> bool, } @(default_calling_convention="system", link_prefix="GameInput") diff --git a/vendor/x11/xlib/xlib.odin b/vendor/x11/xlib/xlib.odin index a2d51c401..c4071055a 100644 --- a/vendor/x11/xlib/xlib.odin +++ b/vendor/x11/xlib/xlib.odin @@ -1,4 +1,4 @@ -// Bindings for [[ X11's Xlib (PDF) ; https://www.x.org/docs/X11/xlib.pdf ]]. +// Bindings for [[ X11's Xlib (PDF) ; https://xorg.freedesktop.org/archive/current/doc/libX11/libX11/libX11.pdf ]]. package xlib // Value, specifying whether `vendor:x11/xlib` is available on the current platform.