Merge remote-tracking branch 'offical/master'

This commit is contained in:
ed
2024-03-27 13:19:18 -04:00
25 changed files with 774 additions and 120 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016-2022 Ginger Bill. All rights reserved. Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
+25 -12
View File
@@ -367,23 +367,36 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, '}') or_return opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Struct: case runtime.Type_Info_Struct:
opt_write_start(w, opt, '{') or_return marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct)
for name, i in info.names {
json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json")
for name, i in info.names { opt_write_iteration(w, opt, i) or_return
json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json") if json_name != "" {
opt_write_key(w, opt, json_name) or_return
} else {
// Marshal the fields of 'using _: T' fields directly into the parent struct
if info.usings[i] && name == "_" {
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_struct_fields(w, any{data, id}, opt) or_return
continue
} else {
opt_write_key(w, opt, name) or_return
}
}
opt_write_iteration(w, opt, i) or_return id := info.types[i].id
if json_name != "" { data := rawptr(uintptr(v.data) + info.offsets[i])
opt_write_key(w, opt, json_name) or_return marshal_to_writer(w, any{data, id}, opt) or_return
} else {
opt_write_key(w, opt, name) or_return
} }
return
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_to_writer(w, any{data, id}, opt) or_return
} }
opt_write_start(w, opt, '{') or_return
marshal_struct_fields(w, v, opt) or_return
opt_write_end(w, opt, '}') or_return opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Union: case runtime.Type_Info_Union:
+4 -4
View File
@@ -1,7 +1,7 @@
package hash package hash
@(optimization_mode="speed") @(optimization_mode="speed")
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check { crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
result = seed result = seed
#no_bounds_check for b in data { #no_bounds_check for b in data {
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff] result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
@@ -15,7 +15,7 @@ crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds
Based on Mark Adler's v1.4 implementation in C under the ZLIB license. Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
*/ */
@(optimization_mode="speed") @(optimization_mode="speed")
crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check { crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
data := data data := data
result := ~u64le(seed) result := ~u64le(seed)
@@ -53,7 +53,7 @@ crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
Generator polynomial: x^64 + x^4 + x^3 + x + 1 Generator polynomial: x^64 + x^4 + x^3 + x + 1
*/ */
@(optimization_mode="speed") @(optimization_mode="speed")
crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check { crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
result := seed result := seed
@@ -70,7 +70,7 @@ crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
return result return result
} }
crc64_iso_3306_inverse :: proc(data: []byte, seed := u64(0)) -> u64 { crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 {
result := #force_inline crc64_iso_3306(data, ~seed) result := #force_inline crc64_iso_3306(data, ~seed)
return ~result return ~result
} }
+2 -2
View File
@@ -3,7 +3,7 @@ package hash
import "base:intrinsics" import "base:intrinsics"
@(optimization_mode="speed") @(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check { crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc := ~seed crc := ~seed
buffer := raw_data(data) buffer := raw_data(data)
length := len(data) length := len(data)
@@ -323,7 +323,7 @@ crc32_table := [8][256]u32{
/* /*
@(optimization_mode="speed") @(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 { crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
result := ~u32(seed); result := ~u32(seed);
#no_bounds_check for b in data { #no_bounds_check for b in data {
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff]; result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
+12 -12
View File
@@ -4,7 +4,7 @@ import "core:mem"
import "base:intrinsics" import "base:intrinsics"
@(optimization_mode="speed") @(optimization_mode="speed")
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check { adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
ADLER_CONST :: 65521 ADLER_CONST :: 65521
@@ -47,7 +47,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 { djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b) hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
@@ -55,7 +55,7 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
return hash return hash
} }
djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check { djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
state := [4]u32{seed, seed, seed, seed} state := [4]u32{seed, seed, seed, seed}
s: u32 = 0 s: u32 = 0
@@ -74,7 +74,7 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
// If you have a choice, prefer fnv32a // If you have a choice, prefer fnv32a
@(optimization_mode="speed") @(optimization_mode="speed")
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
for b in data { for b in data {
h = (h * 0x01000193) ~ u32(b) h = (h * 0x01000193) ~ u32(b)
@@ -87,7 +87,7 @@ fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
// If you have a choice, prefer fnv64a // If you have a choice, prefer fnv64a
@(optimization_mode="speed") @(optimization_mode="speed")
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 { fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed h: u64 = seed
for b in data { for b in data {
h = (h * 0x100000001b3) ~ u64(b) h = (h * 0x100000001b3) ~ u64(b)
@@ -95,7 +95,7 @@ fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
return h return h
} }
@(optimization_mode="speed") @(optimization_mode="speed")
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
for b in data { for b in data {
h = (h ~ u32(b)) * 0x01000193 h = (h ~ u32(b)) * 0x01000193
@@ -104,7 +104,7 @@ fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 { fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed h: u64 = seed
for b in data { for b in data {
h = (h ~ u64(b)) * 0x100000001b3 h = (h ~ u64(b)) * 0x100000001b3
@@ -113,7 +113,7 @@ fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 { jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
hash += u32(b) hash += u32(b)
@@ -127,7 +127,7 @@ jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 { murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
c1_32: u32 : 0xcc9e2d51 c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593 c2_32: u32 : 0x1b873593
@@ -178,7 +178,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96 // See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed") @(optimization_mode="speed")
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0xc6a4a7935bd1e995 m :: 0xc6a4a7935bd1e995
r :: 47 r :: 47
@@ -219,7 +219,7 @@ murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140 // See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
@(optimization_mode="speed") @(optimization_mode="speed")
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0x5bd1e995 m :: 0x5bd1e995
r :: 24 r :: 24
@@ -287,7 +287,7 @@ murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 { sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
hash = u32(b) + (hash<<6) + (hash<<16) - hash hash = u32(b) + (hash<<6) + (hash<<16) - hash
+4 -4
View File
@@ -1,6 +1,6 @@
package hash package hash
ginger_hash8 :: proc(x: u8) -> u8 { ginger_hash8 :: proc "contextless" (x: u8) -> u8 {
h := x * 251 h := x * 251
h += ~(x << 3) h += ~(x << 3)
h ~= (x >> 1) h ~= (x >> 1)
@@ -11,7 +11,7 @@ ginger_hash8 :: proc(x: u8) -> u8 {
} }
ginger_hash16 :: proc(x: u16) -> u16 { ginger_hash16 :: proc "contextless" (x: u16) -> u16 {
z := (x << 8) | (x >> 8) z := (x << 8) | (x >> 8)
h := z h := z
h += ~(z << 5) h += ~(z << 5)
@@ -24,14 +24,14 @@ ginger_hash16 :: proc(x: u16) -> u16 {
} }
ginger8 :: proc(data: []byte) -> u8 { ginger8 :: proc "contextless" (data: []byte) -> u8 {
h := ginger_hash8(0) h := ginger_hash8(0)
for b in data { for b in data {
h ~= ginger_hash8(b) h ~= ginger_hash8(b)
} }
return h return h
} }
ginger16 :: proc(data: []byte) -> u16 { ginger16 :: proc "contextless" (data: []byte) -> u16 {
h := ginger_hash16(0) h := ginger_hash16(0)
for b in data { for b in data {
h ~= ginger_hash16(u16(b)) h ~= ginger_hash16(u16(b))
+1 -1
View File
@@ -129,7 +129,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
} }
XXH3_destroy_state :: proc(state: ^XXH3_state, allocator := context.allocator) -> (err: Error) { XXH3_destroy_state :: proc(state: ^XXH3_state, allocator := context.allocator) -> (err: Error) {
free(state) free(state, allocator)
return .None return .None
} }
+9 -9
View File
@@ -19,15 +19,15 @@ xxh_u32 :: u32
XXH32_DEFAULT_SEED :: XXH32_hash(0) XXH32_DEFAULT_SEED :: XXH32_hash(0)
XXH32_state :: struct { XXH32_state :: struct {
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */ total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
v1: XXH32_hash, /*!< First accumulator lane */ v1: XXH32_hash, /*!< First accumulator lane */
v2: XXH32_hash, /*!< Second accumulator lane */ v2: XXH32_hash, /*!< Second accumulator lane */
v3: XXH32_hash, /*!< Third accumulator lane */ v3: XXH32_hash, /*!< Third accumulator lane */
v4: XXH32_hash, /*!< Fourth accumulator lane */ v4: XXH32_hash, /*!< Fourth accumulator lane */
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */ memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */ reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
} }
XXH32_canonical :: struct { XXH32_canonical :: struct {
+62 -2
View File
@@ -3,6 +3,7 @@
package sync package sync
import "core:c" import "core:c"
import "core:sys/darwin"
import "core:time" import "core:time"
foreign import System "system:System.framework" foreign import System "system:System.framework"
@@ -29,8 +30,29 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
} }
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool { _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
timeout_ns := u32(duration) * 1000 when darwin.WAIT_ON_ADDRESS_AVAILABLE {
s: i32
if duration > 0 {
s = darwin.os_sync_wait_on_address_with_timeout(f, u64(expected), size_of(Futex), {}, .MACH_ABSOLUTE_TIME, u64(duration))
} else {
s = darwin.os_sync_wait_on_address(f, u64(expected), size_of(Futex), {})
}
if s >= 0 {
return true
}
switch darwin.errno() {
case -EINTR, -EFAULT:
return true
case -ETIMEDOUT:
return false
case:
_panic("darwin.os_sync_wait_on_address_with_timeout failure")
}
} else {
timeout_ns := u32(duration) * 1000
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns) s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
if s >= 0 { if s >= 0 {
return true return true
@@ -45,9 +67,27 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
} }
return true return true
}
} }
_futex_signal :: proc "contextless" (f: ^Futex) { _futex_signal :: proc "contextless" (f: ^Futex) {
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
loop: for {
s := darwin.os_sync_wake_by_address_any(f, size_of(Futex), {})
if s >= 0 {
return
}
switch darwin.errno() {
case -EINTR, -EFAULT:
continue loop
case -ENOENT:
return
case:
_panic("darwin.os_sync_wake_by_address_any failure")
}
}
} else {
loop: for { loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0) s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
if s >= 0 { if s >= 0 {
@@ -62,9 +102,28 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
_panic("futex_wake_single failure") _panic("futex_wake_single failure")
} }
} }
}
} }
_futex_broadcast :: proc "contextless" (f: ^Futex) { _futex_broadcast :: proc "contextless" (f: ^Futex) {
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
loop: for {
s := darwin.os_sync_wake_by_address_all(f, size_of(Futex), {})
if s >= 0 {
return
}
switch darwin.errno() {
case -EINTR, -EFAULT:
continue loop
case -ENOENT:
return
case:
_panic("darwin.os_sync_wake_by_address_all failure")
}
}
} else {
loop: for { loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0) s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
if s >= 0 { if s >= 0 {
@@ -79,5 +138,6 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
_panic("futex_wake_all failure") _panic("futex_wake_all failure")
} }
} }
}
}
}
+9
View File
@@ -3,6 +3,8 @@ package darwin
import "core:c" import "core:c"
foreign import system "system:System.framework"
Bool :: b8 Bool :: b8
RUsage :: struct { RUsage :: struct {
@@ -24,3 +26,10 @@ RUsage :: struct {
ru_nivcsw: c.long, ru_nivcsw: c.long,
} }
foreign system {
__error :: proc() -> ^i32 ---
}
errno :: #force_inline proc "contextless" () -> i32 {
return __error()^
}
+309
View File
@@ -0,0 +1,309 @@
package darwin
foreign import system "system:System.framework"
// #define OS_WAIT_ON_ADDR_AVAILABILITY \
// __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
when ODIN_OS == .Darwin {
when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION > 17_04_00 {
WAIT_ON_ADDRESS_AVAILABLE :: true
} else when ODIN_MINIMUM_OS_VERSION > 14_04_00 {
WAIT_ON_ADDRESS_AVAILABLE :: true
} else {
WAIT_ON_ADDRESS_AVAILABLE :: false
}
} else {
WAIT_ON_ADDRESS_AVAILABLE :: false
}
os_sync_wait_on_address_flag :: enum u32 {
// This flag should be used as a default flag when no other flags listed below are required.
NONE,
// This flag should be used when synchronizing among multiple processes by
// placing the @addr passed to os_sync_wait_on_address and its variants
// in a shared memory region.
//
// When using this flag, it is important to pass OS_SYNC_WAKE_BY_ADDRESS_SHARED
// flag along with the exact same @addr to os_sync_wake_by_address_any and
// its variants to correctly find and wake up blocked waiters on the @addr.
//
// This flag should not be used when synchronizing among multiple threads of
// a single process. It allows the kernel to perform performance optimizations
// as the @addr is local to the calling process.
SHARED,
}
os_sync_wait_on_address_flags :: bit_set[os_sync_wait_on_address_flag; u32]
os_sync_wake_by_address_flag :: enum u32 {
// This flag should be used as a default flag when no other flags listed below are required.
NONE,
// This flag should be used when synchronizing among multiple processes by
// placing the @addr passed to os_sync_wake_by_address_any and its variants
// in a shared memory region.
//
// When using this flag, it is important to pass OS_SYNC_WAIT_ON_ADDRESS_SHARED
// flag along with the exact same @addr to os_sync_wait_on_address and
// its variants to correctly find and wake up blocked waiters on the @addr.
//
// This flag should not be used when synchronizing among multiple threads of
// a single process. It allows the kernel to perform performance optimizations
// as the @addr is local the calling process.
SHARED,
}
os_sync_wake_by_address_flags :: bit_set[os_sync_wake_by_address_flag; u32]
os_clockid :: enum u32 {
MACH_ABSOLUTE_TIME = 32,
}
foreign system {
// This function provides an atomic compare-and-wait functionality that
// can be used to implement other higher level synchronization primitives.
//
// It reads a value from @addr, compares it to expected @value and blocks
// the calling thread if they are equal. This sequence of operations is
// done atomically with respect to other concurrent operations that can
// be performed on this @addr by other threads using this same function
// or os_sync_wake_by_addr variants. At this point, the blocked calling
// thread is considered to be a waiter on this @addr, waiting to be woken
// up by a call to os_sync_wake_by_addr variants. If the value at @addr
// turns out to be different than expected, the calling thread returns
// immediately without blocking.
//
// This function is expected to be used for implementing synchronization
// primitives that do not have a sense of ownership (e.g. condition
// variables, semaphores) as it does not provide priority inversion avoidance.
// For locking primitives, it is recommended that you use existing OS
// primitives such as os_unfair_lock API family / pthread mutex or
// std::mutex.
//
// @param addr
// The userspace address to be used for atomic compare-and-wait.
// This address must be aligned to @size.
//
// @param value
// The value expected at @addr.
//
// @param size
// The size of @value, in bytes. This can be either 4 or 8 today.
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
//
// @param flags
// Flags to alter behavior of os_sync_wait_on_address.
// See os_sync_wait_on_address_flags_t.
//
// @return
// If the calling thread is woken up by a call to os_sync_wake_by_addr
// variants or the value at @addr is different than expected, this function
// returns successfully and the return value indicates the number
// of outstanding waiters blocked on this address.
// In the event of an error, returns -1 with errno set to indicate the error.
//
// EINVAL : Invalid flags or size.
// EINVAL : The @addr passed is NULL or misaligned.
// EINVAL : The operation associated with existing kernel state
// at this @addr is inconsistent with what the caller
// has requested.
// It is important to make sure consistent values are
// passed across wait and wake APIs for @addr, @size
// and the shared memory specification
// (See os_sync_wait_on_address_flags_t).
//
// It is possible for the os_sync_wait_on_address and its variants to perform
// an early return in the event of following errors where user may want to
// re-try the wait operation. E.g. low memory conditions could cause such early
// return.
// It is important to read the current value at the @addr before re-trying
// to ensure that the new value still requires waiting on @addr.
//
// ENOMEM : Unable to allocate memory for kernel internal data
// structures.
// EINTR : The syscall was interrupted / spurious wake up.
// EFAULT : Unable to read value from the @addr. Kernel copyin failed.
// It is possible to receive EFAULT error in following cases:
// 1. The @addr is an invalid address. This is a programmer error.
// 2. The @addr is valid; but, this is a transient error such as
// due to low memory conditions. User may want to re-try the wait
// operation.
// Following code snippet illustrates a possible re-try loop.
// <code>
// retry:
// current = atomic_load_explicit(addr, memory_order_relaxed);
// if (current != expected) {
// int ret = os_sync_wait_on_address(addr, current, size, flags);
// if ((ret < 0) && ((errno == EINTR) || (errno == EFAULT))) {
// goto retry;
// }
// }
// </code>
os_sync_wait_on_address :: proc(
addr: rawptr,
value: u64,
size: uint,
flags: os_sync_wait_on_address_flags,
) -> i32 ---
// This function is a variant of os_sync_wait_on_address that
// allows the calling thread to specify a deadline
// until which it is willing to block.
//
// @param addr
// The userspace address to be used for atomic compare-and-wait.
// This address must be aligned to @size.
//
// @param value
// The value expected at @addr.
//
// @param size
// The size of @value, in bytes. This can be either 4 or 8 today.
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
//
// @param flags
// Flags to alter behavior of os_sync_wait_on_address_with_deadline.
// See os_sync_wait_on_address_flags_t.
//
// @param clockid
// This value anchors @deadline argument to a specific clock id.
// See os_clockid_t.
//
// @param deadline
// This value is used to specify a deadline until which the calling
// thread is willing to block.
// Passing zero for the @deadline results in an error being returned.
// It is recommended to use os_sync_wait_on_address API to block
// indefinitely until woken up by a call to os_sync_wake_by_address_any
// or os_sync_wake_by_address_all APIs.
//
// @return
// If the calling thread is woken up by a call to os_sync_wake_by_addr
// variants or the value at @addr is different than expected, this function
// returns successfully and the return value indicates the number
// of outstanding waiters blocked on this address.
// In the event of an error, returns -1 with errno set to indicate the error.
//
// In addition to errors returned by os_sync_wait_on_address, this function
// can return the following additional error codes.
//
// EINVAL : Invalid clock id.
// EINVAL : The @deadline passed is 0.
// ETIMEDOUT : Deadline expired.
os_sync_wait_on_address_with_deadline :: proc(
addr: rawptr,
value: u64,
size: uint,
flags: os_sync_wait_on_address_flags,
clockid: os_clockid,
deadline: u64,
) -> i32 ---
// This function is a variant of os_sync_wait_on_address that
// allows the calling thread to specify a timeout
// until which it is willing to block.
//
// @param addr
// The userspace address to be used for atomic compare-and-wait.
// This address must be aligned to @size.
//
// @param value
// The value expected at @addr.
//
// @param size
// The size of @value, in bytes. This can be either 4 or 8 today.
// For @value of @size 4 bytes, the upper 4 bytes of @value are ignored.
//
// @param flags
// Flags to alter behavior of os_sync_wait_on_address_with_timeout.
// See os_sync_wait_on_address_flags_t.
//
// @param clockid
// This value anchors @timeout_ns argument to a specific clock id.
// See os_clockid_t.
//
// @param timeout_ns
// This value is used to specify a timeout in nanoseconds until which
// the calling thread is willing to block.
// Passing zero for the @timeout_ns results in an error being returned.
// It is recommended to use os_sync_wait_on_address API to block
// indefinitely until woken up by a call to os_sync_wake_by_address_any
// or os_sync_wake_by_address_all APIs.
//
// @return
// If the calling thread is woken up by a call to os_sync_wake_by_address
// variants or the value at @addr is different than expected, this function
// returns successfully and the return value indicates the number
// of outstanding waiters blocked on this address.
// In the event of an error, returns -1 with errno set to indicate the error.
//
// In addition to errors returned by os_sync_wait_on_address, this function
// can return the following additional error codes.
//
// EINVAL : Invalid clock id.
// EINVAL : The @timeout_ns passed is 0.
// ETIMEDOUT : Timeout expired.
os_sync_wait_on_address_with_timeout :: proc(
addr: rawptr,
value: u64,
size: uint,
flags: os_sync_wait_on_address_flags,
clockid: os_clockid,
timeout_ns: u64,
) -> i32 ---
// This function wakes up one waiter out of all those blocked in os_sync_wait_on_address
// or its variants on the @addr. No guarantee is provided about which
// specific waiter is woken up.
//
// @param addr
// The userspace address to be used for waking up the blocked waiter.
// It should be same as what is passed to os_sync_wait_on_address or its variants.
//
// @param size
// The size of lock value, in bytes. This can be either 4 or 8 today.
// It should be same as what is passed to os_sync_wait_on_address or its variants.
//
// @param flags
// Flags to alter behavior of os_sync_wake_by_address_any.
// See os_sync_wake_by_address_flags_t.
//
// @return
// Returns 0 on success.
// In the event of an error, returns -1 with errno set to indicate the error.
//
// EINVAL : Invalid flags or size.
// EINVAL : The @addr passed is NULL.
// EINVAL : The operation associated with existing kernel state
// at this @addr is inconsistent with what caller
// has requested.
// It is important to make sure consistent values are
// passed across wait and wake APIs for @addr, @size
// and the shared memory specification
// (See os_sync_wake_by_address_flags_t).
// ENOENT : No waiter(s) found waiting on the @addr.
os_sync_wake_by_address_any :: proc(addr: rawptr, size: uint, flags: os_sync_wait_on_address_flags) -> i32 ---
// This function is a variant of os_sync_wake_by_address_any that wakes up all waiters
// blocked in os_sync_wait_on_address or its variants.
//
// @param addr
// The userspace address to be used for waking up the blocked waiters.
// It should be same as what is passed to os_sync_wait_on_address or its variants.
//
// @param size
// The size of lock value, in bytes. This can be either 4 or 8 today.
// It should be same as what is passed to os_sync_wait_on_address or its variants.
//
// @param flags
// Flags to alter behavior of os_sync_wake_by_address_all.
// See os_sync_wake_by_address_flags_t.
//
// @return
// Returns 0 on success.
// In the event of an error, returns -1 with errno set to indicate the error.
//
// This function returns same error codes as returned by os_sync_wait_on_address.
os_sync_wake_by_address_all :: proc(addr: rawptr, size: uint, flags: os_sync_wait_on_address_flags) -> i32 ---
}
+2 -1
View File
@@ -238,8 +238,9 @@ time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
// //
// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case. // Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
// On Windows it depends but is comparable with regular sleep in the worst case. // On Windows it depends but is comparable with regular sleep in the worst case.
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to // To get the same kind of accuracy as on Linux, have your program call `windows.timeBeginPeriod(1)` to
// tell Windows to use a more accurate timer for your process. // tell Windows to use a more accurate timer for your process.
// Additionally your program should call `windows.timeEndPeriod(1)` once you're done with `accurate_sleep`.
accurate_sleep :: proc "contextless" (d: Duration) { accurate_sleep :: proc "contextless" (d: Duration) {
to_sleep, estimate, mean, m2, count: Duration to_sleep, estimate, mean, m2, count: Duration
+3 -1
View File
@@ -436,7 +436,9 @@ struct BuildContext {
BlockingMutex target_features_mutex; BlockingMutex target_features_mutex;
StringSet target_features_set; StringSet target_features_set;
String target_features_string; String target_features_string;
String minimum_os_version_string; String minimum_os_version_string;
bool minimum_os_version_string_given;
}; };
gb_global BuildContext build_context = {0}; gb_global BuildContext build_context = {0};
@@ -1419,7 +1421,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->metrics = *metrics; bc->metrics = *metrics;
if (metrics->os == TargetOs_darwin) { if (metrics->os == TargetOs_darwin) {
if (bc->minimum_os_version_string.len == 0) { if (!bc->minimum_os_version_string_given) {
bc->minimum_os_version_string = str_lit("11.0.0"); bc->minimum_os_version_string = str_lit("11.0.0");
} }
+5
View File
@@ -122,6 +122,8 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr);
gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands); gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands);
gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish);
enum LoadDirectiveResult { enum LoadDirectiveResult {
LoadDirective_Success = 0, LoadDirective_Success = 0,
LoadDirective_Error = 1, LoadDirective_Error = 1,
@@ -5031,6 +5033,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
} }
} }
if (operand->type && is_type_soa_struct(type_deref(operand->type))) {
complete_soa_type(c->checker, type_deref(operand->type), false);
}
if (entity == nullptr && selector->kind == Ast_Ident) { if (entity == nullptr && selector->kind == Ast_Ident) {
String field_name = selector->Ident.token.string; String field_name = selector->Ident.token.string;
+174 -55
View File
@@ -2683,6 +2683,118 @@ type_assign:;
return; return;
} }
struct SoaTypeWorkerData {
CheckerContext ctx;
Type * type;
bool wait_to_finish;
};
gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish) {
Type *original_type = t;
gb_unused(original_type);
t = base_type(t);
if (t == nullptr || !is_type_soa_struct(t)) {
return true;
}
MUTEX_GUARD(&t->Struct.soa_mutex);
if (t->Struct.fields_wait_signal.futex.load()) {
return true;
}
isize field_count = 0;
i32 extra_field_count = 0;
switch (t->Struct.soa_kind) {
case StructSoa_Fixed: extra_field_count = 0; break;
case StructSoa_Slice: extra_field_count = 1; break;
case StructSoa_Dynamic: extra_field_count = 3; break;
}
Scope *scope = t->Struct.scope;
i64 soa_count = t->Struct.soa_count;
Type *elem = t->Struct.soa_elem;
Type *old_struct = base_type(elem);
GB_ASSERT(old_struct->kind == Type_Struct);
if (wait_to_finish) {
wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
} else {
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load());
}
field_count = old_struct->Struct.fields.count;
t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
auto const &add_entity = [](Scope *scope, Entity *entity) {
String name = entity->token.string;
if (!is_blank_ident(name)) {
Entity *ie = scope_insert(scope, entity);
if (ie != nullptr) {
redeclaration_error(name, entity, ie);
}
}
};
for_array(i, old_struct->Struct.fields) {
Entity *old_field = old_struct->Struct.fields[i];
if (old_field->kind == Entity_Variable) {
Type *field_type = nullptr;
if (t->Struct.soa_kind == StructSoa_Fixed) {
GB_ASSERT(soa_count >= 0);
field_type = alloc_type_array(old_field->type, soa_count);
} else {
field_type = alloc_type_pointer(old_field->type);
}
Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
t->Struct.fields[i] = new_field;
add_entity(scope, new_field);
new_field->flags |= EntityFlag_Used;
} else {
t->Struct.fields[i] = old_field;
}
t->Struct.tags[i] = old_struct->Struct.tags[i];
}
if (t->Struct.soa_kind != StructSoa_Fixed) {
Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
t->Struct.fields[field_count+0] = len_field;
add_entity(scope, len_field);
len_field->flags |= EntityFlag_Used;
if (t->Struct.soa_kind == StructSoa_Dynamic) {
Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1);
t->Struct.fields[field_count+1] = cap_field;
add_entity(scope, cap_field);
cap_field->flags |= EntityFlag_Used;
init_mem_allocator(checker);
Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2);
t->Struct.fields[field_count+2] = allocator_field;
add_entity(scope, allocator_field);
allocator_field->flags |= EntityFlag_Used;
}
}
// add_type_info_type(ctx, original_type);
wait_signal_set(&t->Struct.fields_wait_signal);
return true;
}
gb_internal WORKER_TASK_PROC(complete_soa_type_worker) {
SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data;
complete_soa_type(wd->ctx.checker, wd->type, wd->wait_to_finish);
return 0;
}
gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) { gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) {
@@ -2697,8 +2809,9 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
return alloc_type_array(elem, count, generic_type); return alloc_type_array(elem, count, generic_type);
} }
Type *soa_struct = nullptr; Type * soa_struct = nullptr;
Scope *scope = nullptr; Scope *scope = nullptr;
bool is_complete = false;
isize field_count = 0; isize field_count = 0;
i32 extra_field_count = 0; i32 extra_field_count = 0;
@@ -2707,39 +2820,43 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
case StructSoa_Slice: extra_field_count = 1; break; case StructSoa_Slice: extra_field_count = 1; break;
case StructSoa_Dynamic: extra_field_count = 3; break; case StructSoa_Dynamic: extra_field_count = 3; break;
} }
soa_struct = alloc_type_struct();
soa_struct->Struct.soa_kind = soa_kind;
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.is_polymorphic = is_polymorphic;
soa_struct->Struct.node = array_typ_expr;
if (count > I32_MAX) {
count = I32_MAX;
error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
}
soa_struct->Struct.soa_count = cast(i32)count;
scope = create_scope(ctx->info, ctx->scope);
soa_struct->Struct.scope = scope;
if (elem && elem->kind == Type_Named) {
add_declaration_dependency(ctx, elem->Named.type_name);
}
if (is_polymorphic) { if (is_polymorphic) {
field_count = 0; field_count = 0;
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.node = array_typ_expr;
soa_struct->Struct.soa_kind = soa_kind;
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0; soa_struct->Struct.soa_count = 0;
soa_struct->Struct.is_polymorphic = true;
scope = create_scope(ctx->info, ctx->scope); is_complete = true;
soa_struct->Struct.scope = scope;
} else if (is_type_array(elem)) { } else if (is_type_array(elem)) {
Type *old_array = base_type(elem); Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count; field_count = cast(isize)old_array->Array.count;
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.node = array_typ_expr;
soa_struct->Struct.soa_kind = soa_kind;
soa_struct->Struct.soa_elem = elem;
if (count > I32_MAX) {
count = I32_MAX;
error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
}
soa_struct->Struct.soa_count = cast(i32)count;
scope = create_scope(ctx->info, ctx->scope);
string_map_init(&scope->elements, 8); string_map_init(&scope->elements, 8);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = { String params_xyzw[4] = {
str_lit("x"), str_lit("x"),
@@ -2765,52 +2882,44 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
add_entity_use(ctx, nullptr, new_field); add_entity_use(ctx, nullptr, new_field);
} }
is_complete = true;
} else { } else {
GB_ASSERT(is_type_struct(elem)); GB_ASSERT(is_type_struct(elem));
Type *old_struct = base_type(elem); Type *old_struct = base_type(elem);
wait_signal_until_available(&old_struct->Struct.fields_wait_signal); if (old_struct->Struct.fields_wait_signal.futex.load()) {
field_count = old_struct->Struct.fields.count; field_count = old_struct->Struct.fields.count;
soa_struct = alloc_type_struct(); soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.node = array_typ_expr;
soa_struct->Struct.soa_kind = soa_kind;
soa_struct->Struct.soa_elem = elem;
if (count > I32_MAX) {
count = I32_MAX;
error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
}
soa_struct->Struct.soa_count = cast(i32)count;
scope = create_scope(ctx->info, old_struct->Struct.scope->parent); for_array(i, old_struct->Struct.fields) {
soa_struct->Struct.scope = scope; Entity *old_field = old_struct->Struct.fields[i];
if (old_field->kind == Entity_Variable) {
for_array(i, old_struct->Struct.fields) { Type *field_type = nullptr;
Entity *old_field = old_struct->Struct.fields[i]; if (soa_kind == StructSoa_Fixed) {
if (old_field->kind == Entity_Variable) { GB_ASSERT(count >= 0);
Type *field_type = nullptr; field_type = alloc_type_array(old_field->type, count);
if (soa_kind == StructSoa_Fixed) { } else {
GB_ASSERT(count >= 0); field_type = alloc_type_pointer(old_field->type);
field_type = alloc_type_array(old_field->type, count); }
Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
} else { } else {
field_type = alloc_type_pointer(old_field->type); soa_struct->Struct.fields[i] = old_field;
} }
Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
} else {
soa_struct->Struct.fields[i] = old_field;
}
soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
}
is_complete = true;
} }
} }
if (soa_kind != StructSoa_Fixed) { if (is_complete && soa_kind != StructSoa_Fixed) {
Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0); Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
soa_struct->Struct.fields[field_count+0] = len_field; soa_struct->Struct.fields[field_count+0] = len_field;
add_entity(ctx, scope, nullptr, len_field); add_entity(ctx, scope, nullptr, len_field);
@@ -2835,8 +2944,18 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved); Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(ctx, scope, nullptr, base_type_entity); add_entity(ctx, scope, nullptr, base_type_entity);
add_type_info_type(ctx, soa_struct); if (is_complete) {
wait_signal_set(&soa_struct->Struct.fields_wait_signal); add_type_info_type(ctx, soa_struct);
wait_signal_set(&soa_struct->Struct.fields_wait_signal);
} else {
SoaTypeWorkerData *wd = gb_alloc_item(permanent_allocator(), SoaTypeWorkerData);
wd->ctx = *ctx;
wd->type = soa_struct;
wd->wait_to_finish = true;
mpsc_enqueue(&ctx->checker->soa_types_to_complete, soa_struct);
thread_pool_add_task(complete_soa_type_worker, wd);
}
return soa_struct; return soa_struct;
} }
+48 -4
View File
@@ -1097,6 +1097,15 @@ gb_internal void init_universal(void) {
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
} }
{
int minimum_os_version = 0;
if (build_context.minimum_os_version_string != "") {
int major, minor, revision = 0;
sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
minimum_os_version = (major*10000)+(minor*100)+revision;
}
add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
}
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG); add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT); add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
@@ -1227,9 +1236,9 @@ gb_internal void init_universal(void) {
// intrinsics types for objective-c stuff // intrinsics types for objective-c stuff
{ {
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct()); t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct()); t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct()); t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object); t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector); t_objc_SEL = alloc_type_pointer(t_objc_selector);
@@ -1371,6 +1380,7 @@ gb_internal void init_checker(Checker *c) {
array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20); array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20);
mpsc_init(&c->global_untyped_queue, a); // , 1<<20); mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
c->builtin_ctx = make_checker_context(c); c->builtin_ctx = make_checker_context(c);
} }
@@ -1383,6 +1393,7 @@ gb_internal void destroy_checker(Checker *c) {
array_free(&c->nested_proc_lits); array_free(&c->nested_proc_lits);
array_free(&c->procs_to_check); array_free(&c->procs_to_check);
mpsc_destroy(&c->global_untyped_queue); mpsc_destroy(&c->global_untyped_queue);
mpsc_destroy(&c->soa_types_to_complete);
} }
@@ -1682,6 +1693,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden
} }
return true; return true;
} }
gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) {
if (scope == nullptr) {
return false;
}
if (!is_blank_ident(name)) {
Entity *ie = scope_insert(scope, entity);
if (ie != nullptr) {
return redeclaration_error(name, entity, ie);
}
}
if (identifier != nullptr) {
GB_ASSERT(entity->file != nullptr);
add_entity_definition(info, identifier, entity);
}
return true;
}
gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) { gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) {
return add_entity_with_name(c, scope, identifier, entity, entity->token.string); return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
} }
@@ -2006,6 +2037,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
break; break;
case Type_Struct: case Type_Struct:
if (bt->Struct.fields_wait_signal.futex.load() == 0)
return;
if (bt->Struct.scope != nullptr) { if (bt->Struct.scope != nullptr) {
for (auto const &entry : bt->Struct.scope->elements) { for (auto const &entry : bt->Struct.scope->elements) {
Entity *e = entry.value; Entity *e = entry.value;
@@ -2026,7 +2059,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->Struct.polymorphic_params); add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) { for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i]; Entity *f = bt->Struct.fields[i];
add_type_info_type_internal(c, f->type); if (f && f->type) {
add_type_info_type_internal(c, f->type);
}
} }
add_comparison_procedures_for_fields(c, bt); add_comparison_procedures_for_fields(c, bt);
break; break;
@@ -4440,6 +4475,10 @@ gb_internal void check_all_global_entities(Checker *c) {
DeclInfo *d = e->decl_info; DeclInfo *d = e->decl_info;
check_single_global_entity(c, e, d); check_single_global_entity(c, e, d);
if (e->type != nullptr && is_type_typed(e->type)) { if (e->type != nullptr && is_type_typed(e->type)) {
for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
complete_soa_type(c, t, false);
}
(void)type_size_of(e->type); (void)type_size_of(e->type);
(void)type_align_of(e->type); (void)type_align_of(e->type);
} }
@@ -6108,6 +6147,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) {
} }
gb_internal void check_merge_queues_into_arrays(Checker *c) { gb_internal void check_merge_queues_into_arrays(Checker *c) {
for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
complete_soa_type(c, t, false);
}
check_add_entities_from_queues(c); check_add_entities_from_queues(c);
check_add_definitions_from_queues(c); check_add_definitions_from_queues(c);
} }
@@ -6318,6 +6360,8 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check bodies have all been checked"); TIME_SECTION("check bodies have all been checked");
check_unchecked_bodies(c); check_unchecked_bodies(c);
TIME_SECTION("check #soa types");
check_merge_queues_into_arrays(c); check_merge_queues_into_arrays(c);
thread_pool_wait(); thread_pool_wait();
+1
View File
@@ -500,6 +500,7 @@ struct Checker {
MPSCQueue<UntypedExprInfo> global_untyped_queue; MPSCQueue<UntypedExprInfo> global_untyped_queue;
MPSCQueue<Type *> soa_types_to_complete;
}; };
+8 -3
View File
@@ -502,11 +502,16 @@ gb_internal i32 linker_stage(LinkerData *gen) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib "); platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
} }
if (build_context.minimum_os_version_string.len) { // Only specify this flag if the user has given a minimum version to target.
// This will cause warnings to show up for mismatched libraries.
if (build_context.minimum_os_version_string_given) {
link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string)); link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
} }
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main "); if (build_context.build_mode != BuildMode_DynamicLibrary) {
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main ");
}
} }
if (!build_context.no_crt) { if (!build_context.no_crt) {
+1 -1
View File
@@ -1343,7 +1343,7 @@ namespace lbAbiWasm {
// ignore padding // ignore padding
LLVMStructGetTypeAtIndex(type, 2) LLVMStructGetTypeAtIndex(type, 2)
}; };
LLVMTypeRef new_type = LLVMStructTypeInContext(c, types, gb_count_of(types), true); LLVMTypeRef new_type = LLVMStructTypeInContext(c, types, gb_count_of(types), false);
return lb_arg_type_direct(type, new_type, nullptr, nullptr); return lb_arg_type_direct(type, new_type, nullptr, nullptr);
} else { } else {
return is_struct(c, type, calling_convention); return is_struct(c, type, calling_convention);
+3 -3
View File
@@ -205,7 +205,7 @@ gb_internal LLVMTypeRef *lb_setup_modified_types_for_type_info(lbModule *m, isiz
stypes[1] = lb_type(m, tibt->Struct.fields[1]->type); stypes[1] = lb_type(m, tibt->Struct.fields[1]->type);
stypes[2] = lb_type(m, tibt->Struct.fields[2]->type); stypes[2] = lb_type(m, tibt->Struct.fields[2]->type);
isize variant_index = 0; isize variant_index = 0;
if (build_context.int_size == 8) { if (build_context.ptr_size == 8) {
stypes[3] = lb_type(m, t_i32); // padding stypes[3] = lb_type(m, t_i32); // padding
stypes[4] = lb_type(m, tibt->Struct.fields[3]->type); stypes[4] = lb_type(m, tibt->Struct.fields[3]->type);
variant_index = 5; variant_index = 5;
@@ -385,7 +385,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
small_const_values[2] = type_info_flags.value; small_const_values[2] = type_info_flags.value;
unsigned variant_index = 0; unsigned variant_index = 0;
if (build_context.int_size == 8) { if (build_context.ptr_size == 8) {
small_const_values[3] = LLVMConstNull(LLVMStructGetTypeAtIndex(stype, 3)); small_const_values[3] = LLVMConstNull(LLVMStructGetTypeAtIndex(stype, 3));
small_const_values[4] = id.value; small_const_values[4] = id.value;
variant_index = 5; variant_index = 5;
@@ -860,7 +860,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
Entity *f = t->Struct.fields[source_index]; Entity *f = t->Struct.fields[source_index];
i64 foffset = 0; i64 foffset = 0;
if (!t->Struct.is_raw_union) { if (!t->Struct.is_raw_union) {
GB_ASSERT(t->Struct.offsets != nullptr); GB_ASSERT_MSG(t->Struct.offsets != nullptr, "%s", type_to_string(t));
GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count); GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
foffset = t->Struct.offsets[source_index]; foffset = t->Struct.offsets[source_index];
} }
+2 -2
View File
@@ -124,7 +124,7 @@ gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbVa
gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) { gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t); x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t); y = lb_emit_conv(p, y, t);
bool use_llvm_intrinsic = is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))); bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
if (use_llvm_intrinsic) { if (use_llvm_intrinsic) {
LLVMValueRef args[2] = {x.value, y.value}; LLVMValueRef args[2] = {x.value, y.value};
LLVMTypeRef types[1] = {lb_type(p->module, t)}; LLVMTypeRef types[1] = {lb_type(p->module, t)};
@@ -140,7 +140,7 @@ gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) { gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t); x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t); y = lb_emit_conv(p, y, t);
bool use_llvm_intrinsic = is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))); bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
if (use_llvm_intrinsic) { if (use_llvm_intrinsic) {
LLVMValueRef args[2] = {x.value, y.value}; LLVMValueRef args[2] = {x.value, y.value};
LLVMTypeRef types[1] = {lb_type(p->module, t)}; LLVMTypeRef types[1] = {lb_type(p->module, t)};
+2 -1
View File
@@ -1066,6 +1066,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_MinimumOSVersion: { case BuildFlag_MinimumOSVersion: {
GB_ASSERT(value.kind == ExactValue_String); GB_ASSERT(value.kind == ExactValue_String);
build_context.minimum_os_version_string = value.value_string; build_context.minimum_os_version_string = value.value_string;
build_context.minimum_os_version_string_given = true;
break; break;
} }
case BuildFlag_RelocMode: { case BuildFlag_RelocMode: {
@@ -1926,7 +1927,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-minimum-os-version:<string>"); print_usage_line(1, "-minimum-os-version:<string>");
print_usage_line(2, "Sets the minimum OS version targeted by the application."); print_usage_line(2, "Sets the minimum OS version targeted by the application.");
print_usage_line(2, "Default: -minimum-os-version:11.0.0"); print_usage_line(2, "Default: -minimum-os-version:11.0.0");
print_usage_line(2, "(Only used when target is Darwin.)"); print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-extra-linker-flags:<string>"); print_usage_line(1, "-extra-linker-flags:<string>");
+6
View File
@@ -2885,6 +2885,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
elem = parse_expr(f, true); elem = parse_expr(f, true);
f->allow_range = prev_allow_range; f->allow_range = prev_allow_range;
if (elem == nullptr) {
syntax_error(token, "Expected a type or range, got nothing");
}
if (allow_token(f, Token_Semicolon)) { if (allow_token(f, Token_Semicolon)) {
underlying = parse_type(f); underlying = parse_type(f);
} else if (allow_token(f, Token_Comma)) { } else if (allow_token(f, Token_Comma)) {
@@ -2894,6 +2898,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
underlying = parse_type(f); underlying = parse_type(f);
} }
expect_token(f, Token_CloseBracket); expect_token(f, Token_CloseBracket);
return ast_bit_set_type(f, token, elem, underlying); return ast_bit_set_type(f, token, elem, underlying);
} }
@@ -5009,6 +5014,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
case Token_Xor: case Token_Xor:
case Token_Not: case Token_Not:
case Token_And: case Token_And:
case Token_Mul: // Used for error handling when people do C-like things
s = parse_simple_stmt(f, StmtAllowFlag_Label); s = parse_simple_stmt(f, StmtAllowFlag_Label);
expect_semicolon(f); expect_semicolon(f);
return s; return s;
+69
View File
@@ -758,6 +758,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
#elif defined(GB_SYSTEM_OSX) #elif defined(GB_SYSTEM_OSX)
#if __has_include(<os/os_sync_wait_on_address.h>)
#define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
#include <os/os_sync_wait_on_address.h>
#endif
#define UL_COMPARE_AND_WAIT 0x00000001 #define UL_COMPARE_AND_WAIT 0x00000001
#define ULF_NO_ERRNO 0x01000000 #define ULF_NO_ERRNO 0x01000000
@@ -765,6 +770,23 @@ extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value); extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
gb_internal void futex_signal(Futex *f) { gb_internal void futex_signal(Futex *f) {
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
if (__builtin_available(macOS 14.4, *)) {
for (;;) {
int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
if (ret >= 0) {
return;
}
if (errno == EINTR || errno == EFAULT) {
continue;
}
if (errno == ENOENT) {
return;
}
GB_PANIC("Failed in futex wake %d %d!\n", ret, errno);
}
} else {
#endif
for (;;) { for (;;) {
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0); int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
if (ret >= 0) { if (ret >= 0) {
@@ -778,9 +800,29 @@ gb_internal void futex_signal(Futex *f) {
} }
GB_PANIC("Failed in futex wake!\n"); GB_PANIC("Failed in futex wake!\n");
} }
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
}
#endif
} }
gb_internal void futex_broadcast(Futex *f) { gb_internal void futex_broadcast(Futex *f) {
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
if (__builtin_available(macOS 14.4, *)) {
for (;;) {
int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
if (ret >= 0) {
return;
}
if (errno == EINTR || errno == EFAULT) {
continue;
}
if (errno == ENOENT) {
return;
}
GB_PANIC("Failed in futext wake %d %d!\n", ret, errno);
}
} else {
#endif
for (;;) { for (;;) {
enum { ULF_WAKE_ALL = 0x00000100 }; enum { ULF_WAKE_ALL = 0x00000100 };
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0); int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
@@ -795,9 +837,32 @@ gb_internal void futex_broadcast(Futex *f) {
} }
GB_PANIC("Failed in futex wake!\n"); GB_PANIC("Failed in futex wake!\n");
} }
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
}
#endif
} }
gb_internal void futex_wait(Futex *f, Footex val) { gb_internal void futex_wait(Futex *f, Footex val) {
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
if (__builtin_available(macOS 14.4, *)) {
for (;;) {
int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_NONE);
if (ret >= 0) {
if (*f != val) {
return;
}
continue;
}
if (errno == EINTR || errno == EFAULT) {
continue;
}
if (errno == ENOENT) {
return;
}
GB_PANIC("Failed in futex wait %d %d!\n", ret, errno);
}
} else {
#endif
for (;;) { for (;;) {
int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0); int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
if (ret >= 0) { if (ret >= 0) {
@@ -815,7 +880,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
GB_PANIC("Failed in futex wait!\n"); GB_PANIC("Failed in futex wait!\n");
} }
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
}
#endif
} }
#elif defined(GB_SYSTEM_WINDOWS) #elif defined(GB_SYSTEM_WINDOWS)
gb_internal void futex_signal(Futex *f) { gb_internal void futex_signal(Futex *f) {
+10
View File
@@ -145,6 +145,7 @@ struct TypeStruct {
i32 soa_count; i32 soa_count;
StructSoaKind soa_kind; StructSoaKind soa_kind;
Wait_Signal fields_wait_signal; Wait_Signal fields_wait_signal;
BlockingMutex soa_mutex;
BlockingMutex offset_mutex; // for settings offsets BlockingMutex offset_mutex; // for settings offsets
bool is_polymorphic; bool is_polymorphic;
@@ -1060,6 +1061,13 @@ gb_internal Type *alloc_type_struct() {
return t; return t;
} }
gb_internal Type *alloc_type_struct_complete() {
Type *t = alloc_type(Type_Struct);
wait_signal_set(&t->Struct.fields_wait_signal);
return t;
}
gb_internal Type *alloc_type_union() { gb_internal Type *alloc_type_union() {
Type *t = alloc_type(Type_Union); Type *t = alloc_type(Type_Union);
return t; return t;
@@ -3771,6 +3779,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return 1; return 1;
} }
type_set_offsets(t);
i64 max = 1; i64 max = 1;
for_array(i, t->Struct.fields) { for_array(i, t->Struct.fields) {
Type *field_type = t->Struct.fields[i]->type; Type *field_type = t->Struct.fields[i]->type;