Browse Source

Merge branch 'odin-lang:master' into master

FrancisTheCat 1 year ago
parent
commit
29e4e85152

+ 1 - 1
LICENSE

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

+ 4 - 4
core/hash/crc.odin

@@ -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
core/hash/crc32.odin

@@ -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
core/hash/hash.odin

@@ -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
core/hash/mini.odin

@@ -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
core/hash/xxhash/streaming.odin

@@ -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
core/hash/xxhash/xxhash_32.odin

@@ -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 */
-   large_len:    XXH32_hash,    /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
-   v1:           XXH32_hash,    /*!< First accumulator lane */
-   v2:           XXH32_hash,    /*!< Second accumulator lane */
-   v3:           XXH32_hash,    /*!< Third accumulator lane */
-   v4:           XXH32_hash,    /*!< Fourth accumulator lane */
-   mem32:        [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
-   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. */
+	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) */
+	v1:           XXH32_hash,    /*!< First accumulator lane */
+	v2:           XXH32_hash,    /*!< Second accumulator lane */
+	v3:           XXH32_hash,    /*!< Third accumulator lane */
+	v4:           XXH32_hash,    /*!< Fourth accumulator lane */
+	mem32:        [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
+	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. */
 }
 }
 
 
 XXH32_canonical :: struct {
 XXH32_canonical :: struct {

+ 62 - 2
core/sync/futex_darwin.odin

@@ -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 {
+	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
 	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 - 0
core/sys/darwin/darwin.odin

@@ -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 - 0
core/sys/darwin/sync.odin

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

+ 9 - 0
src/checker.cpp

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

+ 4 - 2
src/linker.cpp

@@ -508,8 +508,10 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 					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) {

+ 6 - 0
src/parser.cpp

@@ -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 - 0
src/threading.cpp

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