Merge pull request #6654 from odin-lang/bill/bedrock

`-bedrock`
This commit is contained in:
gingerBill
2026-06-24 13:28:10 +01:00
committed by GitHub
21 changed files with 676 additions and 440 deletions

View File

@@ -215,6 +215,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 +251,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 ---

View File

@@ -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 {

View File

@@ -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 {
@@ -1525,53 +1533,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
}
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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)

View 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))
}
}

View File

@@ -1,3 +1,4 @@
#+build !bedrock
package runtime
import "base:intrinsics"

View File

@@ -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);
}
}
}

View File

@@ -7789,6 +7789,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) {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -353,6 +353,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,
@@ -754,6 +756,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},

View File

@@ -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;
}

View File

@@ -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;
@@ -3595,6 +3617,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;
@@ -3899,6 +3947,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)) {

View File

@@ -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;
}
@@ -6404,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 = {};

View File

@@ -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;
}

View File

@@ -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