Browse Source

Merge branch 'master' into netbsd

Andreas T Jonsson 1 year ago
parent
commit
b72c2edabb

+ 3 - 1
.gitignore

@@ -322,4 +322,6 @@ build.sh
 !core/debug/
 !core/debug/
 
 
 # RAD debugger project file
 # RAD debugger project file
-*.raddbg
+*.raddbg
+
+misc/featuregen/featuregen

+ 20 - 11
base/intrinsics/intrinsics.odin

@@ -169,15 +169,18 @@ type_has_nil :: proc($T: typeid) -> bool ---
 
 
 type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
 type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
 
 
-type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
-type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
-type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
-type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
-type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
-type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
-type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
-
-type_has_field :: proc($T: typeid, $name: string) -> bool ---
+type_is_variant_of        :: proc($U, $V: typeid)          -> bool    where type_is_union(U) ---
+type_union_tag_type       :: proc($T: typeid)              -> typeid  where type_is_union(T) ---
+type_union_tag_offset     :: proc($T: typeid)              -> uintptr where type_is_union(T) ---
+type_union_base_tag_value :: proc($T: typeid)              -> int     where type_is_union(U) ---
+type_union_variant_count  :: proc($T: typeid)              -> int     where type_is_union(T) ---
+type_variant_type_of      :: proc($T: typeid, $index: int) -> typeid  where type_is_union(T) ---
+type_variant_index_of     :: proc($U, $V: typeid)          -> int     where type_is_union(U) ---
+
+type_bit_set_elem_type       :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
+type_bit_set_underlying_type :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
+
+type_has_field  :: proc($T: typeid, $name: string) -> bool ---
 type_field_type :: proc($T: typeid, $name: string) -> typeid ---
 type_field_type :: proc($T: typeid, $name: string) -> typeid ---
 
 
 type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
 type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
@@ -282,6 +285,12 @@ simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
 simd_rotate_left  :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
 simd_rotate_left  :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
 simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
 simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
 
 
+// Checks if the current target supports the given target features.
+//
+// Takes a constant comma-seperated string (eg: "sha512,sse4.1"), or a procedure type which has either
+// `@(require_target_feature)` or `@(enable_target_feature)` as its input and returns a boolean indicating
+// if all listed features are supported.
+has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
 
 
 // WASM targets only
 // WASM targets only
 wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
 wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
@@ -293,9 +302,9 @@ wasm_memory_size :: proc(index: uintptr)        -> int ---
 // 0 - indicates that the thread blocked and then was woken up
 // 0 - indicates that the thread blocked and then was woken up
 // 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
 // 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
 // 2 - the thread blocked, but the timeout
 // 2 - the thread blocked, but the timeout
-@(enable_target_feature="atomics")
+@(require_target_feature="atomics")
 wasm_memory_atomic_wait32   :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
 wasm_memory_atomic_wait32   :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
-@(enable_target_feature="atomics")
+@(require_target_feature="atomics")
 wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
 wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
 
 
 // x86 Targets (i386, amd64)
 // x86 Targets (i386, amd64)

+ 9 - 7
build_odin.sh

@@ -2,7 +2,6 @@
 set -eu
 set -eu
 
 
 : ${CPPFLAGS=}
 : ${CPPFLAGS=}
-: ${CXX=clang++}
 : ${CXXFLAGS=}
 : ${CXXFLAGS=}
 : ${LDFLAGS=}
 : ${LDFLAGS=}
 : ${LLVM_CONFIG=}
 : ${LLVM_CONFIG=}
@@ -26,12 +25,14 @@ error() {
 
 
 if [ -z "$LLVM_CONFIG" ]; then
 if [ -z "$LLVM_CONFIG" ]; then
 	# darwin, linux, openbsd
 	# darwin, linux, openbsd
-	if   [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
+	if   [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
+	elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
 	elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
 	elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
 	elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
 	elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
 	elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
 	elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
 	elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
 	elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
 	# freebsd
 	# freebsd
+	elif [ -n "$(command -v llvm-config18)" ]; then  LLVM_CONFIG="llvm-config18"
 	elif [ -n "$(command -v llvm-config17)" ]; then  LLVM_CONFIG="llvm-config17"
 	elif [ -n "$(command -v llvm-config17)" ]; then  LLVM_CONFIG="llvm-config17"
 	elif [ -n "$(command -v llvm-config14)" ]; then  LLVM_CONFIG="llvm-config14"
 	elif [ -n "$(command -v llvm-config14)" ]; then  LLVM_CONFIG="llvm-config14"
 	elif [ -n "$(command -v llvm-config13)" ]; then  LLVM_CONFIG="llvm-config13"
 	elif [ -n "$(command -v llvm-config13)" ]; then  LLVM_CONFIG="llvm-config13"
@@ -44,21 +45,22 @@ if [ -z "$LLVM_CONFIG" ]; then
 	fi
 	fi
 fi
 fi
 
 
+: ${CXX=$($LLVM_CONFIG --bindir)/clang++}
+
 LLVM_VERSION="$($LLVM_CONFIG --version)"
 LLVM_VERSION="$($LLVM_CONFIG --version)"
 LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
 LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
 LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
 LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
 LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
 LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
 
 
-if [ $LLVM_VERSION_MAJOR -lt 11 ] ||
-	([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]); then
-	error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14 or 17"
+if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 18 ]; then
+	error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17 or 18"
 fi
 fi
 
 
 case "$OS_NAME" in
 case "$OS_NAME" in
 Darwin)
 Darwin)
 	if [ "$OS_ARCH" = "arm64" ]; then
 	if [ "$OS_ARCH" = "arm64" ]; then
-		if [ $LLVM_VERSION_MAJOR -lt 13 ] || [ $LLVM_VERSION_MAJOR -gt 17 ]; then
-			error "Darwin Arm64 requires LLVM 13, 14 or 17"
+		if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
+			error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17 or 18"
 		fi
 		fi
 	fi
 	fi
 
 

+ 1 - 1
core/math/big/helpers.odin

@@ -356,7 +356,7 @@ int_count_lsb :: proc(a: ^Int, allocator := context.allocator) -> (count: int, e
 }
 }
 
 
 platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
 platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
-	where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
+	where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
 	return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
 	return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
 }
 }
 
 

+ 2 - 2
core/math/big/internal.odin

@@ -546,7 +546,7 @@ internal_int_shl1 :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
  	Like `internal_int_mul_digit` but with an integer as the small input.
  	Like `internal_int_mul_digit` but with an integer as the small input.
 */
 */
 internal_int_mul_integer :: proc(dest, a: ^Int, b: $T, allocator := context.allocator) -> (err: Error)
 internal_int_mul_integer :: proc(dest, a: ^Int, b: $T, allocator := context.allocator) -> (err: Error)
-where intrinsics.type_is_integer(T) && T != DIGIT {
+where intrinsics.type_is_integer(T), T != DIGIT {
 	context.allocator = allocator
 	context.allocator = allocator
 
 
 	t := &Int{}
 	t := &Int{}
@@ -2806,7 +2806,7 @@ internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
 }
 }
 
 
 internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
 internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
-	where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
+	where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
 	return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
 	return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
 }
 }
 
 

+ 2 - 2
core/math/big/radix.odin

@@ -469,7 +469,7 @@ internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed
 	Assumes `a` not to be `nil` and to have been initialized.
 	Assumes `a` not to be `nil` and to have been initialized.
 */
 */
 internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
 internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
-                     where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
+                     where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T), size_of(T) <= 16 {
 
 
 	assert(nails >= 0 && nails < (size_of(T) * 8))
 	assert(nails >= 0 && nails < (size_of(T) * 8))
 
 
@@ -505,7 +505,7 @@ internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_Fir
 
 
 
 
 internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
 internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
-                     where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
+                     where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T), size_of(T) <= 16 {
 	assert(nails >= 0 && nails < (size_of(T) * 8))
 	assert(nails >= 0 && nails < (size_of(T) * 8))
 	context.allocator = allocator
 	context.allocator = allocator
 
 

+ 1 - 1
core/os/os_freebsd.odin

@@ -159,7 +159,7 @@ blkcnt_t :: i64
 blksize_t :: i32
 blksize_t :: i32
 fflags_t :: u32
 fflags_t :: u32
 
 
-when ODIN_ARCH == .amd64 /* LP64 */ {
+when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 /* LP64 */ {
 	time_t :: i64
 	time_t :: i64
 } else {
 } else {
 	time_t :: i32
 	time_t :: i32

+ 2 - 2
core/simd/x86/sse3.odin

@@ -36,7 +36,7 @@ _mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
 _mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
 _mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
 	return simd.shuffle(a, a, 0, 0)
 	return simd.shuffle(a, a, 0, 0)
 }
 }
-@(require_results, enable_target_feature="sse3")
+@(require_results, enable_target_feature="sse2,sse3")
 _mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
 _mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
 	return _mm_load1_pd(mem_addr)
 	return _mm_load1_pd(mem_addr)
 }
 }
@@ -65,4 +65,4 @@ foreign _ {
 	hsubps :: proc(a, b: __m128) -> __m128 ---
 	hsubps :: proc(a, b: __m128) -> __m128 ---
 	@(link_name = "llvm.x86.sse3.ldu.dq")
 	@(link_name = "llvm.x86.sse3.ldu.dq")
 	lddqu :: proc(mem_addr: rawptr) -> i8x16 ---
 	lddqu :: proc(mem_addr: rawptr) -> i8x16 ---
-}
+}

+ 2 - 2
core/simd/x86/sse41.odin

@@ -268,7 +268,7 @@ _mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
 _mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
 _mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
 	return _mm_testz_si128(a, mask)
 	return _mm_testz_si128(a, mask)
 }
 }
-@(require_results, enable_target_feature="sse4.1")
+@(require_results, enable_target_feature="sse2,sse4.1")
 _mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
 _mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
 	return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
 	return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
 }
 }
@@ -349,4 +349,4 @@ foreign _ {
 	ptestc     :: proc(a, mask: i64x2) -> i32 ---
 	ptestc     :: proc(a, mask: i64x2) -> i32 ---
 	@(link_name = "llvm.x86.sse41.ptestnzc")
 	@(link_name = "llvm.x86.sse41.ptestnzc")
 	ptestnzc   :: proc(a, mask: i64x2) -> i32 ---
 	ptestnzc   :: proc(a, mask: i64x2) -> i32 ---
-}
+}

+ 36 - 0
core/slice/slice.odin

@@ -701,3 +701,39 @@ enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
 	where intrinsics.type_is_enumerated_array(T) {
 	where intrinsics.type_is_enumerated_array(T) {
 	return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
 	return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
 }
 }
+
+// Turn a `[]E` into `bit_set[E]`
+// e.g.:
+//    bs := slice.enum_slice_to_bitset(my_flag_slice, rl.ConfigFlags)
+@(require_results)
+enum_slice_to_bitset :: proc(enums: []$E, $T: typeid/bit_set[E]) -> (bits: T) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
+	for v in enums {
+		bits |= {v}
+	}
+	return
+}
+
+// Turn a `bit_set[E]` into a `[]E`
+// e.g.:
+//    sl := slice.bitset_to_enum_slice(flag_buf[:], bs)
+@(require_results)
+bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
+	count := 0
+	for v in bs {
+		buf[count] = v
+		count += 1
+	}
+	return buf[:count]
+}
+
+// Turn a `bit_set[E]` into a `[]E`, allocates
+// e.g.:
+//    sl := slice.bitset_to_enum_slice(bs)
+@(require_results)
+bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
+	ones := intrinsics.count_ones(transmute(E)bs)
+	buf  := make([]E, int(ones), allocator)
+	return bitset_to_enum_slice(buf, bs)
+}
+
+bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}

+ 1 - 1
core/sync/extended.odin

@@ -433,7 +433,7 @@ One_Shot_Event :: struct #no_copy {
 // Blocks the current thread until the event is made available with `one_shot_event_signal`.
 // Blocks the current thread until the event is made available with `one_shot_event_signal`.
 one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
 one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
 	for atomic_load_explicit(&e.state, .Acquire) == 0 {
 	for atomic_load_explicit(&e.state, .Acquire) == 0 {
-		futex_wait(&e.state, 1)
+		futex_wait(&e.state, 0)
 	}
 	}
 }
 }
 
 

+ 31 - 13
core/sync/futex_wasm.odin

@@ -5,31 +5,49 @@ package sync
 import "base:intrinsics"
 import "base:intrinsics"
 import "core:time"
 import "core:time"
 
 
+// NOTE: because `core:sync` is in the dependency chain of a lot of the core packages (mostly through `core:mem`)
+// without actually calling into it much, I opted for a runtime panic instead of a compile error here.
+
 _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
 _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
