mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 01:21:38 -07:00
Merge tag 'dev-2025-09'
This commit is contained in:
+17
-1
@@ -61,6 +61,11 @@ Type_Info_Struct_Soa_Kind :: enum u8 {
|
||||
Dynamic = 3,
|
||||
}
|
||||
|
||||
Type_Info_String_Encoding_Kind :: enum u8 {
|
||||
UTF_8 = 0,
|
||||
UTF_16 = 1,
|
||||
}
|
||||
|
||||
// Variant Types
|
||||
Type_Info_Named :: struct {
|
||||
name: string,
|
||||
@@ -73,7 +78,7 @@ Type_Info_Rune :: struct {}
|
||||
Type_Info_Float :: struct {endianness: Platform_Endianness}
|
||||
Type_Info_Complex :: struct {}
|
||||
Type_Info_Quaternion :: struct {}
|
||||
Type_Info_String :: struct {is_cstring: bool}
|
||||
Type_Info_String :: struct {is_cstring: bool, encoding: Type_Info_String_Encoding_Kind}
|
||||
Type_Info_Boolean :: struct {}
|
||||
Type_Info_Any :: struct {}
|
||||
Type_Info_Type_Id :: struct {}
|
||||
@@ -397,6 +402,11 @@ Raw_String :: struct {
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_String16 :: struct {
|
||||
data: [^]u16,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Slice :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
@@ -450,6 +460,12 @@ Raw_Cstring :: struct {
|
||||
}
|
||||
#assert(size_of(Raw_Cstring) == size_of(cstring))
|
||||
|
||||
Raw_Cstring16 :: struct {
|
||||
data: [^]u16,
|
||||
}
|
||||
#assert(size_of(Raw_Cstring16) == size_of(cstring16))
|
||||
|
||||
|
||||
Raw_Soa_Pointer :: struct {
|
||||
data: rawptr,
|
||||
index: int,
|
||||
|
||||
@@ -5,6 +5,11 @@ import "base:intrinsics"
|
||||
@builtin
|
||||
Maybe :: union($T: typeid) {T}
|
||||
|
||||
/*
|
||||
Represents an Objective-C block with a given procedure signature T
|
||||
*/
|
||||
@builtin
|
||||
Objc_Block :: struct($T: typeid) where intrinsics.type_is_proc(T) { using _: intrinsics.objc_object }
|
||||
|
||||
/*
|
||||
Recovers the containing/parent struct from a pointer to one of its fields.
|
||||
@@ -86,11 +91,26 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
|
||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||
// of len(src) and len(dst).
|
||||
//
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int {
|
||||
n := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
|
||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||
// of len(src) and len(dst).
|
||||
@builtin
|
||||
copy :: proc{copy_slice, copy_from_string}
|
||||
copy :: proc{copy_slice, copy_from_string, copy_from_string16}
|
||||
|
||||
|
||||
|
||||
@@ -285,6 +305,15 @@ delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free_with_size(raw_data(str), len(str)*size_of(u16), allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free((^u16)(str), allocator, 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.
|
||||
@@ -297,6 +326,8 @@ delete :: proc{
|
||||
delete_map,
|
||||
delete_soa_slice,
|
||||
delete_soa_dynamic_array,
|
||||
delete_string16,
|
||||
delete_cstring16,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -180,8 +180,31 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #cal
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
reserve_soa(array, length, loc) or_return
|
||||
|
||||
footer := raw_soa_footer(array)
|
||||
|
||||
if length > footer.cap {
|
||||
reserve_soa(array, length, loc) or_return
|
||||
} else if size_of(E) > 0 && length > footer.len {
|
||||
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
|
||||
data := (^rawptr)(array)^
|
||||
|
||||
soa_offset := 0
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
soa_offset = align_forward_int(soa_offset, align_of(E))
|
||||
|
||||
mem_zero(rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len)), type.size * (length - footer.len))
|
||||
|
||||
soa_offset += type.size * footer.cap
|
||||
}
|
||||
}
|
||||
|
||||
footer.len = length
|
||||
return nil
|
||||
}
|
||||
@@ -252,17 +275,77 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
|
||||
old_data := (^rawptr)(array)^
|
||||
|
||||
resize: if old_data != nil {
|
||||
|
||||
new_bytes, resize_err := array.allocator.procedure(
|
||||
array.allocator.data, .Resize_Non_Zeroed, new_size, max_align,
|
||||
old_data, old_size, loc,
|
||||
)
|
||||
new_data := raw_data(new_bytes)
|
||||
|
||||
#partial switch resize_err {
|
||||
case .Mode_Not_Implemented: break resize
|
||||
case .None: // continue resizing
|
||||
case: return resize_err
|
||||
}
|
||||
|
||||
footer.cap = capacity
|
||||
|
||||
old_offset := 0
|
||||
new_offset := 0
|
||||
|
||||
// Correct data memory
|
||||
// from: |x x y y z z _ _ _|
|
||||
// to: |x x _ y y _ z z _|
|
||||
|
||||
// move old data to the end of the new allocation to avoid overlap
|
||||
old_data = rawptr(uintptr(new_data) + uintptr(new_size - old_size))
|
||||
mem_copy(old_data, new_data, old_size)
|
||||
|
||||
// now: |_ _ _ x x y y z z|
|
||||
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
old_offset = align_forward_int(old_offset, max_align)
|
||||
new_offset = align_forward_int(new_offset, max_align)
|
||||
|
||||
new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset))
|
||||
old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset))
|
||||
|
||||
old_size_elem := type.size * old_cap
|
||||
new_size_elem := type.size * capacity
|
||||
|
||||
mem_copy(new_data_elem, old_data_elem, old_size_elem)
|
||||
|
||||
(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem
|
||||
|
||||
if zero_memory {
|
||||
mem_zero(rawptr(uintptr(new_data_elem) + uintptr(old_size_elem)), new_size_elem - old_size_elem)
|
||||
}
|
||||
|
||||
old_offset += old_size_elem
|
||||
new_offset += new_size_elem
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
new_bytes := array.allocator.procedure(
|
||||
array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
|
||||
nil, old_size, loc,
|
||||
) or_return
|
||||
new_data := raw_data(new_bytes)
|
||||
|
||||
|
||||
footer.cap = capacity
|
||||
|
||||
old_offset := 0
|
||||
new_offset := 0
|
||||
|
||||
// Correct data memory
|
||||
// from: |x x y y z z| ... |_ _ _ _ _ _ _ _ _|
|
||||
// to: |x x _ y y _ z z _|
|
||||
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
@@ -280,10 +363,12 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
new_offset += type.size * capacity
|
||||
}
|
||||
|
||||
array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, loc,
|
||||
) or_return
|
||||
if old_data != nil {
|
||||
array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, loc,
|
||||
) or_return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
@(require_results)
|
||||
nil_allocator :: proc "contextless" () -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
@@ -72,6 +73,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = panic_allocator_proc,
|
||||
|
||||
@@ -52,10 +52,13 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
return
|
||||
}
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
|
||||
memory_block_dealloc :: proc "contextless" (block_to_free: ^Memory_Block, loc := #caller_location) {
|
||||
if block_to_free != nil {
|
||||
|
||||
allocator := block_to_free.allocator
|
||||
// sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity)
|
||||
context = default_context()
|
||||
context.allocator = allocator
|
||||
mem_free(block_to_free, allocator, loc)
|
||||
}
|
||||
}
|
||||
@@ -174,7 +177,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
arena.total_used = 0
|
||||
}
|
||||
|
||||
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
arena_destroy :: proc "contextless" (arena: ^Arena, loc := #caller_location) {
|
||||
for arena.curr_block != nil {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
@@ -186,6 +189,7 @@ arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
arena.total_capacity = 0
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {}
|
||||
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {}
|
||||
|
||||
default_temp_allocator_proc :: nil_allocator_proc
|
||||
|
||||
@@ -28,7 +28,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
_ = arena_init(&s.arena, uint(size), backing_allocator)
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {
|
||||
if s != nil {
|
||||
arena_destroy(&s.arena)
|
||||
s^ = {}
|
||||
@@ -61,7 +61,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
}
|
||||
|
||||
@(fini, private)
|
||||
_destroy_temp_allocator_fini :: proc() {
|
||||
_destroy_temp_allocator_fini :: proc "contextless" () {
|
||||
default_temp_allocator_destroy(&global_default_temp_allocator_data)
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD :: #force_inline proc(ignore := false, loc :=
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
|
||||
/*
|
||||
Declarations which are required by the compiler
|
||||
|
||||
## Descriptions of files
|
||||
|
||||
There are a lot of files in this package and below is described roughly what
|
||||
kind of functionality is placed in different files:
|
||||
|
||||
| File pattern | Description
|
||||
|----------------------|------------------------------------------------------|
|
||||
| `core.odin` | Contains the declarations that compiler will require to be present. Contains context-related declarations, `Type_Info` declarations and some other types used to implement the runtime and other packages. |
|
||||
| `core_builtin*.odin` | Contain `@(builtin)` declarations that can be used without importing the package. Most of them aren't required by the compiler |
|
||||
| `default_*.odin` | Contain default implementations for context allocators |
|
||||
| `entry_*.odin` | Contain OS-specific entry points |
|
||||
| `os_specific_*.odin` | Contain OS-specific utility procedures |
|
||||
| `*internal*.odin` | Contain implementations for internal procedures that can be called by the compiler |
|
||||
|
||||
## Implementing custom runtime
|
||||
|
||||
For embedded and kernel development it might be required to re-implement parts
|
||||
of the `base:runtime` package. This can include changing the default printing
|
||||
procedures that handle console output when the program panics, custom
|
||||
entry-points, tailored for a specific platform or execution environment, or
|
||||
simply switching up implementations of some procedures.
|
||||
|
||||
In case this is required, the following is suggested:
|
||||
|
||||
1. Define `$ODIN_ROOT` environment variable to point to a directory within your
|
||||
project that contains the following directories: `base/`, `core/` and `vendor/`.
|
||||
2. Inside the `$ODIN_ROOT/base` subdirectory, implement the *necessary
|
||||
declarations*.
|
||||
|
||||
What constitutes the necessary definitions is described below.
|
||||
|
||||
### Context-related
|
||||
|
||||
The compiler will require these declarations as they concern the `context`
|
||||
variable.
|
||||
|
||||
* `Maybe`
|
||||
* `Source_Code_Location`
|
||||
* `Context`
|
||||
* `Allocator`
|
||||
* `Random_Generator`
|
||||
* `Logger`
|
||||
* `__init_context`
|
||||
|
||||
### Runtime initialization/cleanup
|
||||
|
||||
These are not strictly required for compilation, but if global variables or
|
||||
`@(init)`/`@(fini)` blocks are used, these procedures need to be called inside
|
||||
the entry point.
|
||||
|
||||
* `_startup_runtime`
|
||||
* `_cleanup_runtime`
|
||||
|
||||
### Type assertion check
|
||||
|
||||
These procedures are called every time `.(Type)` expressions are used in order
|
||||
to check the union tag or the underlying type of `any` before returning the
|
||||
value of the underlying type. These are not required if `-no-type-assert` is
|
||||
specified.
|
||||
|
||||
* `type_assertion_check`
|
||||
* `type_assertion_check2` (takes in typeid)
|
||||
|
||||
### Bounds checking procedures
|
||||
|
||||
These procedures are called every time index or slicing expression are used in
|
||||
order to perform bounds-checking before the actual operation. These are not
|
||||
required if the `-no-bounds-check` option is specified.
|
||||
|
||||
* `bounds_check_error`
|
||||
* `matrix_bounds_check_error`
|
||||
* `slice_expr_error_hi`
|
||||
* `slice_expr_error_lo_hi`
|
||||
* `multi_pointer_slice_expr_error`
|
||||
|
||||
### cstring calls
|
||||
|
||||
If `cstring` or `cstring16` types are used, these procedures are required.
|
||||
|
||||
* `cstring_to_string`
|
||||
* `cstring_len`
|
||||
* `cstring16_to_string16`
|
||||
* `cstring16_len`
|
||||
|
||||
### Comparison
|
||||
|
||||
These procedures are required for comparison operators between strings and other
|
||||
compound types to function properly. If strings, structs nor unions are compared,
|
||||
only `string_eq` procedure is required.
|
||||
|
||||
* `memory_equal`
|
||||
* `memory_compare`
|
||||
* `memory_compare_zero`
|
||||
* `cstring_eq`
|
||||
* `cstring16_eq`
|
||||
* `cstring_ne`
|
||||
* `cstring16_ne`
|
||||
* `cstring_lt`
|
||||
* `cstring16_lt`
|
||||
* `cstring_gt`
|
||||
* `cstring16_gt`
|
||||
* `cstring_le`
|
||||
* `cstring16_le`
|
||||
* `cstring_ge`
|
||||
* `cstring16_ge`
|
||||
* `string_eq`
|
||||
* `string16_eq`
|
||||
* `string_ne`
|
||||
* `string16_ne`
|
||||
* `string_lt`
|
||||
* `string16_lt`
|
||||
* `string_gt`
|
||||
* `string16_gt`
|
||||
* `string_le`
|
||||
* `string16_le`
|
||||
* `string_ge`
|
||||
* `string16_ge`
|
||||
* `complex32_eq`
|
||||
* `complex32_ne`
|
||||
* `complex64_eq`
|
||||
* `complex64_ne`
|
||||
* `complex128_eq`
|
||||
* `complex128_ne`
|
||||
* `quaternion64_eq`
|
||||
* `quaternion64_ne`
|
||||
* `quaternion128_eq`
|
||||
* `quaternion128_ne`
|
||||
* `quaternion256_eq`
|
||||
* `quaternion256_ne`
|
||||
|
||||
### for-in `string` type
|
||||
|
||||
These procedures are required to iterate strings using `for ... in` loop. If this
|
||||
kind of loop isn't used, these procedures aren't required.
|
||||
|
||||
* `string_decode_rune`
|
||||
* `string_decode_last_rune` (for `#reverse for`)
|
||||
|
||||
### Required when RTTI is enabled (the vast majority of targets)
|
||||
|
||||
These declarations are required unless the `-no-rtti` compiler option is
|
||||
specified. Note that in order to be useful, some other procedures need to be
|
||||
implemented. Those procedures aren't mentioned here as the compiler won't
|
||||
complain if they're missing.
|
||||
|
||||
* `Type_Info`
|
||||
* `type_table`
|
||||
* `__type_info_of`
|
||||
|
||||
### Hashing
|
||||
|
||||
Required if maps are used
|
||||
|
||||
* `default_hasher`
|
||||
* `default_hasher_cstring`
|
||||
* `default_hasher_string`
|
||||
|
||||
### Pseudo-CRT required procedured due to LLVM but useful in general
|
||||
|
||||
* `memset`
|
||||
* `memcpy`
|
||||
* `memove`
|
||||
|
||||
### Procedures required by the LLVM backend if u128/i128 is used
|
||||
|
||||
* `umodti3`
|
||||
* `udivti3`
|
||||
* `modti3`
|
||||
* `divti3`
|
||||
* `fixdfti`
|
||||
* `fixunsdfti`
|
||||
* `fixunsdfdi`
|
||||
* `floattidf`
|
||||
* `floattidf_unsigned`
|
||||
* `truncsfhf2`
|
||||
* `truncdfhf2`
|
||||
* `gnu_h2f_ieee`
|
||||
* `gnu_f2h_ieee`
|
||||
* `extendhfsf2`
|
||||
|
||||
### Procedures required by the LLVM backend if f16 is used (WASM only)
|
||||
|
||||
* `__ashlti3`
|
||||
* `__multi3`
|
||||
|
||||
### When -no-crt is defined (windows only)
|
||||
|
||||
* `_tls_index`
|
||||
* `_fltused`
|
||||
|
||||
### Arithmetic
|
||||
|
||||
* `quo_complex32`
|
||||
* `quo_complex64`
|
||||
* `quo_complex128`
|
||||
|
||||
* `mul_quaternion64`
|
||||
* `mul_quaternion128`
|
||||
* `mul_quaternion256`
|
||||
|
||||
* `quo_quaternion64`
|
||||
* `quo_quaternion128`
|
||||
* `quo_quaternion256`
|
||||
|
||||
* `abs_complex32`
|
||||
* `abs_complex64`
|
||||
* `abs_complex128`
|
||||
|
||||
* `abs_quaternion64`
|
||||
* `abs_quaternion128`
|
||||
* `abs_quaternion256`
|
||||
|
||||
## Map specific calls
|
||||
|
||||
* `map_seed_from_map_data`
|
||||
* `__dynamic_map_check_grow` (for static map calls)
|
||||
* `map_insert_hash_dynamic` (for static map calls)
|
||||
* `__dynamic_map_get` (for dynamic map calls)
|
||||
* `__dynamic_map_set` (for dynamic map calls)
|
||||
|
||||
## Dynamic literals (`[dynamic]T` and `map[K]V`) (can be disabled with `-no-dynamic-literals`)
|
||||
|
||||
* `__dynamic_array_reserve`
|
||||
* `__dynamic_array_append`
|
||||
* `__dynamic_map_reserve`
|
||||
|
||||
### Objective-C specific
|
||||
|
||||
* `objc_lookUpClass`
|
||||
* `sel_registerName`
|
||||
* `objc_allocateClassPair`
|
||||
|
||||
### Other required declarations
|
||||
|
||||
This is required without conditions.
|
||||
|
||||
* `Load_Directory_File`
|
||||
|
||||
*/
|
||||
package runtime
|
||||
@@ -1,180 +0,0 @@
|
||||
package runtime
|
||||
|
||||
/*
|
||||
|
||||
package runtime has numerous entities (declarations) which are required by the compiler to function.
|
||||
|
||||
|
||||
## Basic types and calls (and anything they rely on)
|
||||
|
||||
Source_Code_Location
|
||||
Context
|
||||
Allocator
|
||||
Logger
|
||||
|
||||
__init_context
|
||||
_cleanup_runtime
|
||||
|
||||
|
||||
## cstring calls
|
||||
|
||||
cstring_to_string
|
||||
cstring_len
|
||||
|
||||
|
||||
|
||||
## Required when RTTI is enabled (the vast majority of targets)
|
||||
|
||||
Type_Info
|
||||
|
||||
type_table
|
||||
__type_info_of
|
||||
|
||||
|
||||
## Hashing
|
||||
|
||||
default_hasher
|
||||
default_hasher_cstring
|
||||
default_hasher_string
|
||||
|
||||
|
||||
## Pseudo-CRT required procedured due to LLVM but useful in general
|
||||
memset
|
||||
memcpy
|
||||
memove
|
||||
|
||||
|
||||
## Procedures required by the LLVM backend if u128/i128 is used
|
||||
umodti3
|
||||
udivti3
|
||||
modti3
|
||||
divti3
|
||||
fixdfti
|
||||
fixunsdfti
|
||||
fixunsdfdi
|
||||
floattidf
|
||||
floattidf_unsigned
|
||||
truncsfhf2
|
||||
truncdfhf2
|
||||
gnu_h2f_ieee
|
||||
gnu_f2h_ieee
|
||||
extendhfsf2
|
||||
|
||||
## Procedures required by the LLVM backend if f16 is used
|
||||
__ashlti3 // wasm specific
|
||||
__multi3 // wasm specific
|
||||
|
||||
|
||||
## Required an entry point is defined (i.e. 'main')
|
||||
|
||||
args__
|
||||
|
||||
|
||||
## When -no-crt is defined (and not a wasm target) (mostly due to LLVM)
|
||||
_tls_index
|
||||
_fltused
|
||||
|
||||
|
||||
## Bounds checking procedures (when not disabled with -no-bounds-check)
|
||||
|
||||
bounds_check_error
|
||||
matrix_bounds_check_error
|
||||
slice_expr_error_hi
|
||||
slice_expr_error_lo_hi
|
||||
multi_pointer_slice_expr_error
|
||||
|
||||
|
||||
## Type assertion check
|
||||
|
||||
type_assertion_check
|
||||
type_assertion_check2 // takes in typeid
|
||||
|
||||
|
||||
## Arithmetic
|
||||
|
||||
quo_complex32
|
||||
quo_complex64
|
||||
quo_complex128
|
||||
|
||||
mul_quaternion64
|
||||
mul_quaternion128
|
||||
mul_quaternion256
|
||||
|
||||
quo_quaternion64
|
||||
quo_quaternion128
|
||||
quo_quaternion256
|
||||
|
||||
abs_complex32
|
||||
abs_complex64
|
||||
abs_complex128
|
||||
|
||||
abs_quaternion64
|
||||
abs_quaternion128
|
||||
abs_quaternion256
|
||||
|
||||
|
||||
## Comparison
|
||||
|
||||
memory_equal
|
||||
memory_compare
|
||||
memory_compare_zero
|
||||
|
||||
cstring_eq
|
||||
cstring_ne
|
||||
cstring_lt
|
||||
cstring_gt
|
||||
cstring_le
|
||||
cstring_gt
|
||||
|
||||
string_eq
|
||||
string_ne
|
||||
string_lt
|
||||
string_gt
|
||||
string_le
|
||||
string_gt
|
||||
|
||||
complex32_eq
|
||||
complex32_ne
|
||||
complex64_eq
|
||||
complex64_ne
|
||||
complex128_eq
|
||||
complex128_ne
|
||||
|
||||
quaternion64_eq
|
||||
quaternion64_ne
|
||||
quaternion128_eq
|
||||
quaternion128_ne
|
||||
quaternion256_eq
|
||||
quaternion256_ne
|
||||
|
||||
|
||||
## Map specific calls
|
||||
|
||||
map_seed_from_map_data
|
||||
__dynamic_map_check_grow // static map calls
|
||||
map_insert_hash_dynamic // static map calls
|
||||
__dynamic_map_get // dynamic map calls
|
||||
__dynamic_map_set // dynamic map calls
|
||||
|
||||
|
||||
## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
|
||||
|
||||
__dynamic_array_reserve
|
||||
__dynamic_array_append
|
||||
|
||||
__dynamic_map_reserve
|
||||
|
||||
|
||||
## Objective-C specific
|
||||
|
||||
objc_lookUpClass
|
||||
sel_registerName
|
||||
objc_allocateClassPair
|
||||
|
||||
|
||||
## for-in `string` type
|
||||
|
||||
string_decode_rune
|
||||
string_decode_last_rune // #reverse for
|
||||
|
||||
*/
|
||||
@@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(require_results)
|
||||
heap_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = heap_allocator_proc,
|
||||
|
||||
+153
-4
@@ -268,8 +268,8 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
m = (n-i) / 8 * 8
|
||||
for /**/; i < m; i += 8 {
|
||||
m = (n-i) / size_of(uintptr) * size_of(uintptr)
|
||||
for /**/; i < m; i += size_of(uintptr) {
|
||||
if intrinsics.unaligned_load(cast(^uintptr)&a[i]) != intrinsics.unaligned_load(cast(^uintptr)&b[i]) {
|
||||
return false
|
||||
}
|
||||
@@ -389,8 +389,8 @@ memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_
|
||||
}
|
||||
}
|
||||
|
||||
m = (n-i) / 8 * 8
|
||||
for /**/; i < m; i += 8 {
|
||||
m = (n-i) / size_of(uintptr) * size_of(uintptr)
|
||||
for /**/; i < m; i += size_of(uintptr) {
|
||||
if intrinsics.unaligned_load(cast(^uintptr)&bytes[i]) != 0 {
|
||||
return 1
|
||||
}
|
||||
@@ -493,12 +493,40 @@ string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
string16_eq :: proc "contextless" (lhs, rhs: string16) -> bool {
|
||||
x := transmute(Raw_String16)lhs
|
||||
y := transmute(Raw_String16)rhs
|
||||
if x.len != y.len {
|
||||
return false
|
||||
}
|
||||
return #force_inline memory_equal(x.data, y.data, x.len*size_of(u16))
|
||||
}
|
||||
|
||||
string16_cmp :: proc "contextless" (a, b: string16) -> int {
|
||||
x := transmute(Raw_String16)a
|
||||
y := transmute(Raw_String16)b
|
||||
|
||||
ret := memory_compare(x.data, y.data, min(x.len, y.len)*size_of(u16))
|
||||
if ret == 0 && x.len != y.len {
|
||||
return -1 if x.len < y.len else +1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) }
|
||||
string_lt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0 }
|
||||
string_gt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0 }
|
||||
string_le :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0 }
|
||||
string_ge :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0 }
|
||||
|
||||
string16_ne :: #force_inline proc "contextless" (a, b: string16) -> bool { return !string16_eq(a, b) }
|
||||
string16_lt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) < 0 }
|
||||
string16_gt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) > 0 }
|
||||
string16_le :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) <= 0 }
|
||||
string16_ge :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) >= 0 }
|
||||
|
||||
|
||||
cstring_len :: proc "contextless" (s: cstring) -> int {
|
||||
p0 := uintptr((^byte)(s))
|
||||
p := p0
|
||||
@@ -508,6 +536,16 @@ cstring_len :: proc "contextless" (s: cstring) -> int {
|
||||
return int(p - p0)
|
||||
}
|
||||
|
||||
cstring16_len :: proc "contextless" (s: cstring16) -> int {
|
||||
p := ([^]u16)(s)
|
||||
n := 0
|
||||
for p != nil && p[0] != 0 {
|
||||
p = p[1:]
|
||||
n += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
if s == nil {
|
||||
return ""
|
||||
@@ -517,6 +555,15 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
return transmute(string)Raw_String{ptr, n}
|
||||
}
|
||||
|
||||
cstring16_to_string16 :: proc "contextless" (s: cstring16) -> string16 {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
ptr := (^u16)(s)
|
||||
n := cstring16_len(s)
|
||||
return transmute(string16)Raw_String16{ptr, n}
|
||||
}
|
||||
|
||||
|
||||
cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool {
|
||||
x := ([^]byte)(lhs)
|
||||
@@ -559,6 +606,46 @@ cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return
|
||||
cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 }
|
||||
cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 }
|
||||
|
||||
cstring16_eq :: proc "contextless" (lhs, rhs: cstring16) -> bool {
|
||||
x := ([^]u16)(lhs)
|
||||
y := ([^]u16)(rhs)
|
||||
if x == y {
|
||||
return true
|
||||
}
|
||||
if (x == nil) ~ (y == nil) {
|
||||
return false
|
||||
}
|
||||
xn := cstring16_len(lhs)
|
||||
yn := cstring16_len(rhs)
|
||||
if xn != yn {
|
||||
return false
|
||||
}
|
||||
return #force_inline memory_equal(x, y, xn*size_of(u16))
|
||||
}
|
||||
|
||||
cstring16_cmp :: proc "contextless" (lhs, rhs: cstring16) -> int {
|
||||
x := ([^]u16)(lhs)
|
||||
y := ([^]u16)(rhs)
|
||||
if x == y {
|
||||
return 0
|
||||
}
|
||||
if (x == nil) ~ (y == nil) {
|
||||
return -1 if x == nil else +1
|
||||
}
|
||||
xn := cstring16_len(lhs)
|
||||
yn := cstring16_len(rhs)
|
||||
ret := memory_compare(x, y, min(xn, yn)*size_of(u16))
|
||||
if ret == 0 && xn != yn {
|
||||
return -1 if xn < yn else +1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
cstring16_ne :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return !cstring16_eq(a, b) }
|
||||
cstring16_lt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) < 0 }
|
||||
cstring16_gt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) > 0 }
|
||||
cstring16_le :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) <= 0 }
|
||||
cstring16_ge :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) >= 0 }
|
||||
|
||||
complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) }
|
||||
complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) }
|
||||
@@ -694,6 +781,68 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
return r, size
|
||||
}
|
||||
|
||||
|
||||
string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) {
|
||||
REPLACEMENT_CHAR :: '\ufffd'
|
||||
_surr1 :: 0xd800
|
||||
_surr2 :: 0xdc00
|
||||
_surr3 :: 0xe000
|
||||
_surr_self :: 0x10000
|
||||
|
||||
r := rune(REPLACEMENT_CHAR)
|
||||
|
||||
if len(s) < 1 {
|
||||
return r, 0
|
||||
}
|
||||
|
||||
w := 1
|
||||
switch c := s[0]; {
|
||||
case c < _surr1, _surr3 <= c:
|
||||
r = rune(c)
|
||||
case _surr1 <= c && c < _surr2 && 1 < len(s) &&
|
||||
_surr2 <= s[1] && s[1] < _surr3:
|
||||
r1, r2 := rune(c), rune(s[1])
|
||||
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
|
||||
r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
|
||||
}
|
||||
w += 1
|
||||
}
|
||||
return r, w
|
||||
}
|
||||
|
||||
string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) {
|
||||
REPLACEMENT_CHAR :: '\ufffd'
|
||||
_surr1 :: 0xd800
|
||||
_surr2 :: 0xdc00
|
||||
_surr3 :: 0xe000
|
||||
_surr_self :: 0x10000
|
||||
|
||||
r := rune(REPLACEMENT_CHAR)
|
||||
|
||||
if len(s) < 1 {
|
||||
return r, 0
|
||||
}
|
||||
|
||||
n := len(s)-1
|
||||
c := s[n]
|
||||
w := 1
|
||||
if _surr2 <= c && c < _surr3 {
|
||||
if n >= 1 {
|
||||
r1 := rune(s[n-1])
|
||||
r2 := rune(c)
|
||||
if _surr1 <= r1 && r1 < _surr2 {
|
||||
r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
|
||||
}
|
||||
w = 2
|
||||
}
|
||||
} else if c < _surr1 || _surr3 <= c {
|
||||
r = rune(c)
|
||||
}
|
||||
return r, w
|
||||
}
|
||||
|
||||
|
||||
|
||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||
p, q := abs(real(x)), abs(imag(x))
|
||||
if p < q {
|
||||
|
||||
@@ -293,7 +293,14 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("quaternion")
|
||||
print_u64(u64(8*ti.size))
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
print_byte('c')
|
||||
}
|
||||
print_string("string")
|
||||
switch info.encoding {
|
||||
case .UTF_8: /**/
|
||||
case .UTF_16: print_string("16")
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: print_string("bool")
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
@(priority_index=-1e6)
|
||||
@(priority_index=-1e5)
|
||||
foreign import ObjC "system:objc"
|
||||
|
||||
@(priority_index=-1e6)
|
||||
foreign import libSystem "system:System"
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
objc_id :: ^intrinsics.objc_object
|
||||
@@ -34,3 +37,10 @@ foreign ObjC {
|
||||
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
|
||||
}
|
||||
|
||||
foreign libSystem {
|
||||
_NSConcreteGlobalBlock: intrinsics.objc_class
|
||||
_NSConcreteStackBlock: intrinsics.objc_class
|
||||
|
||||
_Block_object_assign :: proc "c" (rawptr, rawptr, i32) ---
|
||||
_Block_object_dispose :: proc "c" (rawptr, i32) ---
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package runtime
|
||||
|
||||
init_default_context_for_js: Context
|
||||
@(init, private="file")
|
||||
init_default_context :: proc() {
|
||||
init_default_context_for_js = context
|
||||
init_default_context :: proc "contextless" () {
|
||||
__init_context(&init_default_context_for_js)
|
||||
}
|
||||
|
||||
@(export)
|
||||
|
||||
@@ -97,7 +97,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
|
||||
for &v in p {
|
||||
if pos == 0 {
|
||||
val = read_u64(r)
|
||||
pos = 7
|
||||
pos = 8
|
||||
}
|
||||
v = byte(val)
|
||||
val >>= 8
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package runtime
|
||||
|
||||
Thread_Local_Cleaner :: #type proc "odin" ()
|
||||
Thread_Local_Cleaner_Odin :: #type proc "odin" ()
|
||||
Thread_Local_Cleaner_Contextless :: #type proc "contextless" ()
|
||||
|
||||
Thread_Local_Cleaner :: union #shared_nil {Thread_Local_Cleaner_Odin, Thread_Local_Cleaner_Contextless}
|
||||
|
||||
@(private="file")
|
||||
thread_local_cleaners: [8]Thread_Local_Cleaner
|
||||
|
||||
|
||||
// Add a procedure that will be run at the end of a thread for the purpose of
|
||||
// deallocating state marked as `thread_local`.
|
||||
//
|
||||
@@ -29,6 +33,9 @@ run_thread_local_cleaners :: proc "odin" () {
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
p()
|
||||
switch v in p {
|
||||
case Thread_Local_Cleaner_Odin: v()
|
||||
case Thread_Local_Cleaner_Contextless: v()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,12 @@ wasm_allocator_init :: proc(a: ^WASM_Allocator, alignment: uint = 8) {
|
||||
|
||||
global_default_wasm_allocator_data: WASM_Allocator
|
||||
|
||||
@(require_results)
|
||||
default_wasm_allocator :: proc() -> Allocator {
|
||||
return wasm_allocator(&global_default_wasm_allocator_data)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
wasm_allocator :: proc(a: ^WASM_Allocator) -> Allocator {
|
||||
return {
|
||||
data = a,
|
||||
|
||||
Reference in New Issue
Block a user