mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-05 20:00:43 +00:00
Merge branch 'master' into bill/rexcode
This commit is contained in:
@@ -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 ---
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
217
base/runtime/internal_i128.odin
Normal file
217
base/runtime/internal_i128.odin
Normal file
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build !bedrock
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
// `(` <generated code>
|
||||
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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -449,6 +449,20 @@ gb_internal void array_unordered_remove(Array<T> *array, isize index) {
|
||||
array_pop(array);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal void array_inject_at(Array<T> *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 <typename T>
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 *> 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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
|
||||
Array<ObjcMethodData> *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;
|
||||
}
|
||||
|
||||
@@ -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<LLVMValueRef>(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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<LLVMTypeRef>(temporary_allocator(), 0, 3);
|
||||
auto fields = array_make<LLVMTypeRef>(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;
|
||||
|
||||
|
||||
103
src/main.cpp
103
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<String> 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<String> 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:<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)) {
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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"`,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
35
tests/issues/test_issue_6874.odin
Normal file
35
tests/issues/test_issue_6874.odin
Normal file
@@ -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)
|
||||
}
|
||||
4
vendor/sdl3/sdl3_joystick.odin
vendored
4
vendor/sdl3/sdl3_joystick.odin
vendored
@@ -2,10 +2,6 @@ package sdl3
|
||||
|
||||
import "core:c"
|
||||
|
||||
@(link_prefix="SDL_")
|
||||
foreign lib {
|
||||
joystick_lock: ^Mutex
|
||||
}
|
||||
|
||||
Joystick :: struct {}
|
||||
JoystickID :: distinct Uint32
|
||||
|
||||
206
vendor/windows/GameInput/windows_game_input.odin
vendored
206
vendor/windows/GameInput/windows_game_input.odin
vendored
@@ -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")
|
||||
|
||||
2
vendor/x11/xlib/xlib.odin
vendored
2
vendor/x11/xlib/xlib.odin
vendored
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user