-	s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
-	return s != 0
+	when !intrinsics.has_target_feature("atomics") {
+		_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
+	} else {
+		s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
+		return s != 0
+	}
 }
 }
 
 
 _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 {
-	s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
-	return s != 0
-
+	when !intrinsics.has_target_feature("atomics") {
+		_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
+	} else {
+		s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
+		return s != 0
+	}
 }
 }
 
 
 _futex_signal :: proc "contextless" (f: ^Futex) {
 _futex_signal :: proc "contextless" (f: ^Futex) {
-	loop: for {
-		s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
-		if s >= 1 {
-			return
+	when !intrinsics.has_target_feature("atomics") {
+		_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
+	} else {
+		loop: for {
+			s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
+			if s >= 1 {
+				return
+			}
 		}
 		}
 	}
 	}
 }
 }
 
 
 _futex_broadcast :: proc "contextless" (f: ^Futex) {
 _futex_broadcast :: proc "contextless" (f: ^Futex) {
-	loop: for {
-		s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
-		if s >= 0 {
-			return
+	when !intrinsics.has_target_feature("atomics") {
+		_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
+	} else {
+		loop: for {
+			s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
+			if s >= 0 {
+				return
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 2 - 2
core/sys/darwin/sync.odin

@@ -5,9 +5,9 @@ foreign import system "system:System.framework"
 // #define OS_WAIT_ON_ADDR_AVAILABILITY \
 // #define OS_WAIT_ON_ADDR_AVAILABILITY \
 // 	__API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
 // 	__API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4))
 when ODIN_OS == .Darwin {
 when ODIN_OS == .Darwin {
-	when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION > 17_04_00 {
+	when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 17_04_00 {
 		WAIT_ON_ADDRESS_AVAILABLE :: true
 		WAIT_ON_ADDRESS_AVAILABLE :: true
-	} else when ODIN_MINIMUM_OS_VERSION > 14_04_00 {
+	} else when ODIN_MINIMUM_OS_VERSION >= 14_04_00 {
 		WAIT_ON_ADDRESS_AVAILABLE :: true
 		WAIT_ON_ADDRESS_AVAILABLE :: true
 	} else {
 	} else {
 		WAIT_ON_ADDRESS_AVAILABLE :: false
 		WAIT_ON_ADDRESS_AVAILABLE :: false

+ 35 - 16
core/sys/linux/bits.odin

@@ -153,22 +153,41 @@ Errno :: enum i32 {
 Open_Flags_Bits :: enum {
 Open_Flags_Bits :: enum {
 	WRONLY    = 0,
 	WRONLY    = 0,
 	RDWR      = 1,
 	RDWR      = 1,
-	CREAT     = 8,
-	EXCL      = 9,
-	NOCTTY    = 10,
-	TRUNC     = 11,
-	APPEND    = 12,
-	NONBLOCK  = 14,
-	DSYNC     = 16,
-	ASYNC     = 17,
-	DIRECT    = 18,
-	LARGEFILE = 20,
-	DIRECTORY = 21,
-	NOFOLLOW  = 22,
-	NOATIME   = 24,
-	CLOEXEC   = 25,
-	PATH      = 28,
-}
+	CREAT     = 6,
+	EXCL      = 7,
+	NOCTTY    = 8,
+	TRUNC     = 9,
+	APPEND    = 10,
+	NONBLOCK  = 11,
+	DSYNC     = 12,
+	ASYNC     = 13,
+	DIRECT    = 14,
+	LARGEFILE = 15,
+	DIRECTORY = 16,
+	NOFOLLOW  = 17,
+	NOATIME   = 18,
+	CLOEXEC   = 19,
+	PATH      = 21,
+}
+
+// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
+#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
+#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
+#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
+#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
+#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
+#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
+#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
+#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
+#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
+#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
+#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
+#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
+#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
+#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
 
 
 /*
 /*
 	Bits for FD_Flags bitset
 	Bits for FD_Flags bitset

+ 15 - 15
core/sys/linux/helpers.odin

@@ -26,7 +26,7 @@ where
 @(private)
 @(private)
 syscall2 :: #force_inline proc "contextless" (nr: uintptr,p1: $T1, p2: $T2) -> int
 syscall2 :: #force_inline proc "contextless" (nr: uintptr,p1: $T1, p2: $T2) -> int
 where
 where
-	size_of(p1) <= size_of(uintptr) &&
+	size_of(p1) <= size_of(uintptr),
 	size_of(p2) <= size_of(uintptr) 
 	size_of(p2) <= size_of(uintptr) 
 {
 {
 	return cast(int) intrinsics.syscall(nr,
 	return cast(int) intrinsics.syscall(nr,
@@ -36,8 +36,8 @@ where
 @(private)
 @(private)
 syscall3 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3) -> int
 syscall3 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3) -> int
 where
 where
-	size_of(p1) <= size_of(uintptr) &&
-	size_of(p2) <= size_of(uintptr) &&
+	size_of(p1) <= size_of(uintptr),
+	size_of(p2) <= size_of(uintptr),
 	size_of(p3) <= size_of(uintptr)
 	size_of(p3) <= size_of(uintptr)
 {
 {
 	return cast(int) intrinsics.syscall(nr,
 	return cast(int) intrinsics.syscall(nr,
@@ -49,9 +49,9 @@ where
 @(private)
 @(private)
 syscall4 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4) -> int
 syscall4 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4) -> int
 where
 where
-	size_of(p1) <= size_of(uintptr) &&
-	size_of(p2) <= size_of(uintptr) &&
-	size_of(p3) <= size_of(uintptr) &&
+	size_of(p1) <= size_of(uintptr),
+	size_of(p2) <= size_of(uintptr),
+	size_of(p3) <= size_of(uintptr),
 	size_of(p4) <= size_of(uintptr)
 	size_of(p4) <= size_of(uintptr)
 {
 {
 	return cast(int) intrinsics.syscall(nr,
 	return cast(int) intrinsics.syscall(nr,
@@ -64,10 +64,10 @@ where
 @(private)
 @(private)
 syscall5 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4, p5: $T5) -> int
 syscall5 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4, p5: $T5) -> int
 where
 where
-	size_of(p1) <= size_of(uintptr) &&
-	size_of(p2) <= size_of(uintptr) &&
-	size_of(p3) <= size_of(uintptr) &&
-	size_of(p4) <= size_of(uintptr) &&
+	size_of(p1) <= size_of(uintptr),
+	size_of(p2) <= size_of(uintptr),
+	size_of(p3) <= size_of(uintptr),
+	size_of(p4) <= size_of(uintptr),
 	size_of(p5) <= size_of(uintptr)
 	size_of(p5) <= size_of(uintptr)
 {
 {
 	return cast(int) intrinsics.syscall(nr,
 	return cast(int) intrinsics.syscall(nr,
@@ -81,11 +81,11 @@ where
 @(private)
 @(private)
 syscall6 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4, p5: $T5, p6: $T6) -> int
 syscall6 :: #force_inline proc "contextless" (nr: uintptr, p1: $T1, p2: $T2, p3: $T3, p4: $T4, p5: $T5, p6: $T6) -> int
 where
 where
-	size_of(p1) <= size_of(uintptr) &&
-	size_of(p2) <= size_of(uintptr) &&
-	size_of(p3) <= size_of(uintptr) &&
-	size_of(p4) <= size_of(uintptr) &&
-	size_of(p5) <= size_of(uintptr) &&
+	size_of(p1) <= size_of(uintptr),
+	size_of(p2) <= size_of(uintptr),
+	size_of(p3) <= size_of(uintptr),
+	size_of(p4) <= size_of(uintptr),
+	size_of(p5) <= size_of(uintptr),
 	size_of(p6) <= size_of(uintptr)
 	size_of(p6) <= size_of(uintptr)
 {
 {
 	return cast(int) intrinsics.syscall(nr,
 	return cast(int) intrinsics.syscall(nr,

+ 1 - 1
core/sys/linux/sys.odin

@@ -2314,7 +2314,7 @@ futex :: proc {
 */
 */
 epoll_create :: proc(size: i32 = 1) -> (Fd, Errno) {
 epoll_create :: proc(size: i32 = 1) -> (Fd, Errno) {
 	when ODIN_ARCH != .arm64 {
 	when ODIN_ARCH != .arm64 {
-		ret := syscall(SYS_epoll_create)
+		ret := syscall(SYS_epoll_create, i32(1))
 		return errno_unwrap(ret, Fd)
 		return errno_unwrap(ret, Fd)
 	} else {
 	} else {
 		ret := syscall(SYS_epoll_create1, i32(0))
 		ret := syscall(SYS_epoll_create1, i32(0))

+ 2 - 2
core/thread/thread_unix.odin

@@ -25,7 +25,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 
 
 		when ODIN_OS != .Darwin {
 		when ODIN_OS != .Darwin {
 			// We need to give the thread a moment to start up before we enable cancellation.
 			// We need to give the thread a moment to start up before we enable cancellation.
-			can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) == 0
+			can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0
 		}
 		}
 
 
 		sync.lock(&t.mutex)
 		sync.lock(&t.mutex)
@@ -40,7 +40,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 			// Enable thread's cancelability.
 			// Enable thread's cancelability.
 			if can_set_thread_cancel_state {
 			if can_set_thread_cancel_state {
 				unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
 				unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
-				unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE,      nil)
+				unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE,       nil)
 			}
 			}
 		}
 		}
 
 

+ 1 - 1
examples/demo/demo.odin

@@ -48,7 +48,7 @@ the_basics :: proc() {
 		// os.args holds the path to the current executable and any arguments passed to it.
 		// os.args holds the path to the current executable and any arguments passed to it.
 		if len(os.args) == 1 {
 		if len(os.args) == 1 {
 			fmt.printf("Hellope from %v.\n", os.args[0])
 			fmt.printf("Hellope from %v.\n", os.args[0])
-		} else {
+		} else if len(os.args) > 2 {
 			fmt.printf("%v, %v! from %v.\n", os.args[1], os.args[2], os.args[0])
 			fmt.printf("%v, %v! from %v.\n", os.args[1], os.args[2], os.args[0])
 		}
 		}
 
 

+ 28 - 0
misc/featuregen/README.md

@@ -0,0 +1,28 @@
+# Featuregen
+
+This directory contains a python and CPP script that generates the needed information
+for features regarding microarchitecture and target features of the compiler.
+
+It is not pretty! But LLVM has no way to query this information with their C API.
+
+It generates these globals (intended for `src/build_settings.cpp`:
+
+- `target_microarch_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of microarchitectures available on that architecture
+- `target_features_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of target features available on that architecture
+- `target_microarch_counts`: an array of ints indexed by the architecture, each int represents the amount of microarchitectures available on that target, intended for easier iteration of the next global
+- `microarch_features_list`: an array of a tuple like struct where the first string is a microarchitecture and the second is a comma-seperated list of all features that are enabled by default for it
+
+In order to get the default features for a microarchitecture there is a small CPP program that takes
+a target triple and microarchitecture and spits out the default features, this is then parsed by the python script.
+
+This should be ran each time we update LLVM to stay in sync.
+
+If there are minor differences (like the Odin user using LLVM 14 and this table being generated on LLVM 17) it
+does not impact much at all, the only thing it will do is make LLVM print a message that the feature is ignored (if it was added between 14 and 17 in this case).
+
+## Usage
+
+1. Make sure the table of architectures at the top of the python script is up-to-date (the triple can be any valid triple for the architecture)
+1. `./build.sh`
+1. `python3 featuregen.py`
+1. Copy the output into `src/build_settings.cpp`

+ 37 - 0
misc/featuregen/featuregen.cpp

@@ -0,0 +1,37 @@
+#include <llvm/MC/MCSubtargetInfo.h>
+#include <llvm/MC/TargetRegistry.h>
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/ADT/ArrayRef.h>
+#include <llvm/Support/InitLLVM.h>
+#include <llvm/Support/TargetSelect.h>
+
+// Dumps the default set of supported features for the given microarch.
+int main(int argc, char **argv) {
+	if (argc < 3) {
+		llvm::errs() << "Error: first arg should be triple, second should be microarch\n";
+		return 1;
+	}
+
+	llvm::InitializeAllTargets();
+	llvm::InitializeAllTargetMCs();
+
+	std::string error;
+	const llvm::Target* target = llvm::TargetRegistry::lookupTarget(argv[1], error);
+
+	if (!target) {
+		llvm::errs() << "Error: " << error << "\n";
+		return 1;
+	}
+
+	auto STI = target->createMCSubtargetInfo(argv[1], argv[2], "");
+
+	std::string plus = "+";
+	llvm::ArrayRef<llvm::SubtargetFeatureKV> features = STI->getAllProcessorFeatures();
+	for (const auto& feature : features) {
+		if (STI->checkFeatures(plus + feature.Key)) {
+			llvm::outs() << feature.Key << "\n";
+		}
+	}
+
+	return 0;
+}

+ 116 - 0
misc/featuregen/featuregen.py

@@ -0,0 +1,116 @@
+import subprocess
+import tempfile
+import os
+import sys
+
+archs = [
+	("amd64",     "linux_amd64", "x86_64-pc-linux-gnu", [], []),
+	("i386",      "linux_i386",  "i386-pc-linux-gnu",   [], []),
+	("arm32",     "linux_arm32", "arm-linux-gnu",       [], []),
+	("arm64",     "linux_arm64", "aarch64-linux-elf",   [], []),
+	("wasm32",    "js_wasm32",   "wasm32-js-js",        [], []),
+	("wasm64p32", "js_wasm64p32","wasm32-js-js",        [], []),
+];
+
+SEEKING_CPUS     = 0
+PARSING_CPUS     = 1
+PARSING_FEATURES = 2
+
+with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
+	temp_file.write(b"package main\n")
+
+	for arch, target, triple, cpus, features in archs:
+		cmd = ["odin", "build", temp_file.name, "-file", "-build-mode:llvm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
+		process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
+
+		state = SEEKING_CPUS
+		for line in process.stderr:
+
+			if state == SEEKING_CPUS:
+				if line == "Available CPUs for this target:\n":
+					state = PARSING_CPUS
+			
+			elif state == PARSING_CPUS:
+				if line == "Available features for this target:\n":
+					state = PARSING_FEATURES
+					continue
+			
+				parts = line.split(" -", maxsplit=1)
+				if len(parts) < 2:
+					continue
+
+				cpu = parts[0].strip()
+				cpus.append(cpu)
+
+			elif state == PARSING_FEATURES:
+				if line == "\n" and len(features) > 0:
+					break
+
+				parts = line.split(" -", maxsplit=1)
+				if len(parts) < 2:
+					continue
+
+				feature = parts[0].strip()
+				features.append(feature)
+
+		process.wait()
+		if process.returncode != 0:
+			print(f"odin build returned with non-zero exit code {process.returncode}")
+			sys.exit(1)
+
+		os.remove("temp.ll")
+
+def print_default_features(triple, microarch):
+	cmd = ["./featuregen", triple, microarch]
+	process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
+	first = True
+	for line in process.stdout:
+		print("" if first else ",", line.strip(), sep="", end="")
+		first = False
+	process.wait()
+	if process.returncode != 0:
+		print(f"featuregen returned with non-zero exit code {process.returncode}")
+		sys.exit(1)
+
+print("// Generated with the featuregen script in `misc/featuregen`")
+print("gb_global String target_microarch_list[TargetArch_COUNT] = {")
+print("\t// TargetArch_Invalid:")
+print('\tstr_lit(""),')
+for arch, target, triple, cpus, features in archs:
+	print(f"\t// TargetArch_{arch}:")
+	print(f'\tstr_lit("{','.join(cpus)}"),')
+print("};")
+
+print("")
+
+print("// Generated with the featuregen script in `misc/featuregen`")
+print("gb_global String target_features_list[TargetArch_COUNT] = {")
+print("\t// TargetArch_Invalid:")
+print('\tstr_lit(""),')
+for arch, target, triple, cpus, features in archs:
+	print(f"\t// TargetArch_{arch}:")
+	print(f'\tstr_lit("{','.join(features)}"),')
+print("};")
+
+print("")
+
+print("// Generated with the featuregen script in `misc/featuregen`")
+print("gb_global int target_microarch_counts[TargetArch_COUNT] = {")
+print("\t// TargetArch_Invalid:")
+print("\t0,")
+for arch, target, triple, cpus, feature in archs:
+	print(f"\t// TargetArch_{arch}:")
+	print(f"\t{len(cpus)},")
+print("};")
+
+print("")
+
+print("// Generated with the featuregen script in `misc/featuregen`")
+print("gb_global MicroarchFeatureList microarch_features_list[] = {")
+for arch, target, triple, cpus, features in archs:
+	print(f"\t// TargetArch_{arch}:")
+	for cpu in cpus:
+		print(f'\t{{ str_lit("{cpu}"), str_lit("', end="")
+		print_default_features(triple, cpu)
+		print('") },')
+print("};")

File diff suppressed because it is too large
+ 33 - 14
src/build_settings.cpp


+ 99 - 2
src/check_builtin.cpp

@@ -1719,6 +1719,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 	case BuiltinProc_objc_register_selector: 
 	case BuiltinProc_objc_register_selector: 
 	case BuiltinProc_objc_register_class: 
 	case BuiltinProc_objc_register_class: 
 	case BuiltinProc_atomic_type_is_lock_free:
 	case BuiltinProc_atomic_type_is_lock_free:
+	case BuiltinProc_has_target_feature:
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		break;
 		break;
 
 
@@ -2022,6 +2023,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		
 		
 		Selection sel = lookup_field(type, field_name, false);
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
 		if (sel.entity == nullptr) {
+			ERROR_BLOCK();
 			gbString type_str = type_to_string_shorthand(type);
 			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -2095,6 +2097,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		
 		
 		Selection sel = lookup_field(type, field_name, false);
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
 		if (sel.entity == nullptr) {
+			ERROR_BLOCK();
 			gbString type_str = type_to_string_shorthand(type);
 			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -3663,6 +3666,41 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		break;
 		break;
 	}
 	}
 
 
+	case BuiltinProc_has_target_feature: {
+		String features = str_lit("");
+
+		check_expr_or_type(c, operand, ce->args[0]);
+
+		if (is_type_string(operand->type) && operand->mode == Addressing_Constant) {
+			GB_ASSERT(operand->value.kind == ExactValue_String);
+			features = operand->value.value_string;
+		} else {
+			Type *pt = base_type(operand->type);
+			if (pt->kind == Type_Proc) {
+				if (pt->Proc.require_target_feature.len != 0) {
+					GB_ASSERT(pt->Proc.enable_target_feature.len == 0);
+					features = pt->Proc.require_target_feature;
+				} else if (pt->Proc.enable_target_feature.len != 0) {
+					features = pt->Proc.enable_target_feature;
+				} else {
+					error(ce->args[0], "Expected the procedure type given to '%.*s' to have @(require_target_feature=\"...\") or @(enable_target_feature=\"...\")", LIT(builtin_name));
+				}
+			} else {
+				error(ce->args[0], "Expected a constant string or procedure type for '%.*s'", LIT(builtin_name));
+			}
+		}
+
+		String invalid;
+		if (!check_target_feature_is_valid_globally(features, &invalid)) {
+			error(ce->args[0], "Target feature '%.*s' is not a valid target feature", LIT(invalid));
+		}
+
+		operand->value = exact_value_bool(check_target_feature_is_enabled(features, nullptr));
+		operand->mode = Addressing_Constant;
+		operand->type = t_untyped_bool;
+		break;
+	}
+
 	case BuiltinProc_soa_struct: {
 	case BuiltinProc_soa_struct: {
 		Operand x = {};
 		Operand x = {};
 		Operand y = {};
 		Operand y = {};
@@ -5395,6 +5433,58 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 			operand->value = exact_value_i64(u->Union.kind == UnionType_no_nil ? 0 : 1);
 			operand->value = exact_value_i64(u->Union.kind == UnionType_no_nil ? 0 : 1);
 		} break;
 		} break;
 
 
+	case BuiltinProc_type_bit_set_elem_type:
+		{
+
+			if (operand->mode != Addressing_Type) {
+				error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			Type *bs = operand->type;
+
+			if (!is_type_bit_set(bs)) {
+				error(operand->expr, "Expected a bit_set type for '%.*s'", LIT(builtin_name));
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			bs = base_type(bs);
+			GB_ASSERT(bs->kind == Type_BitSet);
+
+			operand->mode = Addressing_Type;
+			operand->type = bs->BitSet.elem;
+		} break;
+
+	case BuiltinProc_type_bit_set_underlying_type:
+		{
+
+			if (operand->mode != Addressing_Type) {
+				error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			Type *bs = operand->type;
+
+			if (!is_type_bit_set(bs)) {
+				error(operand->expr, "Expected a bit_set type for '%.*s'", LIT(builtin_name));
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
+				return false;
+			}
+
+			bs = base_type(bs);
+			GB_ASSERT(bs->kind == Type_BitSet);
+
+			operand->mode = Addressing_Type;
+			operand->type = bit_set_to_int(bs);
+		} break;
+
 	case BuiltinProc_type_union_variant_count:
 	case BuiltinProc_type_union_variant_count:
 		{
 		{
 			if (operand->mode != Addressing_Type) {
 			if (operand->mode != Addressing_Type) {
@@ -5801,6 +5891,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 
 
 			Selection sel = lookup_field(type, field_name, false);
 			Selection sel = lookup_field(type, field_name, false);
 			if (sel.entity == nullptr) {
 			if (sel.entity == nullptr) {
+				ERROR_BLOCK();
 				gbString type_str = type_to_string(bt);
 				gbString type_str = type_to_string(bt);
 				error(ce->args[0],
 				error(ce->args[0],
 				      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 				      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -6014,7 +6105,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 				return false;
 				return false;
 			}
 			}
 
 
-			enable_target_feature({}, str_lit("atomics"));
+			if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) {
+				error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name));
+				return false;
+			}
 
 
 			Operand ptr = {};
 			Operand ptr = {};
 			Operand expected = {};
 			Operand expected = {};
@@ -6068,7 +6162,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 				return false;
 				return false;
 			}
 			}
 
 
-			enable_target_feature({}, str_lit("atomics"));
+			if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) {
+				error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name));
+				return false;
+			}
 
 
 			Operand ptr = {};
 			Operand ptr = {};
 			Operand waiters = {};
 			Operand waiters = {};

+ 50 - 10
src/check_decl.cpp

@@ -886,17 +886,37 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 
 	check_objc_methods(ctx, e, ac);
 	check_objc_methods(ctx, e, ac);
 
 
-	if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
-		error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
-	} else if (ac.require_target_feature.len != 0) {
-		if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) {
-			e->Procedure.target_feature = ac.require_target_feature;
-		} else {
-			e->Procedure.target_feature_disabled = true;
+	{
+		if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
+			error(e->token, "A procedure cannot have both @(require_target_feature=\"...\") and @(enable_target_feature=\"...\")");
+		}
+
+		if (build_context.strict_target_features && ac.enable_target_feature.len != 0) {
+			ac.require_target_feature = ac.enable_target_feature;
+			ac.enable_target_feature.len = 0;
+		}
+
+		if (ac.require_target_feature.len != 0) {
+			pt->require_target_feature = ac.require_target_feature;
+			String invalid;
+			if (!check_target_feature_is_valid_globally(ac.require_target_feature, &invalid)) {
+				error(e->token, "Required target feature '%.*s' is not a valid target feature", LIT(invalid));
+			} else if (!check_target_feature_is_enabled(ac.require_target_feature, nullptr)) {
+				e->flags |= EntityFlag_Disabled;
+			}
+		} else if (ac.enable_target_feature.len != 0) {
+
+			// NOTE: disallow wasm, features on that arch are always global to the module.
+			if (is_arch_wasm()) {
+				error(e->token, "@(enable_target_feature=\"...\") is not allowed on wasm, features for wasm must be declared globally");
+			}
+
+			pt->enable_target_feature = ac.enable_target_feature;
+			String invalid;
+			if (!check_target_feature_is_valid_globally(ac.enable_target_feature, &invalid)) {
+				error(e->token, "Procedure enabled target feature '%.*s' is not a valid target feature", LIT(invalid));
+			}
 		}
 		}
-	} else if (ac.enable_target_feature.len != 0) {
-		enable_target_feature(e->token.pos, ac.enable_target_feature);
-		e->Procedure.target_feature = ac.enable_target_feature;
 	}
 	}
 
 
 	switch (e->Procedure.optimization_mode) {
 	switch (e->Procedure.optimization_mode) {
@@ -1370,6 +1390,10 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
 			continue;
 			continue;
 		}
 		}
 
 
+		if (p->flags & EntityFlag_Disabled) {
+			continue;
+		}
+
 		String name = p->token.string;
 		String name = p->token.string;
 
 
 		for (isize k = j+1; k < pge->entities.count; k++) {
 		for (isize k = j+1; k < pge->entities.count; k++) {
@@ -1387,6 +1411,10 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
 
 
 			ERROR_BLOCK();
 			ERROR_BLOCK();
 
 
+			if (q->flags & EntityFlag_Disabled) {
+				continue;
+			}
+
 			ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
 			ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
 			bool both_have_where_clauses = false;
 			bool both_have_where_clauses = false;
 			if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) {
 			if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) {
@@ -1423,6 +1451,7 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
 				break;
 				break;
 			case ProcOverload_ParamCount:
 			case ProcOverload_ParamCount:
 			case ProcOverload_ParamTypes:
 			case ProcOverload_ParamTypes:
+			case ProcOverload_TargetFeatures:
 				// This is okay :)
 				// This is okay :)
 				break;
 				break;
 
 
@@ -1590,6 +1619,17 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
 				if (e->kind != Entity_Variable) {
 				if (e->kind != Entity_Variable) {
 					continue;
 					continue;
 				}
 				}
+				if (is_type_polymorphic(e->type)) {
+					gbString s = type_to_string(e->type);
+					char const *msg = "Unspecialized polymorphic types are not allowed in procedure parameters, got %s";
+					if (e->Variable.type_expr) {
+						error(e->Variable.type_expr, msg, s);
+					} else {
+						error(e->token, msg, s);
+					}
+					gb_string_free(s);
+				}
+
 				if (!(e->flags & EntityFlag_Using)) {
 				if (!(e->flags & EntityFlag_Using)) {
 					continue;
 					continue;
 				}
 				}

+ 94 - 6
src/check_expr.cpp

@@ -102,6 +102,7 @@ gb_internal Type *   check_init_variable            (CheckerContext *c, Entity *
 gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
 gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
 gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
 gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
 
 
+gb_internal Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type);
 gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
 gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
 gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
 gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
 
 
@@ -1409,11 +1410,16 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
 			    poly->Struct.soa_kind != StructSoa_None) {
 			    poly->Struct.soa_kind != StructSoa_None) {
 				bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
 				bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
 				if (ok) switch (source->Struct.soa_kind) {
 				if (ok) switch (source->Struct.soa_kind) {
-				case StructSoa_Fixed:
+				case StructSoa_None:
 				default:
 				default:
 					GB_PANIC("Unhandled SOA Kind");
 					GB_PANIC("Unhandled SOA Kind");
 					break;
 					break;
-
+				case StructSoa_Fixed:
+					if (modify_type) {
+						Type *type = make_soa_struct_fixed(c, nullptr, poly->Struct.node, poly->Struct.soa_elem, poly->Struct.soa_count, nullptr);
+						gb_memmove(poly, type, gb_size_of(*type));
+					}
+					break;
 				case StructSoa_Slice:
 				case StructSoa_Slice:
 					if (modify_type) {
 					if (modify_type) {
 						Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
 						Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
@@ -1802,11 +1808,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
 	case Token_Not:
 	case Token_Not:
 		if (!is_type_boolean(type) || is_type_array_like(o->type)) {
 		if (!is_type_boolean(type) || is_type_array_like(o->type)) {
 			ERROR_BLOCK();
 			ERROR_BLOCK();
-			str = expr_to_string(o->expr);
 			error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
 			error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
-			gb_string_free(str);
 			if (is_type_integer(type)) {
 			if (is_type_integer(type)) {
-				error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n");
+				str = expr_to_string(o->expr);
+				error_line("\tSuggestion: Did you mean to do one of the following?\n");
+				error_line("\t\t'%s == 0'?\n", str);
+				error_line("\t\tUse of the bitwise not operator '~'?\n");
+				gb_string_free(str);
 			}
 			}
 		} else {
 		} else {
 			o->type = t_untyped_bool;
 			o->type = t_untyped_bool;
@@ -6185,6 +6193,20 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
 				}
 				}
 				return false;
 				return false;
 			}
 			}
+
+			if (ast_file_vet_style(ctx->file)) {
+				Ast *c = unparen_expr(clause);
+				if (c->kind == Ast_BinaryExpr && c->BinaryExpr.op.kind == Token_CmpAnd) {
+					ERROR_BLOCK();
+					error(c, "Prefer to separate 'where' clauses with a comma rather than '&&'");
+					gbString x = expr_to_string(c->BinaryExpr.left);
+					gbString y = expr_to_string(c->BinaryExpr.right);
+					error_line("\tSuggestion: '%s, %s'\n", x, y);
+					gb_string_free(y);
+					gb_string_free(x);
+				}
+			}
+
 		}
 		}
 	}
 	}
 
 
@@ -6526,12 +6548,17 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 		array_add(&proc_entities, proc);
 		array_add(&proc_entities, proc);
 	}
 	}
 
 
+	int max_matched_features = 0;
 
 
 	gbString expr_name = expr_to_string(operand->expr);
 	gbString expr_name = expr_to_string(operand->expr);
 	defer (gb_string_free(expr_name));
 	defer (gb_string_free(expr_name));
 
 
 	for_array(i, procs) {
 	for_array(i, procs) {
 		Entity *p = procs[i];
 		Entity *p = procs[i];
+		if (p->flags & EntityFlag_Disabled) {
+			continue;
+		}
+
 		Type *pt = base_type(p->type);
 		Type *pt = base_type(p->type);
 		if (pt != nullptr && is_type_proc(pt)) {
 		if (pt != nullptr && is_type_proc(pt)) {
 			CallArgumentData data = {};
 			CallArgumentData data = {};
@@ -6562,11 +6589,24 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 				item.score += assign_score_function(1);
 				item.score += assign_score_function(1);
 			}
 			}
 
 
+			max_matched_features = gb_max(max_matched_features, matched_target_features(&pt->Proc));
+
 			item.index = index;
 			item.index = index;
 			array_add(&valids, item);
 			array_add(&valids, item);
 		}
 		}
 	}
 	}
 
 
+	if (max_matched_features > 0) {
+		for_array(i, valids) {
+			Entity *p = procs[valids[i].index];
+			Type *t = base_type(p->type);
+			GB_ASSERT(t->kind == Type_Proc);
+
+			int matched = matched_target_features(&t->Proc);
+			valids[i].score += assign_score_function(max_matched_features-matched);
+		}
+	}
+
 	if (valids.count > 1) {
 	if (valids.count > 1) {
 		array_sort(valids, valid_index_and_score_cmp);
 		array_sort(valids, valid_index_and_score_cmp);
 		i64 best_score = valids[0].score;
 		i64 best_score = valids[0].score;
@@ -6708,7 +6748,11 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
 		ERROR_BLOCK();
 		ERROR_BLOCK();
 
 
 		error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
 		error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
-		print_argument_types();
+		if (positional_operands.count == 0 && named_operands.count == 0) {
+			error_line("\tNo given arguments\n");
+		} else {
+			print_argument_types();
+		}
 
 
 		for (auto const &valid : valids) {
 		for (auto const &valid : valids) {
 			Entity *proc = proc_entities[valid.index];
 			Entity *proc = proc_entities[valid.index];
@@ -7553,8 +7597,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 		}
 		}
 	}
 	}
 
 
+	bool is_call_inlined = false;
+
 	switch (inlining) {
 	switch (inlining) {
 	case ProcInlining_inline:
 	case ProcInlining_inline:
+		is_call_inlined = true;
 		if (proc != nullptr) {
 		if (proc != nullptr) {
 			Entity *e = entity_from_expr(proc);
 			Entity *e = entity_from_expr(proc);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 			if (e != nullptr && e->kind == Entity_Procedure) {
@@ -7570,6 +7617,47 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 		break;
 		break;
 	case ProcInlining_no_inline:
 	case ProcInlining_no_inline:
 		break;
 		break;
+	case ProcInlining_none:
+		if (proc != nullptr) {
+			Entity *e = entity_from_expr(proc);
+			if (e != nullptr && e->kind == Entity_Procedure) {
+				DeclInfo *decl = e->decl_info;
+				if (decl->proc_lit) {
+					ast_node(pl, ProcLit, decl->proc_lit);
+					if (pl->inlining == ProcInlining_inline) {
+						is_call_inlined = true;
+					}
+				}
+			}
+		}
+	}
+
+	{
+		String invalid;
+		if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
+			if (!check_target_feature_is_valid_for_target_arch(pt->Proc.require_target_feature, &invalid)) {
+				error(call, "Called procedure requires target feature '%.*s' which is invalid for the build target", LIT(invalid));
+			} else if (!check_target_feature_is_enabled(pt->Proc.require_target_feature, &invalid)) {
+				error(call, "Calling this procedure requires target feature '%.*s' to be enabled", LIT(invalid));
+			}
+		}
+
+		if (pt->kind == Type_Proc && pt->Proc.enable_target_feature.len != 0) {
+			if (!check_target_feature_is_valid_for_target_arch(pt->Proc.enable_target_feature, &invalid)) {
+				error(call, "Called procedure enables target feature '%.*s' which is invalid for the build target", LIT(invalid));
+			}
+
+			// NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
+			if (is_call_inlined) {
+				GB_ASSERT(c->curr_proc_decl);
+				GB_ASSERT(c->curr_proc_decl->entity);
+				GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
+				String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
+				if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
+					error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+				}
+			}
+		}
 	}
 	}
 
 
 	operand->expr = call;
 	operand->expr = call;

+ 32 - 7
src/check_type.cpp

@@ -381,6 +381,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
 			Type *type = nullptr;
 			Type *type = nullptr;
 			bool is_type_param = false;
 			bool is_type_param = false;
 			bool is_type_polymorphic_type = false;
 			bool is_type_polymorphic_type = false;
+			Type *specialization = nullptr;
 			if (type_expr == nullptr && default_value == nullptr) {
 			if (type_expr == nullptr && default_value == nullptr) {
 				error(param, "Expected a type for this parameter");
 				error(param, "Expected a type for this parameter");
 				continue;
 				continue;
@@ -393,7 +394,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
 				}
 				}
 				if (type_expr->kind == Ast_TypeidType) {
 				if (type_expr->kind == Ast_TypeidType) {
 					is_type_param = true;
 					is_type_param = true;
-					Type *specialization = nullptr;
 					if (type_expr->TypeidType.specialization != nullptr) {
 					if (type_expr->TypeidType.specialization != nullptr) {
 						Ast *s = type_expr->TypeidType.specialization;
 						Ast *s = type_expr->TypeidType.specialization;
 						specialization = check_type(ctx, s);
 						specialization = check_type(ctx, s);
@@ -471,6 +471,15 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
 						if (is_type_polymorphic(base_type(operand.type))) {
 						if (is_type_polymorphic(base_type(operand.type))) {
 							*is_polymorphic_ = true;
 							*is_polymorphic_ = true;
 							can_check_fields = false;
 							can_check_fields = false;
+						} else if (specialization &&
+						           !check_type_specialization_to(ctx, specialization, operand.type, false, /*modify_type*/true)) {
+							if (!ctx->no_polymorphic_errors) {
+								gbString t = type_to_string(operand.type);
+								gbString s = type_to_string(specialization);
+								error(operand.expr, "Cannot convert type '%s' to the specialization '%s'", t, s);
+								gb_string_free(s);
+								gb_string_free(t);
+							}
 						}
 						}
 						e = alloc_entity_type_name(scope, token, operand.type);
 						e = alloc_entity_type_name(scope, token, operand.type);
 						e->TypeName.is_type_alias = true;
 						e->TypeName.is_type_alias = true;
@@ -797,11 +806,11 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
 	enum_type->Enum.scope = ctx->scope;
 	enum_type->Enum.scope = ctx->scope;
 
 
 	Type *base_type = t_int;
 	Type *base_type = t_int;
-	if (et->base_type != nullptr) {
+	if (unparen_expr(et->base_type) != nullptr) {
 		base_type = check_type(ctx, et->base_type);
 		base_type = check_type(ctx, et->base_type);
 	}
 	}
 
 
-	if (base_type == nullptr || !is_type_integer(base_type)) {
+	if (base_type == nullptr || base_type == t_invalid || !is_type_integer(base_type)) {
 		error(node, "Base type for enumeration must be an integer");
 		error(node, "Base type for enumeration must be an integer");
 		return;
 		return;
 	}
 	}
@@ -1080,6 +1089,8 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
 			array_add(&tags, tag);
 			array_add(&tags, tag);
 
 
 			add_entity_use(ctx, field, e);
 			add_entity_use(ctx, field, e);
+
+			total_bit_size += bit_size_u8;
 		}
 		}
 	}
 	}
 
 
@@ -1094,7 +1105,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
 
 
 	if (total_bit_size > maximum_bit_size) {
 	if (total_bit_size > maximum_bit_size) {
 		gbString s = type_to_string(backing_type);
 		gbString s = type_to_string(backing_type);
-		error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu",
+		error(node, "The total bit size of a bit_field's fields (%llu) must fit into its backing type's (%s) bit size of %llu",
 		      cast(unsigned long long)total_bit_size,
 		      cast(unsigned long long)total_bit_size,
 		      s,
 		      s,
 		      cast(unsigned long long)maximum_bit_size);
 		      cast(unsigned long long)maximum_bit_size);
@@ -1155,7 +1166,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
 				}
 				}
 			}
 			}
 			if (all_ones && all_booleans) {
 			if (all_ones && all_booleans) {
-				if (build_context.vet_flags & VetFlag_Style) {
+				if (ast_file_vet_style(ctx->file)) {
 					char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)";
 					char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)";
 					error(node, msg);
 					error(node, msg);
 				} else {
 				} else {
@@ -1428,6 +1439,10 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
 		bool can_convert = check_cast_internal(ctx, &o, specialization);
 		bool can_convert = check_cast_internal(ctx, &o, specialization);
 		return can_convert;
 		return can_convert;
 	} else if (t->kind == Type_Struct) {
 	} else if (t->kind == Type_Struct) {
+		if (t->Struct.polymorphic_parent == nullptr &&
+		    t == s) {
+			return true;
+		}
 		if (t->Struct.polymorphic_parent == specialization) {
 		if (t->Struct.polymorphic_parent == specialization) {
 			return true;
 			return true;
 		}
 		}
@@ -1477,6 +1492,10 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
 			return true;
 			return true;
 		}
 		}
 	} else if (t->kind == Type_Union) {
 	} else if (t->kind == Type_Union) {
+		if (t->Union.polymorphic_parent == nullptr &&
+		    t == s) {
+			return true;
+		}
 		if (t->Union.polymorphic_parent == specialization) {
 		if (t->Union.polymorphic_parent == specialization) {
 			return true;
 			return true;
 		}
 		}
@@ -2017,8 +2036,8 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 				}
 				}
 
 
 				if (p->flags&FieldFlag_no_alias) {
 				if (p->flags&FieldFlag_no_alias) {
-					if (!is_type_pointer(type)) {
-						error(name, "'#no_alias' can only be applied pointer typed parameters");
+					if (!is_type_pointer(type) && !is_type_multi_pointer(type)) {
+						error(name, "'#no_alias' can only be applied pointer or multi-pointer typed parameters");
 						p->flags &= ~FieldFlag_no_alias; // Remove the flag
 						p->flags &= ~FieldFlag_no_alias; // Remove the flag
 					}
 					}
 				}
 				}
@@ -2057,6 +2076,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 					param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
 					param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
 					param->Variable.param_value = param_value;
 					param->Variable.param_value = param_value;
 					param->Variable.field_group_index = field_group_index;
 					param->Variable.field_group_index = field_group_index;
+					param->Variable.type_expr = type_expr;
 				}
 				}
 			}
 			}
 			if (p->flags&FieldFlag_no_alias) {
 			if (p->flags&FieldFlag_no_alias) {
@@ -3263,6 +3283,11 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
 	case_end;
 	case_end;
 
 
 	case_ast_node(pe, ParenExpr, e);
 	case_ast_node(pe, ParenExpr, e);
+		if (pe->expr == nullptr) {
+			error(e, "Expected an expression or type within the parentheses");
+			*type = t_invalid;
+			return true;
+		}
 		*type = check_type_expr(ctx, pe->expr, named_type);
 		*type = check_type_expr(ctx, pe->expr, named_type);
 		set_base_type(named_type, *type);
 		set_base_type(named_type, *type);
 		return true;
 		return true;

+ 10 - 0
src/checker_builtin_procs.hpp

@@ -44,6 +44,8 @@ enum BuiltinProcId {
 	// "Intrinsics"
 	// "Intrinsics"
 	BuiltinProc_is_package_imported,
 	BuiltinProc_is_package_imported,
 
 
+	BuiltinProc_has_target_feature,
+
 	BuiltinProc_transpose,
 	BuiltinProc_transpose,
 	BuiltinProc_outer_product,
 	BuiltinProc_outer_product,
 	BuiltinProc_hadamard_product,
 	BuiltinProc_hadamard_product,
@@ -267,6 +269,9 @@ BuiltinProc__type_simple_boolean_end,
 	BuiltinProc_type_variant_type_of,
 	BuiltinProc_type_variant_type_of,
 	BuiltinProc_type_variant_index_of,
 	BuiltinProc_type_variant_index_of,
 
 
+	BuiltinProc_type_bit_set_elem_type,
+	BuiltinProc_type_bit_set_underlying_type,
+
 	BuiltinProc_type_struct_field_count,
 	BuiltinProc_type_struct_field_count,
 
 
 	BuiltinProc_type_proc_parameter_count,
 	BuiltinProc_type_proc_parameter_count,
@@ -354,6 +359,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	// "Intrinsics"
 	// "Intrinsics"
 	{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
+	{STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("transpose"),        1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("transpose"),        1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("outer_product"),    2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("outer_product"),    2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -573,6 +580,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_variant_type_of"),        2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_variant_type_of"),        2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_variant_index_of"),       2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_variant_index_of"),       2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
+	{STR_LIT("type_bit_set_elem_type"),       1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_bit_set_underlying_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("type_struct_field_count"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_struct_field_count"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 2 - 3
src/entity.cpp

@@ -210,6 +210,7 @@ struct Entity {
 			CommentGroup *comment;
 			CommentGroup *comment;
 		} Constant;
 		} Constant;
 		struct {
 		struct {
+			Ast *type_expr; // only used for some variables within procedure bodies
 			Ast *init_expr; // only used for some variables within procedure bodies
 			Ast *init_expr; // only used for some variables within procedure bodies
 			i32  field_index;
 			i32  field_index;
 			i32  field_group_index;
 			i32  field_group_index;
@@ -252,10 +253,8 @@ struct Entity {
 			bool    is_foreign                 : 1;
 			bool    is_foreign                 : 1;
 			bool    is_export                  : 1;
 			bool    is_export                  : 1;
 			bool    generated_from_polymorphic : 1;
 			bool    generated_from_polymorphic : 1;
-			bool    target_feature_disabled    : 1;
 			bool    entry_point_only           : 1;
 			bool    entry_point_only           : 1;
 			bool    has_instrumentation        : 1;
 			bool    has_instrumentation        : 1;
-			String  target_feature;
 		} Procedure;
 		} Procedure;
 		struct {
 		struct {
 			Array<Entity *> entities;
 			Array<Entity *> entities;
@@ -502,4 +501,4 @@ gb_internal bool is_entity_local_variable(Entity *e) {
 
 
 	return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) ||
 	return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) ||
 	       (e->scope->flags & ScopeFlag_Proc) != 0;
 	       (e->scope->flags & ScopeFlag_Proc) != 0;
-}
+}

+ 68 - 39
src/error.cpp

@@ -12,7 +12,7 @@ struct ErrorValue {
 };
 };
 
 
 struct ErrorCollector {
 struct ErrorCollector {
-	TokenPos prev;
+	// TokenPos prev; // no point collecting because of the mulithreaded nature
 	std::atomic<i64>  count;
 	std::atomic<i64>  count;
 	std::atomic<i64>  warning_count;
 	std::atomic<i64>  warning_count;
 	std::atomic<bool> in_block;
 	std::atomic<bool> in_block;
@@ -383,14 +383,13 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va
 	}
 	}
 
 
 	push_error_value(pos, ErrorValue_Error);
 	push_error_value(pos, ErrorValue_Error);
-	// NOTE(bill): Duplicate error, skip it
 	if (pos.line == 0) {
 	if (pos.line == 0) {
 		error_out_empty();
 		error_out_empty();
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
 		error_out("\n");
 		error_out("\n");
-	} else if (global_error_collector.prev != pos) {
-		global_error_collector.prev = pos;
+	} else {
+		// global_error_collector.prev = pos;
 		if (json_errors()) {
 		if (json_errors()) {
 			error_out_empty();
 			error_out_empty();
 		} else {
 		} else {
@@ -402,10 +401,6 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
 		error_out("\n");
 		error_out("\n");
 		show_error_on_line(pos, end);
 		show_error_on_line(pos, end);
-	} else {
-		global_error_collector.curr_error_value = {};
-		global_error_collector.curr_error_value_set.store(false);
-		global_error_collector.count.fetch_sub(1);
 	}
 	}
 	try_pop_error_value();
 	try_pop_error_value();
 	mutex_unlock(&global_error_collector.mutex);
 	mutex_unlock(&global_error_collector.mutex);
@@ -422,14 +417,13 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
 	push_error_value(pos, ErrorValue_Warning);
 	push_error_value(pos, ErrorValue_Warning);
 
 
 	if (!global_ignore_warnings()) {
 	if (!global_ignore_warnings()) {
-		// NOTE(bill): Duplicate error, skip it
 		if (pos.line == 0) {
 		if (pos.line == 0) {
 			error_out_empty();
 			error_out_empty();
 			error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
 			error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
 			error_out_va(fmt, va);
 			error_out_va(fmt, va);
 			error_out("\n");
 			error_out("\n");
-		} else if (global_error_collector.prev != pos) {
-			global_error_collector.prev = pos;
+		} else {
+			// global_error_collector.prev = pos;
 			if (json_errors()) {
 			if (json_errors()) {
 				error_out_empty();
 				error_out_empty();
 			} else {
 			} else {
@@ -452,21 +446,20 @@ gb_internal void error_line_va(char const *fmt, va_list va) {
 
 
 gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
 gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
 	global_error_collector.count.fetch_add(1);
 	global_error_collector.count.fetch_add(1);
+	mutex_lock(&global_error_collector.mutex);
 	if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
 	if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
 		print_all_errors();
 		print_all_errors();
 		gb_exit(1);
 		gb_exit(1);
 	}
 	}
-	mutex_lock(&global_error_collector.mutex);
 
 
 	push_error_value(pos, ErrorValue_Error);
 	push_error_value(pos, ErrorValue_Error);
 
 
-	// NOTE(bill): Duplicate error, skip it
 	if (pos.line == 0) {
 	if (pos.line == 0) {
 		error_out_empty();
 		error_out_empty();
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
-	} else if (global_error_collector.prev != pos) {
-		global_error_collector.prev = pos;
+	} else {
+		// global_error_collector.prev = pos;
 		if (json_errors()) {
 		if (json_errors()) {
 			error_out_empty();
 			error_out_empty();
 		} else {
 		} else {
@@ -485,17 +478,21 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
 
 
 gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	global_error_collector.count.fetch_add(1);
 	global_error_collector.count.fetch_add(1);
+	mutex_lock(&global_error_collector.mutex);
 	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
 	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
 		print_all_errors();
 		print_all_errors();
 		gb_exit(1);
 		gb_exit(1);
 	}
 	}
-	mutex_lock(&global_error_collector.mutex);
 
 
 	push_error_value(pos, ErrorValue_Warning);
 	push_error_value(pos, ErrorValue_Warning);
 
 
-	// NOTE(bill): Duplicate error, skip it
-	if (global_error_collector.prev != pos) {
-		global_error_collector.prev = pos;
+	if (pos.line == 0) {
+		error_out_empty();
+		error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
+		error_out_va(fmt, va);
+		error_out("\n");
+	} else {
+		// global_error_collector.prev = pos;
 		if (json_errors()) {
 		if (json_errors()) {
 			error_out_empty();
 			error_out_empty();
 		} else {
 		} else {
@@ -505,11 +502,6 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
 		error_out("\n");
 		error_out("\n");
 		show_error_on_line(pos, end);
 		show_error_on_line(pos, end);
-	} else if (pos.line == 0) {
-		error_out_empty();
-		error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
-		error_out_va(fmt, va);
-		error_out("\n");
 	}
 	}
 
 
 	try_pop_error_value();
 	try_pop_error_value();
@@ -518,22 +510,21 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *
 
 
 gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	global_error_collector.count.fetch_add(1);
 	global_error_collector.count.fetch_add(1);
+	mutex_lock(&global_error_collector.mutex);
 	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
 	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
 		print_all_errors();
 		print_all_errors();
 		gb_exit(1);
 		gb_exit(1);
 	}
 	}
-	mutex_lock(&global_error_collector.mutex);
 
 
 	push_error_value(pos, ErrorValue_Warning);
 	push_error_value(pos, ErrorValue_Warning);
 
 
-	// NOTE(bill): Duplicate error, skip it
 	if (pos.line == 0) {
 	if (pos.line == 0) {
 		error_out_empty();
 		error_out_empty();
 		error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red);
 		error_out_va(fmt, va);
 		error_out_va(fmt, va);
 		error_out("\n");
 		error_out("\n");
-	} else if (global_error_collector.prev != pos) {
-		global_error_collector.prev = pos;
+	} else {
+		// global_error_collector.prev = pos;
 		if (json_errors()) {
 		if (json_errors()) {
 			error_out_empty();
 			error_out_empty();
 		} else {
 		} else {
@@ -564,9 +555,13 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
 	push_error_value(pos, ErrorValue_Warning);
 	push_error_value(pos, ErrorValue_Warning);
 
 
 	if (!global_ignore_warnings()) {
 	if (!global_ignore_warnings()) {
-		// NOTE(bill): Duplicate error, skip it
-		if (global_error_collector.prev != pos) {
-			global_error_collector.prev = pos;
+		if (pos.line == 0) {
+			error_out_empty();
+			error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
+			error_out_va(fmt, va);
+			error_out("\n");
+		} else {
+			// global_error_collector.prev = pos;
 			if (json_errors()) {
 			if (json_errors()) {
 				error_out_empty();
 				error_out_empty();
 			} else {
 			} else {
@@ -576,11 +571,6 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
 			error_out_va(fmt, va);
 			error_out_va(fmt, va);
 			error_out("\n");
 			error_out("\n");
 			// show_error_on_line(pos, end);
 			// show_error_on_line(pos, end);
-		} else if (pos.line == 0) {
-			error_out_empty();
-			error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
-			error_out_va(fmt, va);
-			error_out("\n");
 		}
 		}
 	}
 	}
 
 
@@ -705,9 +695,46 @@ gb_internal void print_all_errors(void) {
 
 
 	GB_ASSERT(any_errors() || any_warnings());
 	GB_ASSERT(any_errors() || any_warnings());
 
 
-
 	array_sort(global_error_collector.error_values, error_value_cmp);
 	array_sort(global_error_collector.error_values, error_value_cmp);
 
 
+
+	{ // NOTE(bill): merge neighbouring errors
+		isize default_lines_to_skip = 1;
+		if (show_error_line()) {
+			// NOTE(bill): this will always be 2 extra lines
+			default_lines_to_skip += 2;
+		}
+
+		ErrorValue *prev_ev = nullptr;
+		for (isize i = 0; i < global_error_collector.error_values.count; /**/) {
+			ErrorValue &ev = global_error_collector.error_values[i];
+
+			if (prev_ev && prev_ev->pos == ev.pos) {
+				String_Iterator it = {{ev.msg.data, ev.msg.count}, 0};
+
+				for (isize lines_to_skip = default_lines_to_skip; lines_to_skip > 0; lines_to_skip -= 1) {
+					String line = string_split_iterator(&it, '\n');
+					if (line.len == 0) {
+						break;
+					}
+				}
+
+				// Merge additional text (suggestions for example) into the previous error.
+				String current = {prev_ev->msg.data, prev_ev->msg.count};
+				String addition = {it.str.text+it.pos, it.str.len-it.pos};
+				if (addition.len > 0 && !string_contains_string(current, addition)) {
+					array_add_elems(&prev_ev->msg, addition.text, addition.len);
+				}
+
+				array_free(&ev.msg);
+				array_ordered_remove(&global_error_collector.error_values, i);
+			} else {
+				prev_ev = &ev;
+				i += 1;
+			}
+		}
+	}
+
 	gbString res = gb_string_make(heap_allocator(), "");
 	gbString res = gb_string_make(heap_allocator(), "");
 	defer (gb_string_free(res));
 	defer (gb_string_free(res));
 
 
@@ -715,6 +742,7 @@ gb_internal void print_all_errors(void) {
 		res = gb_string_append_fmt(res, "{\n");
 		res = gb_string_append_fmt(res, "{\n");
 		res = gb_string_append_fmt(res, "\t\"error_count\": %td,\n", global_error_collector.error_values.count);
 		res = gb_string_append_fmt(res, "\t\"error_count\": %td,\n", global_error_collector.error_values.count);
 		res = gb_string_append_fmt(res, "\t\"errors\": [\n");
 		res = gb_string_append_fmt(res, "\t\"errors\": [\n");
+
 		for_array(i, global_error_collector.error_values) {
 		for_array(i, global_error_collector.error_values) {
 			ErrorValue ev = global_error_collector.error_values[i];
 			ErrorValue ev = global_error_collector.error_values[i];
 
 
@@ -728,9 +756,8 @@ gb_internal void print_all_errors(void) {
 			}
 			}
 			res = gb_string_append_fmt(res, "\",\n");
 			res = gb_string_append_fmt(res, "\",\n");
 
 
-			res = gb_string_append_fmt(res, "\t\t\t\"pos\": {\n");
-
 			if (ev.pos.file_id) {
 			if (ev.pos.file_id) {
+				res = gb_string_append_fmt(res, "\t\t\t\"pos\": {\n");
 				res = gb_string_append_fmt(res, "\t\t\t\t\"file\": \"");
 				res = gb_string_append_fmt(res, "\t\t\t\t\"file\": \"");
 				String file = get_file_path_string(ev.pos.file_id);
 				String file = get_file_path_string(ev.pos.file_id);
 				for (isize k = 0; k < file.len; k++) {
 				for (isize k = 0; k < file.len; k++) {
@@ -743,6 +770,8 @@ gb_internal void print_all_errors(void) {
 				i32 end_column = gb_max(ev.end.column, ev.pos.column);
 				i32 end_column = gb_max(ev.end.column, ev.pos.column);
 				res = gb_string_append_fmt(res, "\t\t\t\t\"end_column\": %d\n", end_column);
 				res = gb_string_append_fmt(res, "\t\t\t\t\"end_column\": %d\n", end_column);
 				res = gb_string_append_fmt(res, "\t\t\t},\n");
 				res = gb_string_append_fmt(res, "\t\t\t},\n");
+			} else {
+				res = gb_string_append_fmt(res, "\t\t\t\"pos\": null,\n");
 			}
 			}
 
 
 			res = gb_string_append_fmt(res, "\t\t\t\"msgs\": [\n");
 			res = gb_string_append_fmt(res, "\t\t\t\"msgs\": [\n");

+ 5 - 0
src/exact_value.cpp

@@ -121,12 +121,14 @@ gb_internal ExactValue exact_value_string(String string) {
 
 
 gb_internal ExactValue exact_value_i64(i64 i) {
 gb_internal ExactValue exact_value_i64(i64 i) {
 	ExactValue result = {ExactValue_Integer};
 	ExactValue result = {ExactValue_Integer};
+	result.value_integer = {0};
 	big_int_from_i64(&result.value_integer, i);
 	big_int_from_i64(&result.value_integer, i);
 	return result;
 	return result;
 }
 }
 
 
 gb_internal ExactValue exact_value_u64(u64 i) {
 gb_internal ExactValue exact_value_u64(u64 i) {
 	ExactValue result = {ExactValue_Integer};
 	ExactValue result = {ExactValue_Integer};
+	result.value_integer = {0};
 	big_int_from_u64(&result.value_integer, i);
 	big_int_from_u64(&result.value_integer, i);
 	return result;
 	return result;
 }
 }
@@ -177,6 +179,7 @@ gb_internal ExactValue exact_value_typeid(Type *type) {
 
 
 gb_internal ExactValue exact_value_integer_from_string(String const &string) {
 gb_internal ExactValue exact_value_integer_from_string(String const &string) {
 	ExactValue result = {ExactValue_Integer};
 	ExactValue result = {ExactValue_Integer};
+	result.value_integer = {0};
 	bool success;
 	bool success;
 	big_int_from_string(&result.value_integer, string, &success);
 	big_int_from_string(&result.value_integer, string, &success);
 	if (!success) {
 	if (!success) {
@@ -585,6 +588,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3
 			return v;
 			return v;
 		case ExactValue_Integer: {
 		case ExactValue_Integer: {
 			ExactValue i = {ExactValue_Integer};
 			ExactValue i = {ExactValue_Integer};
+			i.value_integer = {0};
 			big_int_neg(&i.value_integer, &v.value_integer);
 			big_int_neg(&i.value_integer, &v.value_integer);
 			return i;
 			return i;
 		}
 		}
@@ -616,6 +620,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3
 		case ExactValue_Integer: {
 		case ExactValue_Integer: {
 			GB_ASSERT(precision != 0);
 			GB_ASSERT(precision != 0);
 			ExactValue i = {ExactValue_Integer};
 			ExactValue i = {ExactValue_Integer};
+			i.value_integer = {0};
 			big_int_not(&i.value_integer, &v.value_integer, precision, !is_unsigned);
 			big_int_not(&i.value_integer, &v.value_integer, precision, !is_unsigned);
 			return i;
 			return i;
 		}
 		}

+ 2 - 0
src/gb/gb.h

@@ -3025,6 +3025,8 @@ gb_inline u32 gb_thread_current_id(void) {
 	thread_id = gettid();
 	thread_id = gettid();
 #elif defined(GB_SYSTEM_HAIKU)
 #elif defined(GB_SYSTEM_HAIKU)
 	thread_id = find_thread(NULL);
 	thread_id = find_thread(NULL);
+#elif defined(GB_SYSTEM_FREEBSD)
+	thread_id = pthread_getthreadid_np();
 #else
 #else
 	#error Unsupported architecture for gb_thread_current_id()
 	#error Unsupported architecture for gb_thread_current_id()
 #endif
 #endif

+ 1 - 1
src/linker.cpp

@@ -432,7 +432,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 							if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
 							if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
 								// static libs and object files, absolute full path relative to the file in which the lib was imported from
 								// static libs and object files, absolute full path relative to the file in which the lib was imported from
 								lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
 								lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
-							} else if (string_ends_with(lib, str_lit(".so"))) {
+							} else if (string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
 								// dynamic lib, relative path to executable
 								// dynamic lib, relative path to executable
 								// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
 								// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
 								//                at runtime to the executable
 								//                at runtime to the executable

+ 448 - 66
src/llvm_backend.cpp

@@ -41,6 +41,37 @@ String get_default_microarchitecture() {
 	return default_march;
 	return default_march;
 }
 }
 
 
+String get_final_microarchitecture() {
+	BuildContext *bc = &build_context;
+
+	String microarch = bc->microarch;
+	if (microarch.len == 0) {
+		microarch = get_default_microarchitecture();
+	} else if (microarch == str_lit("native")) {
+		microarch = make_string_c(LLVMGetHostCPUName());
+	}
+	return microarch;
+}
+
+gb_internal String get_default_features() {
+	BuildContext *bc = &build_context;
+
+	int off = 0;
+	for (int i = 0; i < bc->metrics.arch; i += 1) {
+		off += target_microarch_counts[i];
+	}
+
+	String microarch = get_final_microarchitecture();
+	for (int i = off; i < off+target_microarch_counts[bc->metrics.arch]; i += 1) {
+		if (microarch_features_list[i].microarch == microarch) {
+			return microarch_features_list[i].features;
+		}
+	}
+
+	GB_PANIC("unknown microarch");
+	return {};
+}
+
 gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e) {
 gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e) {
 	if (e == nullptr) {
 	if (e == nullptr) {
 		return;
 		return;
@@ -1477,6 +1508,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
 	case 1:
 	case 1:
 // default<Os>
 // default<Os>
 // Passes removed: coro, openmp, sroa
 // Passes removed: coro, openmp, sroa
+#if LLVM_VERSION_MAJOR == 17
 		array_add(&passes, u8R"(
 		array_add(&passes, u8R"(
 annotation2metadata,
 annotation2metadata,
 forceattrs,
 forceattrs,
@@ -1492,13 +1524,14 @@ globalopt,
 function<eager-inv>(
 function<eager-inv>(
 	mem2reg,
 	mem2reg,
 	instcombine<max-iterations=1000;no-use-loop-info>,
 	instcombine<max-iterations=1000;no-use-loop-info>,
-	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),
-	require<globals-aa>,
-	function(
-		invalidate<aa>
-	),
-	require<profile-summary>,
-	cgscc(
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+require<globals-aa>,
+function(
+	invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
 	devirt<4>(
 	devirt<4>(
 		inline<only-mandatory>,
 		inline<only-mandatory>,
 		inline,
 		inline,
@@ -1599,10 +1632,142 @@ function(
 ),
 ),
 verify
 verify
 )");
 )");
+#else
+		array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+	lower-expect,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	sroa<modify-cfg>,
+	early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+	mem2reg,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(
+	invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+	devirt<4>(
+		inline,
+		function-attrs<skip-non-recursive-function-attrs>,
+		function<eager-inv;no-rerun>(
+			sroa<modify-cfg>,
+			early-cse<memssa>,
+			speculative-execution<only-if-divergent-target>,
+			jump-threading,
+			correlated-propagation,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			aggressive-instcombine,
+			tailcallelim,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			reassociate,
+			constraint-elimination,
+			loop-mssa(
+				loop-instsimplify,
+				loop-simplifycfg,
+				licm<no-allowspeculation>,
+				loop-rotate<header-duplication;no-prepare-for-lto>,
+				licm<allowspeculation>,
+				simple-loop-unswitch<no-nontrivial;trivial>
+			),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			loop(
+				loop-idiom,
+				indvars,
+				loop-deletion,
+				loop-unroll-full
+			),
+			sroa<modify-cfg>,
+			vector-combine,
+			mldst-motion<no-split-footer-bb>,
+			gvn<>,
+			sccp,
+			bdce,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			jump-threading,
+			correlated-propagation,
+			adce,
+			memcpyopt,
+			dse,
+			move-auto-init,
+			loop-mssa(
+				licm<allowspeculation>
+			),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+		),
+		function-attrs,
+		function(
+			require<should-not-run-function-passes>
+		)
+	)
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+	float2int,
+	lower-constant-intrinsics,
+	loop(
+		loop-rotate<header-duplication;no-prepare-for-lto>,
+		loop-deletion
+	),
+	loop-distribute,
+	inject-tli-mappings,
+	loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+	infer-alignment,
+	loop-load-elim,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	slp-vectorizer,
+	vector-combine,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-unroll<O2>,
+	transform-warning,
+	sroa<preserve-cfg>,
+	infer-alignment,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-mssa(
+		licm<allowspeculation>
+	),
+	alignment-from-assumptions,
+	loop-sink,
+	instsimplify,
+	div-rem-pairs,
+	tailcallelim,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+	annotation-remarks
+),
+verify
+)");
+#endif
 		break;
 		break;
 // default<O2>
 // default<O2>
 // Passes removed: coro, openmp, sroa
 // Passes removed: coro, openmp, sroa
 	case 2:
 	case 2:
+#if LLVM_VERSION_MAJOR == 17
 		array_add(&passes, u8R"(
 		array_add(&passes, u8R"(
 annotation2metadata,
 annotation2metadata,
 forceattrs,
 forceattrs,
@@ -1727,11 +1892,144 @@ function(
 ),
 ),
 verify
 verify
 )");
 )");
+#else
+		array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+	lower-expect,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	sroa<modify-cfg>,
+	early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+	mem2reg,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(
+	invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+	devirt<4>(
+		inline,
+		function-attrs<skip-non-recursive-function-attrs>,
+		function<eager-inv;no-rerun>(
+			sroa<modify-cfg>,
+			early-cse<memssa>,
+			speculative-execution<only-if-divergent-target>,
+			jump-threading,
+			correlated-propagation,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			aggressive-instcombine,
+			libcalls-shrinkwrap,
+			tailcallelim,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			reassociate,
+			constraint-elimination,
+			loop-mssa(
+				loop-instsimplify,
+				loop-simplifycfg,
+				licm<no-allowspeculation>,
+				loop-rotate<header-duplication;no-prepare-for-lto>,
+				licm<allowspeculation>,
+				simple-loop-unswitch<no-nontrivial;trivial>
+			),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			loop(
+				loop-idiom,
+				indvars,
+				loop-deletion,
+				loop-unroll-full
+			),
+			sroa<modify-cfg>,
+			vector-combine,
+			mldst-motion<no-split-footer-bb>,
+			gvn<>,
+			sccp,
+			bdce,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			jump-threading,
+			correlated-propagation,
+			adce,
+			memcpyopt,
+			dse,
+			move-auto-init,
+			loop-mssa(
+				licm<allowspeculation>
+			),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+		),
+		function-attrs,
+		function(
+			require<should-not-run-function-passes>
+		)
+	)
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+	float2int,
+	lower-constant-intrinsics,
+	loop(
+		loop-rotate<header-duplication;no-prepare-for-lto>,
+		loop-deletion
+	),
+	loop-distribute,
+	inject-tli-mappings,
+	loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+	infer-alignment,
+	loop-load-elim,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	slp-vectorizer,
+	vector-combine,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-unroll<O2>,
+	transform-warning,
+	sroa<modify-cfg>,
+	infer-alignment,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-mssa(
+		licm<allowspeculation>
+	),
+	alignment-from-assumptions,
+	loop-sink,
+	instsimplify,
+	div-rem-pairs,
+	tailcallelim,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+	annotation-remarks
+),
+verify
+)");
+#endif
 		break;
 		break;
 
 
 	case 3:
 	case 3:
 // default<O3>
 // default<O3>
 // Passes removed: coro, openmp, sroa
 // Passes removed: coro, openmp, sroa
+#if LLVM_VERSION_MAJOR == 17
 		array_add(&passes, u8R"(
 		array_add(&passes, u8R"(
 annotation2metadata,
 annotation2metadata,
 forceattrs,
 forceattrs,
@@ -1859,6 +2157,135 @@ function(
 ),
 ),
 verify
 verify
 )");
 )");
+#else
+		array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+	lower-expect,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	sroa<modify-cfg>,
+	early-cse<>,
+	callsite-splitting
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+	mem2reg,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(invalidate<aa>),
+require<profile-summary>,
+cgscc(
+	devirt<4>(
+		inline,
+		function-attrs<skip-non-recursive-function-attrs>,
+		argpromotion,
+		function<eager-inv;no-rerun>(
+			sroa<modify-cfg>,
+			early-cse<memssa>,
+			speculative-execution<only-if-divergent-target>,
+			jump-threading,
+			correlated-propagation,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			aggressive-instcombine,
+			libcalls-shrinkwrap,
+			tailcallelim,
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			reassociate,
+			constraint-elimination,
+			loop-mssa(
+				loop-instsimplify,
+				loop-simplifycfg,
+				licm<no-allowspeculation>,
+				loop-rotate<header-duplication;no-prepare-for-lto>,
+				licm<allowspeculation>,
+				simple-loop-unswitch<nontrivial;trivial>
+			),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			loop(
+				loop-idiom,
+				indvars,
+				loop-deletion,
+				loop-unroll-full
+			),
+			sroa<modify-cfg>,
+			vector-combine,
+			mldst-motion<no-split-footer-bb>,
+			gvn<>,
+			sccp,
+			bdce,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+			jump-threading,
+			correlated-propagation,
+			adce,
+			memcpyopt,
+			dse,
+			move-auto-init,
+			loop-mssa(licm<allowspeculation>),
+			simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+			instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+		),
+		function-attrs,
+		function(
+			require<should-not-run-function-passes>
+		)
+	)
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+	float2int,
+	lower-constant-intrinsics,
+	chr,
+	loop(
+		loop-rotate<header-duplication;no-prepare-for-lto>,
+		loop-deletion
+	),
+	loop-distribute,
+	inject-tli-mappings,
+	loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+	infer-alignment,
+	loop-load-elim,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+	slp-vectorizer,
+	vector-combine,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-unroll<O3>,
+	transform-warning,
+	sroa<preserve-cfg>,
+	infer-alignment,
+	instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+	loop-mssa(licm<allowspeculation>),
+	alignment-from-assumptions,
+	loop-sink,
+	instsimplify,
+	div-rem-pairs,
+	tailcallelim,
+	simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+	annotation-remarks
+),
+verify
+)");
+#endif
 		break;
 		break;
 	}
 	}
 
 
@@ -2468,69 +2895,24 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 		code_mode = LLVMCodeModelKernel;
 		code_mode = LLVMCodeModelKernel;
 	}
 	}
 
 
-	String      host_cpu_name = copy_string(permanent_allocator(), make_string_c(LLVMGetHostCPUName()));
-	String      llvm_cpu      = get_default_microarchitecture();
-	char const *llvm_features = "";
-	if (build_context.microarch.len != 0) {
-		if (build_context.microarch == "native") {
-			llvm_cpu = host_cpu_name;
-		} else {
-			llvm_cpu = copy_string(permanent_allocator(), build_context.microarch);
-		}
-		if (llvm_cpu == host_cpu_name) {
-			llvm_features = LLVMGetHostCPUFeatures();
+	String llvm_cpu = get_final_microarchitecture();
+
+	gbString llvm_features = gb_string_make(temporary_allocator(), "");
+	String_Iterator it = {build_context.target_features_string, 0};
+	bool first = true;
+	for (;;) {
+		String str = string_split_iterator(&it, ',');
+		if (str == "") break;
+		if (!first) {
+			llvm_features = gb_string_appendc(llvm_features, ",");
 		}
 		}
-	}
+		first = false;
 
 
-	// NOTE(Jeroen): Uncomment to get the list of supported microarchitectures.
-	/*
-	if (build_context.microarch == "?") {
-		string_set_add(&build_context.target_features_set, str_lit("+cpuhelp"));
+		llvm_features = gb_string_appendc(llvm_features, "+");
+		llvm_features = gb_string_append_length(llvm_features, str.text, str.len);
 	}
 	}
-	*/
 
 
-	if (build_context.target_features_set.entries.count != 0) {
-		// Prefix all of the features with a `+`, because we are
-		// enabling additional features.
-		char const *additional_features = target_features_set_to_cstring(permanent_allocator(), false, true);
-
-		String f_string = make_string_c(llvm_features);
-		String a_string = make_string_c(additional_features);
-		isize f_len = f_string.len;
-
-		if (f_len == 0) {
-			// The common case is that llvm_features is empty, so
-			// the target_features_set additions can be used as is.
-			llvm_features = additional_features;
-		} else {
-			// The user probably specified `-microarch:native`, so
-			// llvm_features is populated by LLVM's idea of what
-			// the host CPU supports.
-			//
-			// As far as I can tell, (which is barely better than
-			// wild guessing), a bitset is formed by parsing the
-			// string left to right.
-			//
-			// So, llvm_features + ',' + additonal_features, will
-			// makes the target_features_set override llvm_features.
-
-			char *tmp = gb_alloc_array(permanent_allocator(), char, f_len + 1 + a_string.len + 1);
-			isize len = 0;
-
-			// tmp = f_string
-			gb_memmove(tmp, f_string.text, f_string.len);
-			len += f_string.len;
-			// tmp += ','
-			tmp[len++] = ',';
-			// tmp += a_string
-			gb_memmove(tmp + len, a_string.text, a_string.len);
-			len += a_string.len;
-			// tmp += NUL
-			tmp[len++] = 0;
-
-			llvm_features = tmp;
-		}
-	}
+	debugf("CPU: %.*s, Features: %s\n", LIT(llvm_cpu), llvm_features);	
 
 
 	// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
 	// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
 
 

+ 9 - 10
src/llvm_backend.hpp

@@ -1,13 +1,9 @@
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
-#include "llvm-c/Core.h"
-#include "llvm-c/ExecutionEngine.h"
-#include "llvm-c/Target.h"
-#include "llvm-c/Analysis.h"
-#include "llvm-c/Object.h"
-#include "llvm-c/BitWriter.h"
-#include "llvm-c/DebugInfo.h"
-#include "llvm-c/Transforms/PassBuilder.h"
+#include <llvm-c/Config/llvm-config.h>
 #else
 #else
+#include <llvm/Config/llvm-config.h>
+#endif
+
 #include <llvm-c/Core.h>
 #include <llvm-c/Core.h>
 #include <llvm-c/ExecutionEngine.h>
 #include <llvm-c/ExecutionEngine.h>
 #include <llvm-c/Target.h>
 #include <llvm-c/Target.h>
@@ -26,7 +22,6 @@
 #include <llvm-c/Transforms/Utils.h>
 #include <llvm-c/Transforms/Utils.h>
 #include <llvm-c/Transforms/Vectorize.h>
 #include <llvm-c/Transforms/Vectorize.h>
 #endif
 #endif
-#endif
 
 
 #if LLVM_VERSION_MAJOR < 11
 #if LLVM_VERSION_MAJOR < 11
 #error "LLVM Version 11 is the minimum required"
 #error "LLVM Version 11 is the minimum required"
@@ -581,6 +576,10 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
 #endif
 #endif
 }
 }
 
 
+
+gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value);
+gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name);
+
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
 #define LB_CLEANUP_RUNTIME_PROC_NAME   "__$cleanup_runtime"
 #define LB_TYPE_INFO_DATA_NAME       "__$type_info_data"
 #define LB_TYPE_INFO_DATA_NAME       "__$type_info_data"
@@ -719,4 +718,4 @@ gb_global char const *llvm_linkage_strings[] = {
 	"linker private weak linkage"
 	"linker private weak linkage"
 };
 };
 
 
-#define ODIN_METADATA_REQUIRE "odin-metadata-require", 21
+#define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")

+ 5 - 1
src/llvm_backend_expr.cpp

@@ -4383,7 +4383,11 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 					mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false));
 					mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false));
 
 
 					LLVMValueRef elem = values[i].value;
 					LLVMValueRef elem = values[i].value;
-					elem = LLVMBuildZExt(p->builder, elem, lit, "");
+					if (lb_sizeof(lit) < lb_sizeof(LLVMTypeOf(elem))) {
+						elem = LLVMBuildTrunc(p->builder, elem, lit, "");
+					} else {
+						elem = LLVMBuildZExt(p->builder, elem, lit, "");
+					}
 					elem = LLVMBuildAnd(p->builder, elem, mask, "");
 					elem = LLVMBuildAnd(p->builder, elem, mask, "");
 
 
 					elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lit, f.bit_offset, false), "");
 					elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lit, f.bit_offset, false), "");

+ 6 - 0
src/llvm_backend_general.cpp

@@ -1107,6 +1107,12 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
 	GB_ASSERT(is_type_pointer(value.type));
 	GB_ASSERT(is_type_pointer(value.type));
 	Type *t = type_deref(value.type);
 	Type *t = type_deref(value.type);
 	LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
 	LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
+
+	u64 is_packed = lb_get_metadata_custom_u64(p->module, value.value, ODIN_METADATA_IS_PACKED);
+	if (is_packed != 0) {
+		LLVMSetAlignment(v, 1);
+	}
+
 	return lbValue{v, t};
 	return lbValue{v, t};
 }
 }
 
 

+ 17 - 10
src/llvm_backend_proc.cpp

@@ -177,17 +177,24 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
 		break;
 		break;
 	}
 	}
 
 
-	if (!entity->Procedure.target_feature_disabled &&
-	    entity->Procedure.target_feature.len != 0) {
-	    	auto features = split_by_comma(entity->Procedure.target_feature);
-		for_array(i, features) {
-			String feature = features[i];
-			LLVMAttributeRef ref = LLVMCreateStringAttribute(
-				m->ctx,
-				cast(char const *)feature.text, cast(unsigned)feature.len,
-				"", 0);
-			LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref);
+	if (pt->Proc.enable_target_feature.len != 0) {
+		gbString feature_str = gb_string_make(temporary_allocator(), "");
+
+		String_Iterator it = {pt->Proc.enable_target_feature, 0};
+		bool first = true;
+		for (;;) {
+			String str = string_split_iterator(&it, ',');
+			if (str == "") break;
+			if (!first) {
+				feature_str = gb_string_appendc(feature_str, ",");
+			}
+			first = false;
+
+			feature_str = gb_string_appendc(feature_str, "+");
+			feature_str = gb_string_append_length(feature_str, str.text, str.len);
 		}
 		}
+
+		lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("target-features"), make_string_c(feature_str));
 	}
 	}
 
 
 	if (entity->flags & EntityFlag_Cold) {
 	if (entity->flags & EntityFlag_Cold) {

+ 34 - 2
src/llvm_backend_utility.cpp

@@ -57,6 +57,29 @@ gb_internal lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
 	return value;
 	return value;
 }
 }
 
 
+
+gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value) {
+	unsigned md_id = LLVMGetMDKindIDInContext(m->ctx, cast(char const *)name.text, cast(unsigned)name.len);
+	LLVMMetadataRef md = LLVMValueAsMetadata(LLVMConstInt(lb_type(m, t_u64), value, false));
+	LLVMValueRef node = LLVMMetadataAsValue(m->ctx, LLVMMDNodeInContext2(m->ctx, &md, 1));
+	LLVMSetMetadata(v_ref, md_id, node);
+}
+gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name) {
+	unsigned md_id = LLVMGetMDKindIDInContext(m->ctx, cast(char const *)name.text, cast(unsigned)name.len);
+	LLVMValueRef v_md = LLVMGetMetadata(v_ref, md_id);
+	if (v_md == nullptr) {
+		return 0;
+	}
+	unsigned node_count = LLVMGetMDNodeNumOperands(v_md);
+	if (node_count == 0) {
+		return 0;
+	}
+	GB_ASSERT(node_count == 1);
+	LLVMValueRef value = nullptr;
+	LLVMGetMDNodeOperands(v_md, &value);
+	return LLVMConstIntGetZExtValue(value);
+}
+
 gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile) {
 gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile) {
 	return lb_mem_zero_ptr_internal(p, ptr, LLVMConstInt(lb_type(p->module, t_uint), len, false), alignment, is_volatile);
 	return lb_mem_zero_ptr_internal(p, ptr, LLVMConstInt(lb_type(p->module, t_uint), len, false), alignment, is_volatile);
 }
 }
@@ -1148,7 +1171,15 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 
 
 	GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
 	GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
 	
 	
-	return lb_emit_struct_ep_internal(p, s, index, result_type);
+	lbValue gep = lb_emit_struct_ep_internal(p, s, index, result_type);
+
+	Type *bt = base_type(t);
+	if (bt->kind == Type_Struct && bt->Struct.is_packed) {
+		lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
+		GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+	}
+
+	return gep;
 }
 }
 
 
 gb_internal lbValue lb_emit_tuple_ev(lbProcedure *p, lbValue value, i32 index) {
 gb_internal lbValue lb_emit_tuple_ev(lbProcedure *p, lbValue value, i32 index) {
@@ -1708,7 +1739,8 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu
 	if (is_possible) {
 	if (is_possible) {
 		switch (build_context.metrics.arch) {
 		switch (build_context.metrics.arch) {
 		case TargetArch_amd64:
 		case TargetArch_amd64:
-			if (type_size_of(t) == 2) {
+			// NOTE: using the intrinsic when not supported causes slow codegen (See #2928).
+			if (type_size_of(t) == 2 || !check_target_feature_is_enabled(str_lit("fma"), nullptr)) {
 				is_possible = false;
 				is_possible = false;
 			}
 			}
 			break;
 			break;

+ 75 - 11
src/main.cpp

@@ -86,11 +86,8 @@ gb_global Timings global_timings = {0};
 
 
 #if defined(GB_SYSTEM_OSX)
 #if defined(GB_SYSTEM_OSX)
 	#include <llvm/Config/llvm-config.h>
 	#include <llvm/Config/llvm-config.h>
-	#if LLVM_VERSION_MAJOR < 11
-	#error LLVM Version 11+ is required => "brew install llvm@11"
-	#endif
-	#if (LLVM_VERSION_MAJOR > 14 && LLVM_VERSION_MAJOR < 17) || LLVM_VERSION_MAJOR > 17
-	#error LLVM Version 11..=14 or =17 is required => "brew install llvm@14"
+	#if LLVM_VERSION_MAJOR < 11 || (LLVM_VERSION_MAJOR > 14 && LLVM_VERSION_MAJOR < 17) || LLVM_VERSION_MAJOR > 18
+	#error LLVM Version 11..=14 or =18 is required => "brew install llvm@14"
 	#endif
 	#endif
 #endif
 #endif
 
 
@@ -272,6 +269,7 @@ enum BuildFlagKind {
 	BuildFlag_ExtraAssemblerFlags,
 	BuildFlag_ExtraAssemblerFlags,
 	BuildFlag_Microarch,
 	BuildFlag_Microarch,
 	BuildFlag_TargetFeatures,
 	BuildFlag_TargetFeatures,
+	BuildFlag_StrictTargetFeatures,
 	BuildFlag_MinimumOSVersion,
 	BuildFlag_MinimumOSVersion,
 	BuildFlag_NoThreadLocal,
 	BuildFlag_NoThreadLocal,
 
 
@@ -467,6 +465,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags,     str_lit("extra-assembler-flags"),     BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags,     str_lit("extra-assembler-flags"),     BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_Microarch,               str_lit("microarch"),                 BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_Microarch,               str_lit("microarch"),                 BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_TargetFeatures,          str_lit("target-features"),           BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_TargetFeatures,          str_lit("target-features"),           BuildFlagParam_String,  Command__does_build);
+	add_flag(&build_flags, BuildFlag_StrictTargetFeatures,    str_lit("strict-target-features"),    BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_MinimumOSVersion,        str_lit("minimum-os-version"),        BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_MinimumOSVersion,        str_lit("minimum-os-version"),        BuildFlagParam_String,  Command__does_build);
 
 
 	add_flag(&build_flags, BuildFlag_RelocMode,               str_lit("reloc-mode"),                BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_RelocMode,               str_lit("reloc-mode"),                BuildFlagParam_String,  Command__does_build);
@@ -1083,6 +1082,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							string_to_lower(&build_context.target_features_string);
 							string_to_lower(&build_context.target_features_string);
 							break;
 							break;
 						}
 						}
+					    case BuildFlag_StrictTargetFeatures:
+						    build_context.strict_target_features = true;
+						    break;
 						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;
@@ -1981,7 +1983,20 @@ gb_internal void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Examples:");
 		print_usage_line(2, "Examples:");
 		print_usage_line(3, "-microarch:sandybridge");
 		print_usage_line(3, "-microarch:sandybridge");
 		print_usage_line(3, "-microarch:native");
 		print_usage_line(3, "-microarch:native");
-		print_usage_line(3, "-microarch:? for a list");
+		print_usage_line(3, "-microarch:\"?\" for a list");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-target-features:<string>");
+		print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
+		print_usage_line(2, "Examples:");
+		print_usage_line(3, "-target-features:atomics");
+		print_usage_line(3, "-target-features:\"sse2,aes\"");
+		print_usage_line(3, "-target-features:\"?\" for a list");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-strict-target-features");
+		print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\").");
+		print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features.");
 		print_usage_line(0, "");
 		print_usage_line(0, "");
 
 
 		print_usage_line(1, "-reloc-mode:<string>");
 		print_usage_line(1, "-reloc-mode:<string>");
@@ -2663,7 +2678,7 @@ int main(int arg_count, char const **arg_ptr) {
 
 
 	// Check chosen microarchitecture. If not found or ?, print list.
 	// Check chosen microarchitecture. If not found or ?, print list.
 	bool print_microarch_list = true;
 	bool print_microarch_list = true;
-	if (build_context.microarch.len == 0) {
+	if (build_context.microarch.len == 0 || build_context.microarch == str_lit("native")) {
 		// Autodetect, no need to print list.
 		// Autodetect, no need to print list.
 		print_microarch_list = false;
 		print_microarch_list = false;
 	} else {
 	} else {
@@ -2680,6 +2695,11 @@ int main(int arg_count, char const **arg_ptr) {
 		}
 		}
 	}
 	}
 
 
+	// Set and check build paths...
+	if (!init_build_paths(init_filename)) {
+		return 1;
+	}
+
 	String default_march = get_default_microarchitecture();
 	String default_march = get_default_microarchitecture();
 	if (print_microarch_list) {
 	if (print_microarch_list) {
 		if (build_context.microarch != "?") {
 		if (build_context.microarch != "?") {
@@ -2703,13 +2723,57 @@ int main(int arg_count, char const **arg_ptr) {
 		return 0;
 		return 0;
 	}
 	}
 
 
-	// Set and check build paths...
-	if (!init_build_paths(init_filename)) {
-		return 1;
+	String march = get_final_microarchitecture();
+	String default_features = get_default_features();
+	{
+		String_Iterator it = {default_features, 0};
+		for (;;) {
+			String str = string_split_iterator(&it, ',');
+			if (str == "") break;
+			string_set_add(&build_context.target_features_set, str);
+		}
+	}
+
+	if (build_context.target_features_string.len != 0) {
+		String_Iterator target_it = {build_context.target_features_string, 0};
+		for (;;) {
+			String item = string_split_iterator(&target_it, ',');
+			if (item == "") break;
+
+			String invalid;
+			if (!check_target_feature_is_valid_for_target_arch(item, &invalid) && item != str_lit("help")) {
+				if (item != str_lit("?")) {
+					gb_printf_err("Unkown target feature '%.*s'.\n", LIT(invalid));
+				}
+				gb_printf("Possible -target-features for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch]));
+				gb_printf("\n");
+
+				String feature_list = target_features_list[build_context.metrics.arch];
+				String_Iterator it = {feature_list, 0};
+				for (;;) {
+					String str = string_split_iterator(&it, ',');
+					if (str == "") break;
+					if (check_single_target_feature_is_valid(default_features, str)) {
+						if (has_ansi_terminal_colours()) {
+							gb_printf("\t%.*s\x1b[38;5;244m (implied by target microarch %.*s)\x1b[0m\n", LIT(str), LIT(march));
+						} else {
+							gb_printf("\t%.*s (implied by current microarch %.*s)\n", LIT(str), LIT(march));
+						}
+					} else {
+						gb_printf("\t%.*s\n", LIT(str));
+					}
+				}
+
+				return 1;
+			}
+
+			string_set_add(&build_context.target_features_set, item);
+		}
 	}
 	}
 
 
 	if (build_context.show_debug_messages) {
 	if (build_context.show_debug_messages) {
-		debugf("Selected microarch: %.*s\n", LIT(default_march));
+		debugf("Selected microarch: %.*s\n", LIT(march));
+		debugf("Default microarch features: %.*s\n", LIT(default_features));
 		for_array(i, build_context.build_paths) {
 		for_array(i, build_context.build_paths) {
 			String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
 			String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
 			debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
 			debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));

+ 6 - 2
src/parser.cpp

@@ -1,7 +1,7 @@
 #include "parser_pos.cpp"
 #include "parser_pos.cpp"
 
 
 gb_internal u64 ast_file_vet_flags(AstFile *f) {
 gb_internal u64 ast_file_vet_flags(AstFile *f) {
-	if (f->vet_flags_set) {
+	if (f != nullptr && f->vet_flags_set) {
 		return f->vet_flags;
 		return f->vet_flags;
 	}
 	}
 	return build_context.vet_flags;
 	return build_context.vet_flags;
@@ -3499,6 +3499,10 @@ gb_internal Ast *parse_type(AstFile *f) {
 		Token token = advance_token(f);
 		Token token = advance_token(f);
 		syntax_error(token, "Expected a type");
 		syntax_error(token, "Expected a type");
 		return ast_bad_expr(f, token, f->curr_token);
 		return ast_bad_expr(f, token, f->curr_token);
+	} else if (type->kind == Ast_ParenExpr &&
+	           unparen_expr(type) == nullptr) {
+		syntax_error(type, "Expected a type within the parentheses");
+		return ast_bad_expr(f, type->ParenExpr.open, type->ParenExpr.close);
 	}
 	}
 	return type;
 	return type;
 }
 }
@@ -5710,7 +5714,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
 		//                 working directory of the exe to the library search paths.
 		//                 working directory of the exe to the library search paths.
 		//                 Static libraries can be linked directly with the full pathname
 		//                 Static libraries can be linked directly with the full pathname
 		//
 		//
-		if (node->kind == Ast_ForeignImportDecl && string_ends_with(file_str, str_lit(".so"))) {
+		if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) {
 			*path = file_str;
 			*path = file_str;
 			return true;
 			return true;
 		}
 		}

+ 19 - 0
src/string.cpp

@@ -328,6 +328,25 @@ gb_internal bool string_contains_char(String const &s, u8 c) {
 	return false;
 	return false;
 }
 }
 
 
+gb_internal bool string_contains_string(String const &haystack, String const &needle) {
+    if (needle.len == 0) return true;
+    if (needle.len > haystack.len) return false;
+
+    for (isize i = 0; i <= haystack.len - needle.len; i++) {
+        bool found = true;
+        for (isize j = 0; j < needle.len; j++) {
+            if (haystack[i + j] != needle[j]) {
+                found = false;
+                break;
+            }
+        }
+        if (found) {
+            return true;
+        }
+    }
+    return false;
+}
+
 gb_internal String filename_from_path(String s) {
 gb_internal String filename_from_path(String s) {
 	isize i = string_extension_position(s);
 	isize i = string_extension_position(s);
 	if (i >= 0) {
 	if (i >= 0) {

+ 292 - 0
src/string_map.cpp

@@ -24,6 +24,296 @@ gb_internal gb_inline StringHashKey string_hash_string(String const &s) {
 	return hash_key;
 	return hash_key;
 }
 }
 
 
+
+#if 1 /* old string map */
+
+template <typename T>
+struct StringMapEntry {
+	String        key;
+	u32           hash;
+	MapIndex      next;
+	T             value;
+};
+
+template <typename T>
+struct StringMap {
+	MapIndex *         hashes;
+	usize              hashes_count;
+	StringMapEntry<T> *entries;
+	u32                count;
+	u32                entries_capacity;
+};
+
+
+template <typename T> gb_internal void string_map_init    (StringMap<T> *h, usize capacity = 16);
+template <typename T> gb_internal void string_map_destroy (StringMap<T> *h);
+
+template <typename T> gb_internal T *  string_map_get     (StringMap<T> *h, char const *key);
+template <typename T> gb_internal T *  string_map_get     (StringMap<T> *h, String const &key);
+template <typename T> gb_internal T *  string_map_get     (StringMap<T> *h, StringHashKey const &key);
+
+template <typename T> gb_internal T &  string_map_must_get(StringMap<T> *h, char const *key);
+template <typename T> gb_internal T &  string_map_must_get(StringMap<T> *h, String const &key);
+template <typename T> gb_internal T &  string_map_must_get(StringMap<T> *h, StringHashKey const &key);
+
+template <typename T> gb_internal void string_map_set     (StringMap<T> *h, char const *key,   T const &value);
+template <typename T> gb_internal void string_map_set     (StringMap<T> *h, String const &key, T const &value);
+template <typename T> gb_internal void string_map_set     (StringMap<T> *h, StringHashKey const &key, T const &value);
+
+// template <typename T> gb_internal void string_map_remove  (StringMap<T> *h, StringHashKey const &key);
+template <typename T> gb_internal void string_map_clear   (StringMap<T> *h);
+template <typename T> gb_internal void string_map_grow    (StringMap<T> *h);
+template <typename T> gb_internal void string_map_reserve (StringMap<T> *h, usize new_count);
+
+gb_internal gbAllocator string_map_allocator(void) {
+	return heap_allocator();
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_init(StringMap<T> *h, usize capacity) {
+	capacity = next_pow2_isize(capacity);
+	string_map_reserve(h, capacity);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_destroy(StringMap<T> *h) {
+	gb_free(string_map_allocator(), h->hashes);
+	gb_free(string_map_allocator(), h->entries);
+}
+
+
+template <typename T>
+gb_internal void string_map__resize_hashes(StringMap<T> *h, usize count) {
+	h->hashes_count = cast(u32)resize_array_raw(&h->hashes, string_map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE);
+}
+
+
+template <typename T>
+gb_internal void string_map__reserve_entries(StringMap<T> *h, usize capacity) {
+	h->entries_capacity = cast(u32)resize_array_raw(&h->entries, string_map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE);
+}
+
+
+template <typename T>
+gb_internal MapIndex string_map__add_entry(StringMap<T> *h, u32 hash, String const &key) {
+	StringMapEntry<T> e = {};
+	e.key = key;
+	e.hash = hash;
+	e.next = MAP_SENTINEL;
+	if (h->count+1 >= h->entries_capacity) {
+		string_map__reserve_entries(h, gb_max(h->entries_capacity*2, 4));
+	}
+	h->entries[h->count++] = e;
+	return cast(MapIndex)(h->count-1);
+}
+
+template <typename T>
+gb_internal MapFindResult string_map__find(StringMap<T> *h, u32 hash, String const &key) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes_count != 0) {
+		fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+		fr.entry_index = h->hashes[fr.hash_index];
+		while (fr.entry_index != MAP_SENTINEL) {
+			auto *entry = &h->entries[fr.entry_index];
+			if (entry->hash == hash && entry->key == key) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = entry->next;
+		}
+	}
+	return fr;
+}
+
+template <typename T>
+gb_internal MapFindResult string_map__find_from_entry(StringMap<T> *h, StringMapEntry<T> *e) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes_count != 0) {
+		fr.hash_index  = cast(MapIndex)(e->hash & (h->hashes_count-1));
+		fr.entry_index = h->hashes[fr.hash_index];
+		while (fr.entry_index != MAP_SENTINEL) {
+			auto *entry = &h->entries[fr.entry_index];
+			if (entry == e) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = entry->next;
+		}
+	}
+	return fr;
+}
+
+template <typename T>
+gb_internal b32 string_map__full(StringMap<T> *h) {
+	return 0.75f * h->hashes_count <= h->count;
+}
+
+template <typename T>
+gb_inline void string_map_grow(StringMap<T> *h) {
+	isize new_count = gb_max(h->hashes_count<<1, 16);
+	string_map_reserve(h, new_count);
+}
+
+
+template <typename T>
+gb_internal void string_map_reset_entries(StringMap<T> *h) {
+	for (u32 i = 0; i < h->hashes_count; i++) {
+		h->hashes[i] = MAP_SENTINEL;
+	}
+	for (isize i = 0; i < h->count; i++) {
+		MapFindResult fr;
+		StringMapEntry<T> *e = &h->entries[i];
+		e->next = MAP_SENTINEL;
+		fr = string_map__find_from_entry(h, e);
+		if (fr.entry_prev == MAP_SENTINEL) {
+			h->hashes[fr.hash_index] = cast(MapIndex)i;
+		} else {
+			h->entries[fr.entry_prev].next = cast(MapIndex)i;
+		}
+	}
+}
+
+template <typename T>
+gb_internal void string_map_reserve(StringMap<T> *h, usize cap) {
+	if (h->count*2 < h->hashes_count) {
+		return;
+	}
+	string_map__reserve_entries(h, cap);
+	string_map__resize_hashes(h,   cap*2);
+	string_map_reset_entries(h);
+}
+
+template <typename T>
+gb_internal T *string_map_get(StringMap<T> *h, u32 hash, String const &key) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes_count != 0) {
+		fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+		fr.entry_index = h->hashes[fr.hash_index];
+		while (fr.entry_index != MAP_SENTINEL) {
+			auto *entry = &h->entries[fr.entry_index];
+			if (entry->hash == hash && entry->key == key) {
+				return &entry->value;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = entry->next;
+		}
+	}
+	return nullptr;
+}
+
+
+template <typename T>
+gb_internal gb_inline T *string_map_get(StringMap<T> *h, StringHashKey const &key) {
+	return string_map_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T *string_map_get(StringMap<T> *h, String const &key) {
+	return string_map_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal gb_inline T *string_map_get(StringMap<T> *h, char const *key) {
+	String k = make_string_c(key);
+	return string_map_get(h, string_hash(k), k);
+}
+
+template <typename T>
+gb_internal T &string_map_must_get(StringMap<T> *h, u32 hash, String const &key) {
+	isize index = string_map__find(h, hash, key).entry_index;
+	GB_ASSERT(index != MAP_SENTINEL);
+	return h->entries[index].value;
+}
+
+template <typename T>
+gb_internal T &string_map_must_get(StringMap<T> *h, StringHashKey const &key) {
+	return string_map_must_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T &string_map_must_get(StringMap<T> *h, String const &key) {
+	return string_map_must_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal gb_inline T &string_map_must_get(StringMap<T> *h, char const *key) {
+	String k = make_string_c(key);
+	return string_map_must_get(h, string_hash(k), k);
+}
+
+template <typename T>
+gb_internal void string_map_set(StringMap<T> *h, u32 hash, String const &key, T const &value) {
+	MapIndex index;
+	MapFindResult fr;
+	if (h->hashes_count == 0) {
+		string_map_grow(h);
+	}
+	fr = string_map__find(h, hash, key);
+	if (fr.entry_index != MAP_SENTINEL) {
+		index = fr.entry_index;
+	} else {
+		index = string_map__add_entry(h, hash, key);
+		if (fr.entry_prev != MAP_SENTINEL) {
+			h->entries[fr.entry_prev].next = index;
+		} else {
+			h->hashes[fr.hash_index] = index;
+		}
+	}
+	h->entries[index].value = value;
+
+	if (string_map__full(h)) {
+		string_map_grow(h);
+	}
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, String const &key, T const &value) {
+	string_map_set(h, string_hash_string(key), value);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, char const *key, T const &value) {
+	string_map_set(h, string_hash_string(make_string_c(key)), value);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, StringHashKey const &key, T const &value) {
+	string_map_set(h, key.hash, key.string, value);
+}
+
+
+template <typename T>
+gb_internal gb_inline void string_map_clear(StringMap<T> *h) {
+	h->count = 0;
+	for (u32 i = 0; i < h->hashes_count; i++) {
+		h->hashes[i] = MAP_SENTINEL;
+	}
+}
+
+
+
+template <typename T>
+gb_internal StringMapEntry<T> *begin(StringMap<T> &m) noexcept {
+	return m.entries;
+}
+template <typename T>
+gb_internal StringMapEntry<T> const *begin(StringMap<T> const &m) noexcept {
+	return m.entries;
+}
+
+
+template <typename T>
+gb_internal StringMapEntry<T> *end(StringMap<T> &m) noexcept {
+	return m.entries + m.count;
+}
+
+template <typename T>
+gb_internal StringMapEntry<T> const *end(StringMap<T> const &m) noexcept {
+	return m.entries + m.count;
+}
+
+#else /* new string map */
+
 template <typename T>
 template <typename T>
 struct StringMapEntry {
 struct StringMapEntry {
 	String key;
 	String key;
@@ -305,3 +595,5 @@ gb_internal StringMapIterator<T> const begin(StringMap<T> const &m) noexcept {
 	}
 	}
 	return StringMapIterator<T>{&m, index};
 	return StringMapIterator<T>{&m, index};
 }
 }
+
+#endif

+ 2 - 0
src/threading.cpp

@@ -492,6 +492,8 @@ gb_internal u32 thread_current_id(void) {
 	thread_id = gettid();
 	thread_id = gettid();
 #elif defined(GB_SYSTEM_HAIKU)
 #elif defined(GB_SYSTEM_HAIKU)
 	thread_id = find_thread(NULL);
 	thread_id = find_thread(NULL);
+#elif defined(GB_SYSTEM_FREEBSD)
+	thread_id = pthread_getthreadid_np();
 #else
 #else
 	#error Unsupported architecture for thread_current_id()
 	#error Unsupported architecture for thread_current_id()
 #endif
 #endif

+ 26 - 0
src/types.cpp

@@ -184,6 +184,8 @@ struct TypeProc {
 	isize    specialization_count;
 	isize    specialization_count;
 	ProcCallingConvention calling_convention;
 	ProcCallingConvention calling_convention;
 	i32      variadic_index;
 	i32      variadic_index;
+	String   require_target_feature;
+	String   enable_target_feature;
 	// TODO(bill): Make this a flag set rather than bools
 	// TODO(bill): Make this a flag set rather than bools
 	bool     variadic;
 	bool     variadic;
 	bool     require_results;
 	bool     require_results;
@@ -2991,7 +2993,22 @@ gb_internal Type *union_tag_type(Type *u) {
 	return t_uint;
 	return t_uint;
 }
 }
 
 
+gb_internal int matched_target_features(TypeProc *t) {
+	if (t->require_target_feature.len == 0) {
+		return 0;
+	}
 
 
+	int matches = 0;
+	String_Iterator it = {t->require_target_feature, 0};
+	for (;;) {
+		String str = string_split_iterator(&it, ',');
+		if (str == "") break;
+		if (check_target_feature_is_valid_for_target_arch(str, nullptr)) {
+			matches += 1;
+		}
+	}
+	return matches;
+}
 
 
 enum ProcTypeOverloadKind {
 enum ProcTypeOverloadKind {
 	ProcOverload_Identical, // The types are identical
 	ProcOverload_Identical, // The types are identical
@@ -3003,6 +3020,7 @@ enum ProcTypeOverloadKind {
 	ProcOverload_ResultCount,
 	ProcOverload_ResultCount,
 	ProcOverload_ResultTypes,
 	ProcOverload_ResultTypes,
 	ProcOverload_Polymorphic,
 	ProcOverload_Polymorphic,
+	ProcOverload_TargetFeatures,
 
 
 	ProcOverload_NotProcedure,
 	ProcOverload_NotProcedure,
 
 
@@ -3060,6 +3078,10 @@ gb_internal ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y)
 		}
 		}
 	}
 	}
 
 
+	if (matched_target_features(&px) != matched_target_features(&py)) {
+		return ProcOverload_TargetFeatures;
+	}
+
 	if (px.params != nullptr && py.params != nullptr) {
 	if (px.params != nullptr && py.params != nullptr) {
 		Entity *ex = px.params->Tuple.variables[0];
 		Entity *ex = px.params->Tuple.variables[0];
 		Entity *ey = py.params->Tuple.variables[0];
 		Entity *ey = py.params->Tuple.variables[0];
@@ -3245,6 +3267,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
 			}
 			}
 		}
 		}
 
 
+		if (is_type_polymorphic(type)) {
+			// NOTE(bill): A polymorphic struct has no fields, this only hits in the case of an error
+			return sel;
+		}
 		wait_signal_until_available(&type->Struct.fields_wait_signal);
 		wait_signal_until_available(&type->Struct.fields_wait_signal);
 		isize field_count = type->Struct.fields.count;
 		isize field_count = type->Struct.fields.count;
 		if (field_count != 0) for_array(i, type->Struct.fields) {
 		if (field_count != 0) for_array(i, type->Struct.fields) {

Some files were not shown because too many files changed in this diff