mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-13 22:03:42 +00:00
Begin work on -bedrock mode
Currently disables 128-bit integers, `map` type, and RTTI
This commit is contained in:
@@ -279,7 +279,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 {
|
||||
|
||||
@@ -387,7 +387,7 @@ pop_front_safe :: proc {
|
||||
@builtin
|
||||
clear :: proc{
|
||||
clear_dynamic_array,
|
||||
clear_map,
|
||||
clear_map where !ODIN_BEDROCK,
|
||||
clear_fixed_capacity_dynamic_array,
|
||||
|
||||
clear_soa_dynamic_array,
|
||||
@@ -397,7 +397,7 @@ clear :: proc{
|
||||
@builtin
|
||||
reserve :: proc{
|
||||
reserve_dynamic_array,
|
||||
reserve_map,
|
||||
reserve_map where !ODIN_BEDROCK,
|
||||
|
||||
reserve_soa,
|
||||
}
|
||||
@@ -430,7 +430,7 @@ non_zero_resize :: proc{
|
||||
@builtin
|
||||
shrink :: proc{
|
||||
shrink_dynamic_array,
|
||||
shrink_map,
|
||||
shrink_map where !ODIN_BEDROCK,
|
||||
}
|
||||
|
||||
// `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
|
||||
@@ -471,14 +471,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 +481,16 @@ delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc :=
|
||||
return mem_free((^u16)(str), allocator, loc)
|
||||
}
|
||||
|
||||
when !ODIN_BEDROCK {
|
||||
// `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 +500,7 @@ delete :: proc{
|
||||
delete_cstring,
|
||||
delete_dynamic_array,
|
||||
delete_slice,
|
||||
delete_map,
|
||||
delete_map where !ODIN_BEDROCK,
|
||||
delete_soa_slice,
|
||||
delete_soa_dynamic_array,
|
||||
delete_string16,
|
||||
@@ -597,29 +599,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 !ODIN_BEDROCK {
|
||||
// `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 +654,8 @@ make :: proc{
|
||||
make_dynamic_array,
|
||||
make_dynamic_array_len,
|
||||
make_dynamic_array_len_cap,
|
||||
make_map,
|
||||
make_map_cap,
|
||||
make_map where !ODIN_BEDROCK,
|
||||
make_map_cap where !ODIN_BEDROCK,
|
||||
make_multi_pointer,
|
||||
|
||||
make_soa_slice,
|
||||
@@ -659,53 +664,54 @@ make :: proc{
|
||||
make_soa_dynamic_array_len_cap,
|
||||
}
|
||||
|
||||
when !ODIN_BEDROCK {
|
||||
|
||||
// `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) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
@@ -1470,52 +1476,55 @@ _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) -> (ptr: ^V) {
|
||||
key, value := key, value
|
||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||
}
|
||||
when !ODIN_BEDROCK {
|
||||
|
||||
// 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
|
||||
@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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1166,216 +1166,6 @@ extendhfsf2 :: proc "c" (value: __float16) -> f32 {
|
||||
|
||||
|
||||
|
||||
@(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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||
for i in 0..<size {
|
||||
j := offset+i
|
||||
|
||||
214
base/runtime/internal_i128.odin
Normal file
214
base/runtime/internal_i128.odin
Normal file
@@ -0,0 +1,214 @@
|
||||
#+vet !cast
|
||||
#+build !bedrock
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(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"
|
||||
|
||||
@@ -606,6 +606,8 @@ struct BuildContext {
|
||||
|
||||
isize max_error_count;
|
||||
|
||||
bool bedrock;
|
||||
|
||||
|
||||
u32 cmd_doc_flags;
|
||||
Array<String> extra_packages;
|
||||
@@ -1900,8 +1902,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1822,9 +1822,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) {
|
||||
|
||||
@@ -3061,6 +3061,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) {
|
||||
@@ -3800,6 +3804,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;
|
||||
|
||||
|
||||
@@ -1118,6 +1118,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]);
|
||||
@@ -1144,6 +1152,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},
|
||||
@@ -7556,6 +7566,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);
|
||||
}
|
||||
}
|
||||
|
||||
39
src/main.cpp
39
src/main.cpp
@@ -394,6 +394,8 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_BuildDiagnostics,
|
||||
|
||||
BuildFlag_Bedrock,
|
||||
|
||||
// internal use only
|
||||
BuildFlag_InternalFastISel,
|
||||
BuildFlag_InternalIgnoreLazy,
|
||||
@@ -627,6 +629,8 @@ 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_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);
|
||||
@@ -1599,6 +1603,11 @@ 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;
|
||||
break;
|
||||
|
||||
case BuildFlag_InternalFastISel:
|
||||
build_context.fast_isel = true;
|
||||
break;
|
||||
@@ -3520,6 +3529,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;
|
||||
@@ -3796,6 +3831,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)) {
|
||||
|
||||
@@ -2579,8 +2579,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;
|
||||
}
|
||||
@@ -6388,6 +6394,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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user