Selaa lähdekoodia

Merge branch 'master' into windows-llvm-13.0.0

gingerBill 2 vuotta sitten
vanhempi
commit
040dbca42d
100 muutettua tiedostoa jossa 7076 lisäystä ja 5434 poistoa
  1. 2 2
      .github/workflows/stale.yml
  2. 3 1
      build.bat
  3. 1 1
      build_odin.sh
  4. 1 1
      core/c/libc/threads.odin
  5. 46 1
      core/container/small_array/small_array.odin
  6. 4 0
      core/encoding/json/marshal.odin
  7. 21 72
      core/fmt/fmt.odin
  8. 1 1
      core/hash/xxhash/xxhash_3.odin
  9. 1 1
      core/math/linalg/extended.odin
  10. 1 1
      core/mem/allocators.odin
  11. 5 5
      core/mem/mem.odin
  12. 0 13
      core/mem/raw.odin
  13. 42 19
      core/mem/virtual/arena.odin
  14. 5 1
      core/mem/virtual/virtual.odin
  15. 2 1
      core/os/dir_darwin.odin
  16. 9 5
      core/os/os.odin
  17. 32 6
      core/os/os_darwin.odin
  18. 16 1
      core/os/os_freebsd.odin
  19. 7 1
      core/os/os_linux.odin
  20. 10 2
      core/os/os_openbsd.odin
  21. 4 1
      core/os/os_wasi.odin
  22. 23 1
      core/os/os_windows.odin
  23. 2 0
      core/reflect/iterator.odin
  24. 61 39
      core/reflect/reflect.odin
  25. 33 3
      core/reflect/types.odin
  26. 6 0
      core/runtime/core.odin
  27. 1 1
      core/runtime/core_builtin.odin
  28. 2 2
      core/runtime/core_builtin_soa.odin
  29. 2 2
      core/runtime/default_temporary_allocator.odin
  30. 67 0
      core/runtime/print.odin
  31. 1 1
      core/slice/map.odin
  32. 17 15
      core/slice/ptr.odin
  33. 6 5
      core/slice/slice.odin
  34. 121 21
      core/strconv/decimal/decimal.odin
  35. 5 5
      core/strconv/generic_float.odin
  36. 0 5
      core/strconv/integers.odin
  37. 1 2
      core/strconv/strconv.odin
  38. 95 30
      core/sync/extended.odin
  39. 7 7
      core/sync/futex_darwin.odin
  40. 8 8
      core/sync/futex_freebsd.odin
  41. 10 10
      core/sync/futex_linux.odin
  42. 8 8
      core/sync/futex_openbsd.odin
  43. 4 4
      core/sync/futex_wasm.odin
  44. 4 4
      core/sync/futex_windows.odin
  45. 44 28
      core/sync/primitives.odin
  46. 26 26
      core/sync/primitives_atomic.odin
  47. 78 19
      core/sync/primitives_internal.odin
  48. 13 13
      core/sync/primitives_windows.odin
  49. 38 0
      core/sys/windows/dwmapi.odin
  50. 3 0
      core/sys/windows/gdi32.odin
  51. 48 0
      core/sys/windows/kernel32.odin
  52. 33 0
      core/sys/windows/shell32.odin
  53. 20 0
      core/sys/windows/types.odin
  54. 38 0
      core/sys/windows/user32.odin
  55. 12 0
      core/sys/windows/ux_theme.odin
  56. 959 0
      core/text/match/strlib.odin
  57. 76 76
      src/array.cpp
  58. 128 104
      src/big_int.cpp
  59. 6 6
      src/bug_report.cpp
  60. 99 68
      src/build_settings.cpp
  61. 67 43
      src/check_builtin.cpp
  62. 93 88
      src/check_decl.cpp
  63. 200 184
      src/check_expr.cpp
  64. 556 305
      src/check_stmt.cpp
  65. 144 132
      src/check_type.cpp
  66. 235 256
      src/checker.cpp
  67. 104 87
      src/checker.hpp
  68. 73 49
      src/common.cpp
  69. 149 76
      src/common_memory.cpp
  70. 24 38
      src/docs.cpp
  71. 2 2
      src/docs_format.cpp
  72. 58 62
      src/docs_writer.cpp
  73. 30 36
      src/entity.cpp
  74. 31 35
      src/error.cpp
  75. 85 95
      src/exact_value.cpp
  76. 0 2
      src/gb/gb.h
  77. 220 107
      src/llvm_abi.cpp
  78. 622 340
      src/llvm_backend.cpp
  79. 169 150
      src/llvm_backend.hpp
  80. 30 30
      src/llvm_backend_const.cpp
  81. 33 34
      src/llvm_backend_debug.cpp
  82. 62 60
      src/llvm_backend_expr.cpp
  83. 187 215
      src/llvm_backend_general.cpp
  84. 29 26
      src/llvm_backend_opt.cpp
  85. 258 132
      src/llvm_backend_proc.cpp
  86. 180 185
      src/llvm_backend_stmt.cpp
  87. 15 14
      src/llvm_backend_type.cpp
  88. 180 140
      src/llvm_backend_utility.cpp
  89. 124 204
      src/main.cpp
  90. 20 24
      src/microsoft_craziness.h
  91. 207 218
      src/parser.cpp
  92. 59 51
      src/parser.hpp
  93. 2 4
      src/parser_pos.cpp
  94. 26 22
      src/path.cpp
  95. 7 7
      src/priority_queue.cpp
  96. 223 102
      src/ptr_map.cpp
  97. 142 173
      src/ptr_set.cpp
  98. 0 1030
      src/query_data.cpp
  99. 99 14
      src/queue.cpp
  100. 13 13
      src/range_cache.cpp

+ 2 - 2
.github/workflows/stale.yml

@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Close Stale Issues
-        uses: actions/stale@v4.1.0
+        uses: actions/stale@v7.0.0
         with:
 #          stale-issue-message: |
 #            Hello!
@@ -36,7 +36,7 @@ jobs:
 #           The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
 
           days-before-stale: 120
-          days-before-close: 30
+          days-before-close: -1
           exempt-draft-pr: true
           ascending: true
           operations-per-run: 1000

+ 3 - 1
build.bat

@@ -62,12 +62,14 @@ if %release_mode% EQU 0 ( rem Debug
 set compiler_warnings= ^
 	-W4 -WX ^
 	-wd4100 -wd4101 -wd4127 -wd4146 ^
+	-wd4505 ^
 	-wd4456 -wd4457
 
 set compiler_includes= ^
 	/Isrc\
 set libs= ^
 	kernel32.lib ^
+	Synchronization.lib ^
 	bin\llvm\windows\LLVM-C.lib
 
 set linker_flags= -incremental:no -opt:ref -subsystem:console
@@ -94,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
 
 del *.obj > NUL 2> NUL
 
-:end_of_build
+:end_of_build

+ 1 - 1
build_odin.sh

@@ -50,7 +50,7 @@ config_darwin() {
 		panic "Requirement: llvm-config must be base version smaller than 15"
 	fi
 
-	LDFLAGS="$LDFLAGS -liconv -ldl"
+	LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
 	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
 	LDFLAGS="$LDFLAGS -lLLVM-C"
 }

+ 1 - 1
core/c/libc/threads.odin

@@ -108,7 +108,7 @@ when ODIN_OS == .Linux {
 		cnd_destroy   :: proc(cond: ^cnd_t) ---
 		cnd_init      :: proc(cond: ^cnd_t) -> int ---
 		cnd_signal    :: proc(cond: ^cnd_t) -> int ---
-		cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
+		cnd_timedwait :: proc(cond: ^cnd_t, mtx: ^mtx_t, ts: ^timespec) -> int ---
 		cnd_wait      :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
 		
 		// 7.26.4 Mutex functions

+ 46 - 1
core/container/small_array/small_array.odin

@@ -1,6 +1,8 @@
 package container_small_array
 
 import "core:builtin"
+import "core:runtime"
+_ :: runtime
 
 Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
 	data: [N]T,
@@ -32,6 +34,20 @@ get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
 	return &a.data[index]
 }
 
+get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
+	if index < 0 || index >= a.len {
+		return {}, false
+	}
+	return a.data[index], true
+}
+
+get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
+	if index < 0 || index >= a.len {
+		return {}, false
+	}
+	return &a.data[index], true
+}
+
 set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
 	a.data[index] = item
 }
@@ -93,7 +109,7 @@ pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, o
 		copy(s[:], s[1:])
 		a.len -= 1
 		ok = true
-	} 
+	}
 	return
 }
 
@@ -102,6 +118,23 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
 	a.len -= count
 }
 
+ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
+    runtime.bounds_check_error_loc(loc, index, a.len)
+    if index+1 < a.len {
+		copy(a.data[index:], a.data[index+1:])
+	}
+	a.len -= 1
+}
+
+unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
+    runtime.bounds_check_error_loc(loc, index, a.len)
+	n := a.len-1
+	if index != n {
+		a.data[index] = a.data[n]
+	}
+    a.len -= 1
+}
+
 clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
 	resize(a, 0)
 }
@@ -111,6 +144,18 @@ push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
 	a.len += n
 }
 
+inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
+	if a.len < cap(a^) && index >= 0 && index <= len(a^) {
+		a.len += 1
+		for i := a.len - 1; i >= index + 1; i -= 1 {
+			a.data[i] = a.data[i - 1]
+		}
+		a.data[index] = item
+		return true
+	}
+	return false
+}
+
 append_elem  :: push_back
 append_elems :: push_back_elems
 push   :: proc{push_back, push_back_elems}

+ 4 - 0
core/encoding/json/marshal.odin

@@ -262,10 +262,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 			}
 			map_cap := uintptr(runtime.map_cap(m^))
 			ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
+
+			i := 0
 			for bucket_index in 0..<map_cap {
 				if !runtime.map_hash_is_valid(hs[bucket_index]) {
 					continue
 				}
+				opt_write_iteration(w, opt, i) or_return
+				i += 1
 
 				key   := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
 				value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))

+ 21 - 72
core/fmt/fmt.odin

@@ -547,7 +547,7 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
 	is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' }
 
 	new_offset = offset
-	for new_offset <= len(s) {
+	for new_offset < len(s) {
 		c := s[new_offset]
 		if !is_digit(c) {
 			break
@@ -678,7 +678,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
 		}
 	} else if fi.zero && fi.width_set {
 		prec = fi.width
-		if neg || fi.plus || fi.space {
+		if neg || fi.plus {
 			// There needs to be space for the "sign"
 			prec -= 1
 		}
@@ -697,7 +697,6 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
 	flags: strconv.Int_Flags
 	if fi.hash && !fi.zero { flags |= {.Prefix} }
 	if fi.plus             { flags |= {.Plus}   }
-	if fi.space            { flags |= {.Space}  }
 	s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
 
 	if fi.hash && fi.zero && fi.indent == 0 {
@@ -744,7 +743,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
 		}
 	} else if fi.zero && fi.width_set {
 		prec = fi.width
-		if neg || fi.plus || fi.space {
+		if neg || fi.plus {
 			// There needs to be space for the "sign"
 			prec -= 1
 		}
@@ -763,7 +762,6 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
 	flags: strconv.Int_Flags
 	if fi.hash && !fi.zero { flags |= {.Prefix} }
 	if fi.plus             { flags |= {.Plus}   }
-	if fi.space            { flags |= {.Space}  }
 	s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
 
 	if fi.hash && fi.zero && fi.indent == 0 {
@@ -867,79 +865,30 @@ _pad :: proc(fi: ^Info, s: string) {
 	}
 }
 
-fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
-	switch verb {
-	case 'f', 'F', 'g', 'G', 'v':
-		prec: int = 3
-		if fi.prec_set {
-			prec = fi.prec
-		}
-		buf: [386]byte
+_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
+	prec := fi.prec if fi.prec_set else 3
+	buf: [386]byte
 
-		str := strconv.append_float(buf[1:], v, 'f', prec, bit_size)
-		b := buf[:len(str)+1]
-		if b[1] == '+' || b[1] == '-' {
-			b = b[1:]
-		} else {
-			b[0] = '+'
-		}
-
-		if fi.space && !fi.plus && b[0] == '+' {
-			b[0] = ' '
-		}
+	// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
+	str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
 
-		if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
-			io.write_string(fi.writer, string(b), &fi.n)
-			return
+	if !fi.plus {
+		// Strip sign from "+<value>" but not "+Inf".
+		if str[0] == '+' && str[1] != 'I' {
+			str = str[1:] 
 		}
+	}
 
-		if fi.plus || b[0] != '+' {
-			if fi.zero && fi.width_set && fi.width > len(b) {
-				io.write_byte(fi.writer, b[0], &fi.n)
-				fmt_write_padding(fi, fi.width - len(b))
-				io.write_string(fi.writer, string(b[1:]), &fi.n)
-			} else {
-				_pad(fi, string(b))
-			}
-		} else {
-			_pad(fi, string(b[1:]))
-		}
+	_pad(fi, str)
+}
 
+fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
+	switch verb {
+	case 'f', 'F', 'g', 'G', 'v':
+		_fmt_float_as(fi, v, bit_size, verb, 'f')
 	case 'e', 'E':
-		prec: int = 3
-		if fi.prec_set {
-			prec = fi.prec
-		}
-		buf: [386]byte
-
-		str := strconv.append_float(buf[1:], v, 'e', prec, bit_size)
-		b := buf[:len(str)+1]
-		if b[1] == '+' || b[1] == '-' {
-			b = b[1:]
-		} else {
-			b[0] = '+'
-		}
-
-		if fi.space && !fi.plus && b[0] == '+' {
-			b[0] = ' '
-		}
-
-		if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
-			io.write_string(fi.writer, string(b), &fi.n)
-			return
-		}
-
-		if fi.plus || str[0] != '+' {
-			if fi.zero && fi.width_set && fi.width > len(b) {
-				io.write_byte(fi.writer, b[0], &fi.n)
-				fmt_write_padding(fi, fi.width - len(b))
-				io.write_string(fi.writer, string(b[1:]), &fi.n)
-			} else {
-				_pad(fi, string(b))
-			}
-		} else {
-			_pad(fi, string(b[1:]))
-		}
+		// BUG(): "%.3e" returns "3.000e+00"
+		_fmt_float_as(fi, v, bit_size, verb, 'e')
 
 	case 'h', 'H':
 		prev_fi := fi^

+ 1 - 1
core/hash/xxhash/xxhash_3.odin

@@ -118,7 +118,7 @@ XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_
 }
 
 @(optimization_mode="speed")
-XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, auto_cast shift: uint) -> (res: xxh_u64) {
+XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
 	return v ~ (v >> shift)
 }
 

+ 1 - 1
core/math/linalg/extended.odin

@@ -531,7 +531,7 @@ not_equal          :: proc{not_equal_single, not_equal_array}
 
 any :: proc(x: $A/[$N]bool) -> (out: bool) {
 	for e in x {
-		if x {
+		if e {
 			return true
 		}
 	}

+ 1 - 1
core/mem/allocators.odin

@@ -153,7 +153,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	s := (^Scratch_Allocator)(allocator_data)
 
 	if s.data == nil {
-		DEFAULT_BACKING_SIZE :: 1<<22
+		DEFAULT_BACKING_SIZE :: 4 * Megabyte
 		if !(context.allocator.procedure != scratch_allocator_proc &&
 		     context.allocator.data != allocator_data) {
 			panic("cyclic initialization of the scratch allocator with itself")

+ 5 - 5
core/mem/mem.odin

@@ -3,11 +3,11 @@ package mem
 import "core:runtime"
 import "core:intrinsics"
 
-Byte     :: 1
-Kilobyte :: 1024 * Byte
-Megabyte :: 1024 * Kilobyte
-Gigabyte :: 1024 * Megabyte
-Terabyte :: 1024 * Gigabyte
+Byte     :: runtime.Byte
+Kilobyte :: runtime.Kilobyte
+Megabyte :: runtime.Megabyte
+Gigabyte :: runtime.Gigabyte
+Terabyte :: runtime.Terabyte
 
 set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
 	return runtime.memset(data, i32(value), len)

+ 0 - 13
core/mem/raw.odin

@@ -23,16 +23,3 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
 }
 
 raw_data :: builtin.raw_data
-
-
-Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
-	hash:  uintptr,
-	next:  int,
-	key:   Key,
-	value: Value,	
-}
-
-Poly_Raw_Map :: struct($Key, $Value: typeid) {
-	hashes:  []int,
-	entries: [dynamic]Poly_Raw_Map_Entry(Key, Value),
-}

+ 42 - 19
core/mem/virtual/arena.odin

@@ -1,6 +1,7 @@
 package mem_virtual
 
 import "core:mem"
+import "core:sync"
 
 Arena_Kind :: enum uint {
 	Growing = 0, // Chained memory blocks (singly linked list).
@@ -15,15 +16,16 @@ Arena :: struct {
 	total_reserved:     uint,
 	minimum_block_size: uint,
 	temp_count:         uint,
+	mutex:              sync.Mutex,
 }
 
 
 // 1 MiB should be enough to start with
-DEFAULT_ARENA_STATIC_COMMIT_SIZE         :: 1<<20
+DEFAULT_ARENA_STATIC_COMMIT_SIZE         :: mem.Megabyte
 DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
 
 // 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
-DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
+DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 else 128 * mem.Megabyte
 
 
 
@@ -78,6 +80,8 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 		return nil, nil
 	}
 
+	sync.mutex_guard(&arena.mutex)
+
 	switch arena.kind {
 	case .Growing:
 		if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
@@ -116,6 +120,8 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 }
 
 arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
+	sync.mutex_guard(&arena.mutex)
+
 	if arena.curr_block != nil {
 		assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
 
@@ -135,6 +141,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
 }
 
 arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
+	sync.mutex_guard(&arena.mutex)
 	if free_block := arena.curr_block; free_block != nil {
 		assert(arena.kind == .Growing, "expected a .Growing arena", loc)
 		arena.curr_block = free_block.prev
@@ -145,6 +152,7 @@ arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_locat
 arena_free_all :: proc(arena: ^Arena) {
 	switch arena.kind {
 	case .Growing:
+		sync.mutex_guard(&arena.mutex)
 		for arena.curr_block != nil {
 			arena_growing_free_last_memory_block(arena)
 		}
@@ -287,6 +295,8 @@ Arena_Temp :: struct {
 @(require_results)
 arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
 	assert(arena != nil, "nil arena", loc)
+	sync.mutex_guard(&arena.mutex)
+
 	temp.arena = arena
 	temp.block = arena.curr_block
 	if arena.curr_block != nil {
@@ -299,28 +309,41 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena
 arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
 	assert(temp.arena != nil, "nil arena", loc)
 	arena := temp.arena
+	sync.mutex_guard(&arena.mutex)
+
+	if temp.block != nil {
+		memory_block_found := false
+		for block := arena.curr_block; block != nil; block = block.prev {
+			if block == temp.block {
+				memory_block_found = true
+				break
+			}
+		}
+		if !memory_block_found {
+			assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
+		}
 
-	memory_block_found := false
-	for block := arena.curr_block; block != nil; block = block.prev {
-		if block == temp.block {
-			memory_block_found = true
-			break
+		for arena.curr_block != temp.block {
+			arena_growing_free_last_memory_block(arena)
 		}
-	}
-	if !memory_block_found {
-		assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
-	}
 
-	for arena.curr_block != temp.block {
-		arena_growing_free_last_memory_block(arena)
+		if block := arena.curr_block; block != nil {
+			assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
+			amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
+			mem.zero_slice(block.base[temp.used:][:amount_to_zero])
+			block.used = temp.used
+		}
 	}
 
-	if block := arena.curr_block; block != nil {
-		assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
-		amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
-		mem.zero_slice(block.base[temp.used:][:amount_to_zero])
-		block.used = temp.used
-	}
+	assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
+	arena.temp_count -= 1
+}
+
+// Ignore the use of a `arena_temp_begin` entirely
+arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
+	assert(temp.arena != nil, "nil arena", loc)
+	arena := temp.arena
+	sync.mutex_guard(&arena.mutex)
 
 	assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
 	arena.temp_count -= 1

+ 5 - 1
core/mem/virtual/virtual.odin

@@ -7,6 +7,7 @@ DEFAULT_PAGE_SIZE := uint(4096)
 
 Allocator_Error :: mem.Allocator_Error
 
+@(require_results)
 reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	return _reserve(size)
 }
@@ -15,6 +16,7 @@ commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	return _commit(data, size)
 }
 
+@(require_results)
 reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	data = reserve(size) or_return
 	commit(raw_data(data), size) or_return
@@ -57,6 +59,7 @@ Memory_Block_Flag :: enum u32 {
 Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
 
 
+@(require_results)
 memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
 	align_formula :: proc "contextless" (size, align: uint) -> uint {
 		result := size + align-1
@@ -100,6 +103,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 	return &pmblock.block, nil
 }
 
+@(require_results)
 alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
 	calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
 		alignment_offset := uint(0)
@@ -160,7 +164,7 @@ memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
 
 
 
-@(private)
+@(private, require_results)
 safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
 	z, did_overflow := intrinsics.overflow_add(x, y)
 	return z, !did_overflow

+ 2 - 1
core/os/dir_darwin.odin

@@ -14,11 +14,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
 
 	dirpath: string
 	dirpath, err = absolute_path_from_handle(fd)
-
 	if err != ERROR_NONE {
 		return
 	}
 
+	defer delete(dirpath)
+
 	n := n
 	size := n
 	if n <= 0 {

+ 9 - 5
core/os/os.odin

@@ -93,7 +93,7 @@ file_size_from_path :: proc(path: string) -> i64 {
 	return length
 }
 
-read_entire_file_from_filename :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
+read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
 	context.allocator = allocator
 
 	fd, err := open(name, O_RDONLY, 0)
@@ -102,10 +102,10 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
 	}
 	defer close(fd)
 
-	return read_entire_file_from_handle(fd, allocator)
+	return read_entire_file_from_handle(fd, allocator, loc)
 }
 
-read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
+read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
 	context.allocator = allocator
 
 	length: i64
@@ -118,7 +118,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
 		return nil, true
 	}
 
-	data = make([]byte, int(length), allocator)
+	data = make([]byte, int(length), allocator, loc)
 	if data == nil {
 	    return nil, false
 	}
@@ -216,7 +216,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		}
 
 		new_memory = aligned_alloc(new_size, new_alignment, p) or_return
-		
+
 		// NOTE: heap_resize does not zero the new memory, so we do it
 		if new_size > old_size {
 			new_region := mem.raw_data(new_memory[old_size:])
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
 		data = nil,
 	}
 }
+
+processor_core_count :: proc() -> int {
+	return _processor_core_count()
+}

+ 32 - 6
core/os/os_darwin.odin

@@ -314,6 +314,7 @@ foreign libc {
 	@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
 
 	@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
+	@(link_name="sysctlbyname") _sysctlbyname    :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
 
 	@(link_name="exit")    _unix_exit :: proc(status: c.int) -> ! ---
 }
@@ -333,7 +334,7 @@ foreign dl {
 	@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
 }
 
-get_last_error :: proc() -> int {
+get_last_error :: proc "contextless" () -> int {
 	return __error()^
 }
 
@@ -342,21 +343,33 @@ get_last_error_string :: proc() -> string {
 }
 
 open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
+	isDir := is_dir_path(path)
+	flags := flags
+	if isDir {
+		/*
+			@INFO(Platin): To make it impossible to use the wrong flag for dir's 
+			               as you can't write to a dir only read which makes it fail to open
+		*/
+		flags = O_RDONLY
+	}
+
 	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 	handle := _unix_open(cstr, i32(flags), u16(mode))
 	if handle == -1 {
-		return INVALID_HANDLE, 1
+		return INVALID_HANDLE, cast(Errno)get_last_error()
 	}
 
-when  ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
-	if mode != 0 {
+	/*
+		@INFO(Platin): this is only done because O_CREATE for some reason fails to apply mode
+		               should not happen if the handle is a directory
+	*/
+	if mode != 0 && !isDir {
 		err := fchmod(handle, cast(u16)mode)
 		if err != 0 {
 			_unix_close(handle)
-			return INVALID_HANDLE, 1
+			return INVALID_HANDLE, cast(Errno)err
 		}
 	}
-}
 
 	return handle, 0
 }
@@ -688,6 +701,7 @@ get_current_directory :: proc() -> string {
 			return string(cwd)
 		}
 		if Errno(get_last_error()) != ERANGE {
+			delete(buf)
 			return ""
 		}
 		resize(&buf, len(buf)+page_size)
@@ -759,6 +773,18 @@ get_page_size :: proc() -> int {
 	return page_size
 }
 
+@(private)
+_processor_core_count :: proc() -> int {
+	count : int = 0
+	count_size := size_of(count)
+	if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
+		if count > 0 {
+			return count
+		}
+	}
+
+	return 1
+}
 
 _alloc_command_line_arguments :: proc() -> []string {
 	res := make([]string, len(runtime.args__))

+ 16 - 1
core/os/os_freebsd.odin

@@ -287,6 +287,7 @@ foreign libc {
 	
 	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
 	@(link_name="realpath")         _unix_realpath      :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+	@(link_name="sysctlbyname")     _sysctlbyname       :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
 
 	@(link_name="exit")             _unix_exit          :: proc(status: c.int) -> ! ---
 }
@@ -303,7 +304,7 @@ is_path_separator :: proc(r: rune) -> bool {
 	return r == '/'
 }
 
-get_last_error :: proc() -> int {
+get_last_error :: proc "contextless" () -> int {
 	return __errno_location()^
 }
 
@@ -650,6 +651,7 @@ get_current_directory :: proc() -> string {
 			return string(cwd)
 		}
 		if Errno(get_last_error()) != ERANGE {
+			delete(buf)
 			return ""
 		}
 		resize(&buf, len(buf)+page_size)
@@ -702,6 +704,19 @@ get_page_size :: proc() -> int {
 	return page_size
 }
 
+@(private)
+_processor_core_count :: proc() -> int {
+	count : int = 0
+	count_size := size_of(count)
+	if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
+		if count > 0 {
+			return count
+		}
+	}
+
+	return 1
+}
+
 
 _alloc_command_line_arguments :: proc() -> []string {
 	res := make([]string, len(runtime.args__))

+ 7 - 1
core/os/os_linux.odin

@@ -404,6 +404,7 @@ foreign libc {
 	@(link_name="__errno_location") __errno_location    :: proc() -> ^int ---
 
 	@(link_name="getpagesize")      _unix_getpagesize   :: proc() -> c.int ---
+	@(link_name="get_nprocs")       _unix_get_nprocs    :: proc() -> c.int ---
 	@(link_name="fdopendir")        _unix_fdopendir     :: proc(fd: Handle) -> Dir ---
 	@(link_name="closedir")         _unix_closedir      :: proc(dirp: Dir) -> c.int ---
 	@(link_name="rewinddir")        _unix_rewinddir     :: proc(dirp: Dir) ---
@@ -441,7 +442,7 @@ _get_errno :: proc(res: int) -> Errno {
 }
 
 // get errno from libc
-get_last_error :: proc() -> int {
+get_last_error :: proc "contextless" () -> int {
 	return __errno_location()^
 }
 
@@ -822,6 +823,7 @@ get_current_directory :: proc() -> string {
 			return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
 		}
 		if _get_errno(res) != ERANGE {
+			delete(buf)
 			return ""
 		}
 		resize(&buf, len(buf)+page_size)
@@ -878,6 +880,10 @@ get_page_size :: proc() -> int {
 	return page_size
 }
 
+@(private)
+_processor_core_count :: proc() -> int {
+	return int(_unix_get_nprocs())
+}
 
 _alloc_command_line_arguments :: proc() -> []string {
 	res := make([]string, len(runtime.args__))

+ 10 - 2
core/os/os_openbsd.odin

@@ -269,6 +269,7 @@ foreign libc {
 	@(link_name="mkdir")	_unix_mkdir	:: proc(path: cstring, mode: mode_t) -> c.int ---
 
 	@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+	@(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
 	@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
 	@(link_name="closedir")	_unix_closedir	:: proc(dirp: Dir) -> c.int ---
 	@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@@ -294,7 +295,7 @@ is_path_separator :: proc(r: rune) -> bool {
 	return r == '/'
 }
 
-get_last_error :: proc() -> int {
+get_last_error :: proc "contextless" () -> int {
 	return __errno()^
 }
 
@@ -648,6 +649,7 @@ get_current_directory :: proc() -> string {
 			return string(cwd)
 		}
 		if Errno(get_last_error()) != ERANGE {
+			delete(buf)
 			return ""
 		}
 		resize(&buf, len(buf) + MAX_PATH)
@@ -704,6 +706,12 @@ get_page_size :: proc() -> int {
 	return page_size
 }
 
+_SC_NPROCESSORS_ONLN :: 503
+
+@(private)
+_processor_core_count :: proc() -> int {
+	return int(_sysconf(_SC_NPROCESSORS_ONLN))
+}
 
 _alloc_command_line_arguments :: proc() -> []string {
 	res := make([]string, len(runtime.args__))
@@ -711,4 +719,4 @@ _alloc_command_line_arguments :: proc() -> []string {
 		res[i] = string(arg)
 	}
 	return res
-}
+}

+ 4 - 1
core/os/os_wasi.odin

@@ -89,7 +89,10 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 current_thread_id :: proc "contextless" () -> int {
 	return 0
 }
-
+@(private)
+_processor_core_count :: proc() -> int {
+	return 1
+}
 
 file_size :: proc(fd: Handle) -> (i64, Errno) {
 	stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))

+ 23 - 1
core/os/os_windows.odin

@@ -3,6 +3,7 @@ package os
 
 import win32 "core:sys/windows"
 import "core:runtime"
+import "core:intrinsics"
 
 Handle    :: distinct uintptr
 File_Time :: distinct u64
@@ -126,7 +127,28 @@ get_page_size :: proc() -> int {
 	return page_size
 }
 
+@(private)
+_processor_core_count :: proc() -> int {
+	length : win32.DWORD = 0
+	result := win32.GetLogicalProcessorInformation(nil, &length)
 
+	thread_count := 0
+	if !result && win32.GetLastError() == 122 && length > 0 {
+		processors := make([]win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length, context.temp_allocator)
+
+		result = win32.GetLogicalProcessorInformation(&processors[0], &length)
+		if result {
+			for processor in processors {
+				if processor.Relationship == .RelationProcessorCore {
+					thread := intrinsics.count_ones(processor.ProcessorMask)
+					thread_count += int(thread)
+				}
+			}
+		}
+	}
+
+	return thread_count
+}
 
 exit :: proc "contextless" (code: int) -> ! {
 	runtime._cleanup_runtime_contextless()
@@ -214,4 +236,4 @@ is_windows_10 :: proc() -> bool {
 is_windows_11 :: proc() -> bool {
 	osvi := get_windows_version_w()
 	return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
-}
+}

+ 2 - 0
core/reflect/iterator.odin

@@ -2,6 +2,7 @@ package reflect
 
 import "core:runtime"
 
+@(require_results)
 iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
 	if val == nil || it == nil {
 		return
@@ -41,6 +42,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
 	return
 }
 
+@(require_results)
 iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
 	if val == nil || it == nil {
 		return

+ 61 - 39
core/reflect/reflect.odin

@@ -74,6 +74,7 @@ Type_Kind :: enum {
 }
 
 
+@(require_results)
 type_kind :: proc(T: typeid) -> Type_Kind {
 	ti := type_info_of(T)
 	if ti != nil {
@@ -113,57 +114,31 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 }
 
 // TODO(bill): Better name
+@(require_results)
 underlying_type_kind :: proc(T: typeid) -> Type_Kind {
 	return type_kind(runtime.typeid_base(T))
 }
 
 // TODO(bill): Better name
+@(require_results)
 backing_type_kind :: proc(T: typeid) -> Type_Kind {
 	return type_kind(runtime.typeid_core(T))
 }
 
 
-type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
-	if info == nil { return nil }
-
-	base := info
-	loop: for {
-		#partial switch i in base.variant {
-		case Type_Info_Named: base = i.base
-		case: break loop
-		}
-	}
-	return base
-}
-
-
-type_info_core :: proc(info: ^Type_Info) -> ^Type_Info {
-	if info == nil { return nil }
-
-	base := info
-	loop: for {
-		#partial switch i in base.variant {
-		case Type_Info_Named:  base = i.base
-		case Type_Info_Enum:   base = i.base
-		case: break loop
-		}
-	}
-	return base
-}
+type_info_base :: runtime.type_info_base
+type_info_core :: runtime.type_info_core 
 type_info_base_without_enum :: type_info_core
 
 
-typeid_base :: proc(id: typeid) -> typeid {
-	ti := type_info_of(id)
-	ti = type_info_base(ti)
-	return ti.id
-}
-typeid_core :: proc(id: typeid) -> typeid {
-	ti := type_info_base_without_enum(type_info_of(id))
-	return ti.id
+when !ODIN_DISALLOW_RTTI {
+	typeid_base :: runtime.typeid_base
+	typeid_core :: runtime.typeid_core
+	typeid_base_without_enum :: typeid_core
 }
-typeid_base_without_enum :: typeid_core
 
+
+@(require_results)
 any_base :: proc(v: any) -> any {
 	v := v
 	if v != nil {
@@ -171,6 +146,7 @@ any_base :: proc(v: any) -> any {
 	}
 	return v
 }
+@(require_results)
 any_core :: proc(v: any) -> any {
 	v := v
 	if v != nil {
@@ -179,6 +155,7 @@ any_core :: proc(v: any) -> any {
 	return v
 }
 
+@(require_results)
 typeid_elem :: proc(id: typeid) -> typeid {
 	ti := type_info_of(id)
 	if ti == nil { return nil }
@@ -208,6 +185,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
 }
 
 
+@(require_results)
 size_of_typeid :: proc(T: typeid) -> int {
 	if ti := type_info_of(T); ti != nil {
 		return ti.size
@@ -215,6 +193,7 @@ size_of_typeid :: proc(T: typeid) -> int {
 	return 0
 }
 
+@(require_results)
 align_of_typeid :: proc(T: typeid) -> int {
 	if ti := type_info_of(T); ti != nil {
 		return ti.align
@@ -222,6 +201,7 @@ align_of_typeid :: proc(T: typeid) -> int {
 	return 1
 }
 
+@(require_results)
 as_bytes :: proc(v: any) -> []byte {
 	if v != nil {
 		sz := size_of_typeid(v.id)
@@ -230,10 +210,12 @@ as_bytes :: proc(v: any) -> []byte {
 	return nil
 }
 
+@(require_results)
 any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
 	return v.data, v.id
 }
 
+@(require_results)
 is_nil :: proc(v: any) -> bool {
 	if v == nil {
 		return true
@@ -250,6 +232,7 @@ is_nil :: proc(v: any) -> bool {
 	return true
 }
 
+@(require_results)
 length :: proc(val: any) -> int {
 	if val == nil { return 0 }
 
@@ -285,6 +268,7 @@ length :: proc(val: any) -> int {
 	return 0
 }
 
+@(require_results)
 capacity :: proc(val: any) -> int {
 	if val == nil { return 0 }
 
@@ -311,6 +295,7 @@ capacity :: proc(val: any) -> int {
 }
 
 
+@(require_results)
 index :: proc(val: any, i: int, loc := #caller_location) -> any {
 	if val == nil { return nil }
 
@@ -370,6 +355,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
 	return nil
 }
 
+@(require_results)
 deref :: proc(val: any) -> any {
 	if val != nil {
 		ti := type_info_base(type_info_of(val.id))
@@ -399,6 +385,7 @@ Struct_Field :: struct {
 	is_using: bool,
 }
 
+@(require_results)
 struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -413,6 +400,7 @@ struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
 	return
 }
 
+@(require_results)
 struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -430,6 +418,7 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
 	return
 }
 
+@(require_results)
 struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
 	if a == nil { return nil }
 
@@ -461,6 +450,7 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
 
 
 
+@(require_results)
 struct_field_names :: proc(T: typeid) -> []string {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -469,6 +459,7 @@ struct_field_names :: proc(T: typeid) -> []string {
 	return nil
 }
 
+@(require_results)
 struct_field_types :: proc(T: typeid) -> []^Type_Info {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -478,6 +469,7 @@ struct_field_types :: proc(T: typeid) -> []^Type_Info {
 }
 
 
+@(require_results)
 struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -486,6 +478,7 @@ struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
 	return nil
 }
 
+@(require_results)
 struct_field_offsets :: proc(T: typeid) -> []uintptr {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -494,6 +487,7 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
 	return nil
 }
 
+@(require_results)
 struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
 	ti := runtime.type_info_base(type_info_of(T))
 	if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -510,11 +504,13 @@ struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
 
 
 
+@(require_results)
 struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
 	value, _ = struct_tag_lookup(tag, key)
 	return
 }
 
+@(require_results)
 struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
 	for t := tag; t != ""; /**/ {
 		i := 0
@@ -573,6 +569,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, o
 }
 
 
+@(require_results)
 enum_string :: proc(a: any) -> string {
 	if a == nil { return "" }
 	ti := runtime.type_info_base(type_info_of(a.id))
@@ -591,6 +588,7 @@ enum_string :: proc(a: any) -> string {
 }
 
 // Given a enum type and a value name, get the enum value.
+@(require_results)
 enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
 	ti := type_info_base(type_info_of(Enum_Type))
 	if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -607,6 +605,7 @@ enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, o
 	return
 }
 
+@(require_results)
 enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
 	ti := runtime.type_info_base(type_info_of(Enum_Type))
 	if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -623,6 +622,7 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
 }
 
 
+@(require_results)
 enum_field_names :: proc(Enum_Type: typeid) -> []string {
 	ti := runtime.type_info_base(type_info_of(Enum_Type))
 	if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -630,6 +630,7 @@ enum_field_names :: proc(Enum_Type: typeid) -> []string {
 	}
 	return nil
 }
+@(require_results)
 enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
 	ti := runtime.type_info_base(type_info_of(Enum_Type))
 	if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -643,6 +644,7 @@ Enum_Field :: struct {
 	value: Type_Info_Enum_Value,
 }
 
+@(require_results)
 enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
 	ti := runtime.type_info_base(type_info_of(Enum_Type))
 	if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -653,15 +655,18 @@ enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
 
 
 
+@(require_results)
 union_variant_type_info :: proc(a: any) -> ^Type_Info {
 	id := union_variant_typeid(a)
 	return type_info_of(id)
 }
 
+@(require_results)
 type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
 	return len(info.variants) == 1 && is_pointer(info.variants[0])
 }
 
+@(require_results)
 union_variant_typeid :: proc(a: any) -> typeid {
 	if a == nil { return nil }
 
@@ -690,9 +695,10 @@ union_variant_typeid :: proc(a: any) -> typeid {
 		case: unimplemented()
 		}
 
-		if a.data != nil && tag != 0 {
-			i := tag if info.no_nil else tag-1
-			return info.variants[i].id
+		if info.no_nil {
+			return info.variants[tag].id
+		} else if tag != 0 {
+			return info.variants[tag-1].id
 		}
 
 		return nil
@@ -700,6 +706,7 @@ union_variant_typeid :: proc(a: any) -> typeid {
 	panic("expected a union to reflect.union_variant_typeid")
 }
 
+@(require_results)
 get_union_variant_raw_tag :: proc(a: any) -> i64 {
 	if a == nil { return -1 }
 
@@ -730,6 +737,7 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
 	panic("expected a union to reflect.get_union_variant_raw_tag")
 }
 
+@(require_results)
 get_union_variant :: proc(a: any) -> any {
 	if a == nil {
 		return nil
@@ -741,6 +749,7 @@ get_union_variant :: proc(a: any) -> any {
 	return any{a.data, id}
 }
 
+@(require_results)
 get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
 	ptr := rawptr(val)
 	tag := get_union_variant_raw_tag(val^)
@@ -881,6 +890,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
 
 
 
+@(require_results)
 as_bool :: proc(a: any) -> (value: bool, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -903,6 +913,7 @@ as_bool :: proc(a: any) -> (value: bool, valid: bool) {
 	return
 }
 
+@(require_results)
 as_int :: proc(a: any) -> (value: int, valid: bool) {
 	v: i64
 	v, valid = as_i64(a)
@@ -910,6 +921,7 @@ as_int :: proc(a: any) -> (value: int, valid: bool) {
 	return
 }
 
+@(require_results)
 as_uint :: proc(a: any) -> (value: uint, valid: bool) {
 	v: u64
 	v, valid = as_u64(a)
@@ -917,6 +929,7 @@ as_uint :: proc(a: any) -> (value: uint, valid: bool) {
 	return
 }
 
+@(require_results)
 as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1024,6 +1037,7 @@ as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
 	return
 }
 
+@(require_results)
 as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1133,6 +1147,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
 }
 
 
+@(require_results)
 as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1239,6 +1254,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
 }
 
 
+@(require_results)
 as_string :: proc(a: any) -> (value: string, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1258,6 +1274,7 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
 	return
 }
 
+@(require_results)
 relative_pointer_to_absolute :: proc(a: any) -> rawptr {
 	if a == nil { return nil }
 	a := a
@@ -1272,6 +1289,7 @@ relative_pointer_to_absolute :: proc(a: any) -> rawptr {
 }
 
 
+@(require_results)
 relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
 	_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
 		if ptr^ == 0 {
@@ -1314,6 +1332,7 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid)
 
 
 
+@(require_results)
 as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1341,6 +1360,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
 }
 
 
+@(require_results)
 as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
 	if a == nil { return }
 	a := a
@@ -1377,9 +1397,11 @@ ne :: not_equal
 
 DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
 
+@(require_results)
 not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
 	return !equal(a, b, including_indirect_array_recursion, recursion_level)
 }
+@(require_results)
 equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
 	if a == nil && b == nil {
 		return true

+ 33 - 3
core/reflect/types.odin

@@ -3,17 +3,16 @@ package reflect
 import "core:io"
 import "core:strings"
 
+@(require_results)
 are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 	if a == b {
 		return true
 	}
 
-	if (a == nil && b != nil) ||
-	   (a != nil && b == nil) {
+	if a == nil || b == nil {
 		return false
 	}
 
-
 	switch {
 	case a.size != b.size, a.align != b.align:
 		return false
@@ -180,6 +179,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 	return false
 }
 
+@(require_results)
 is_signed :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	#partial switch i in type_info_base(info).variant {
@@ -188,6 +188,7 @@ is_signed :: proc(info: ^Type_Info) -> bool {
 	}
 	return false
 }
+@(require_results)
 is_unsigned :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	#partial switch i in type_info_base(info).variant {
@@ -197,6 +198,7 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
 	return false
 }
 
+@(require_results)
 is_byte :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	#partial switch i in type_info_base(info).variant {
@@ -206,66 +208,79 @@ is_byte :: proc(info: ^Type_Info) -> bool {
 }
 
 
+@(require_results)
 is_integer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Integer)
 	return ok
 }
+@(require_results)
 is_rune :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Rune)
 	return ok
 }
+@(require_results)
 is_float :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Float)
 	return ok
 }
+@(require_results)
 is_complex :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Complex)
 	return ok
 }
+@(require_results)
 is_quaternion :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
 	return ok
 }
+@(require_results)
 is_any :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Any)
 	return ok
 }
+@(require_results)
 is_string :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_String)
 	return ok
 }
+@(require_results)
 is_cstring :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	v, ok := type_info_base(info).variant.(Type_Info_String)
 	return ok && v.is_cstring
 }
+@(require_results)
 is_boolean :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Boolean)
 	return ok
 }
+@(require_results)
 is_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Pointer)
 	return ok
 }
+@(require_results)
 is_multi_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
 	return ok
 }
+@(require_results)
 is_soa_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
 	return ok
 }
+@(require_results)
 is_pointer_internally :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	#partial switch v in info.variant {
@@ -277,76 +292,91 @@ is_pointer_internally :: proc(info: ^Type_Info) -> bool {
 	}
 	return false
 }
+@(require_results)
 is_procedure :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Procedure)
 	return ok
 }
+@(require_results)
 is_array :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Array)
 	return ok
 }
+@(require_results)
 is_enumerated_array :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
 	return ok
 }
+@(require_results)
 is_dynamic_array :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
 	return ok
 }
+@(require_results)
 is_dynamic_map :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Map)
 	return ok
 }
+@(require_results)
 is_bit_set :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
 	return ok
 }
+@(require_results)
 is_slice :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Slice)
 	return ok
 }
+@(require_results)
 is_tuple :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Tuple)
 	return ok
 }
+@(require_results)
 is_struct :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	s, ok := type_info_base(info).variant.(Type_Info_Struct)
 	return ok && !s.is_raw_union
 }
+@(require_results)
 is_raw_union :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	s, ok := type_info_base(info).variant.(Type_Info_Struct)
 	return ok && s.is_raw_union
 }
+@(require_results)
 is_union :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Union)
 	return ok
 }
+@(require_results)
 is_enum :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Enum)
 	return ok
 }
+@(require_results)
 is_simd_vector :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
 	return ok
 }
+@(require_results)
 is_relative_pointer :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
 	return ok
 }
+@(require_results)
 is_relative_slice :: proc(info: ^Type_Info) -> bool {
 	if info == nil { return false }
 	_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice)

+ 6 - 0
core/runtime/core.odin

@@ -329,6 +329,12 @@ Allocator :: struct {
 	data:      rawptr,
 }
 
+Byte     :: 1
+Kilobyte :: 1024 * Byte
+Megabyte :: 1024 * Kilobyte
+Gigabyte :: 1024 * Megabyte
+Terabyte :: 1024 * Gigabyte
+
 // Logging stuff
 
 Logger_Level :: enum uint {

+ 1 - 1
core/runtime/core_builtin.odin

@@ -615,7 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
 	old_size := a.cap * size_of(E)
 	new_size := new_cap * size_of(E)
 
-	new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
+	new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
 	if err != nil {
 		return
 	}

+ 2 - 2
core/runtime/core_builtin_soa.odin

@@ -150,14 +150,14 @@ make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.
 }
 
 @builtin
-make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, auto_cast length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
 	context.allocator = allocator
 	resize_soa(&array, length, loc)
 	return
 }
 
 @builtin
-make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, auto_cast length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
 	context.allocator = allocator
 	if reserve_soa(&array, capacity, loc) {
 		resize_soa(&array, length, loc)

+ 2 - 2
core/runtime/default_temporary_allocator.odin

@@ -1,6 +1,6 @@
 package runtime
 
-DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
+DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
 
 
 when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
@@ -197,4 +197,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
 		procedure = default_temp_allocator_proc,
 		data = allocator,
 	}
-}
+}

+ 67 - 0
core/runtime/print.odin

@@ -2,6 +2,73 @@ package runtime
 
 _INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
 
+when !ODIN_DISALLOW_RTTI {
+	print_any_single :: proc(arg: any) {
+		x := arg
+		if loc, ok := x.(Source_Code_Location); ok {
+			print_caller_location(loc)
+			return
+		}
+		x.id = typeid_base(x.id)
+		switch v in x {
+		case typeid:     print_typeid(v)
+		case ^Type_Info: print_type(v)
+
+		case string:  print_string(v)
+		case cstring: print_string(string(v))
+		case []byte:  print_string(string(v))
+
+		case rune:  print_rune(v)
+
+		case u8:    print_u64(u64(v))
+		case u16:   print_u64(u64(v))
+		case u16le: print_u64(u64(v))
+		case u16be: print_u64(u64(v))
+		case u32:   print_u64(u64(v))
+		case u32le: print_u64(u64(v))
+		case u32be: print_u64(u64(v))
+		case u64:   print_u64(u64(v))
+		case u64le: print_u64(u64(v))
+		case u64be: print_u64(u64(v))
+
+		case i8:    print_i64(i64(v))
+		case i16:   print_i64(i64(v))
+		case i16le: print_i64(i64(v))
+		case i16be: print_i64(i64(v))
+		case i32:   print_i64(i64(v))
+		case i32le: print_i64(i64(v))
+		case i32be: print_i64(i64(v))
+		case i64:   print_i64(i64(v))
+		case i64le: print_i64(i64(v))
+		case i64be: print_i64(i64(v))
+
+		case int:     print_int(v)
+		case uint:    print_uint(v)
+		case uintptr: print_uintptr(v)
+
+		case:
+			ti := type_info_of(x.id)
+			#partial switch v in ti.variant {
+			case Type_Info_Pointer:
+				print_uintptr((^uintptr)(x.data)^)
+				return
+			}
+
+			print_string("<invalid-value>")
+		}
+	}
+	println_any :: proc(args: ..any) {
+		loop: for arg, i in args {
+			if i != 0 {
+				print_string(" ")
+			}
+			print_any_single(arg)
+		}
+		print_string("\n")
+	}
+}
+
+
 encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
 	r := c
 

+ 1 - 1
core/slice/map.odin

@@ -37,7 +37,7 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
 }
 
 
-map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
+map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator_Error) {
 	entries = make(type_of(entries), len(m), allocator) or_return
 	i := 0
 	for key, value in m {

+ 17 - 15
core/slice/ptr.odin

@@ -73,24 +73,26 @@ ptr_rotate :: proc(left: int, mid: ^$T, right: int) {
 		left, mid, right := left, mid, right
 
 		// TODO(bill): Optimization with a buffer for smaller ranges
-		if left >= right {
-			for {
-				ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right)
-				mid = ptr_sub(mid, right)
+		for left > 0 && right > 0 {
+			if left >= right {
+				for {
+					ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right * size_of(T))
+					mid = ptr_sub(mid, right)
 
-				left -= right
-				if left < right {
-					break
+					left -= right
+					if left < right {
+						break
+					}
 				}
-			}
-		} else {
-			for {
-				ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left)
-				mid = ptr_add(mid, left)
+			} else {
+				for {
+					ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left * size_of(T))
+					mid = ptr_add(mid, left)
 
-				right -= left
-				if right < left {
-					break
+					right -= left
+					if right < left {
+						break
+					}
 				}
 			}
 		}

+ 6 - 5
core/slice/slice.odin

@@ -77,8 +77,7 @@ swap_between :: proc(a, b: $T/[]$E) {
 reverse :: proc(array: $T/[]$E) {
 	n := len(array)/2
 	for i in 0..<n {
-		a, b := i, len(array)-i-1
-		array[a], array[b] = array[b], array[a]
+		swap(array, i, len(array)-i-1)
 	}
 }
 
@@ -218,8 +217,10 @@ rotate_left :: proc(array: $T/[]$E, mid: int) {
 	n := len(array)
 	m := mid %% n
 	k := n - m
-	p := raw_data(array)
-	ptr_rotate(mid, ptr_add(p, mid), k)
+	// FIXME: (ap29600) this cast is a temporary fix for the compiler not matching
+	// [^T] with $P/^$T
+	p := cast(^E)raw_data(array)
+	ptr_rotate(m, ptr_add(p, m), k)
 }
 rotate_right :: proc(array: $T/[]$E, k: int) {
 	rotate_left(array, -k)
@@ -515,4 +516,4 @@ dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
 enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
 	where intrinsics.type_is_enumerated_array(T) {
 	return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
-}
+}

+ 121 - 21
core/strconv/decimal/decimal.odin

@@ -214,25 +214,128 @@ shift_right :: proc(a: ^Decimal, k: uint) {
 	trim(a)
 }
 
-shift_left :: proc(a: ^Decimal, k: uint) {
-	// NOTE(bill): used to determine buffer size required for the decimal from the binary shift
-	// 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
-	log10_2 :: 0.301029995663981195213738894724493026768189881462108541310
-	capacity := int(f64(k)*log10_2 + 1)
+import "core:runtime"
+println :: proc(args: ..any) {
+	for arg, i in args {
+		if i != 0 {
+			runtime.print_string(" ")
+		}
+		switch v in arg {
+		case string:  runtime.print_string(v)
+		case rune:    runtime.print_rune(v)
+		case int:     runtime.print_int(v)
+		case uint:    runtime.print_uint(v)
+		case u8:      runtime.print_u64(u64(v))
+		case u16:     runtime.print_u64(u64(v))
+		case u32:     runtime.print_u64(u64(v))
+		case u64:     runtime.print_u64(v)
+		case i8:      runtime.print_i64(i64(v))
+		case i16:     runtime.print_i64(i64(v))
+		case i32:     runtime.print_i64(i64(v))
+		case i64:     runtime.print_i64(v)
+		case uintptr: runtime.print_uintptr(v)
+		case bool:    runtime.print_string("true" if v else "false")
+		}
+	}
+	runtime.print_string("\n")
+}
 
-	r := a.count          // read index
-	w := a.count+capacity // write index
+@(private="file")
+_shift_left_offsets := [?]struct{delta: int, cutoff: string}{
+	{ 0, ""},
+	{ 1, "5"},
+	{ 1, "25"},
+	{ 1, "125"},
+	{ 2, "625"},
+	{ 2, "3125"},
+	{ 2, "15625"},
+	{ 3, "78125"},
+	{ 3, "390625"},
+	{ 3, "1953125"},
+	{ 4, "9765625"},
+	{ 4, "48828125"},
+	{ 4, "244140625"},
+	{ 4, "1220703125"},
+	{ 5, "6103515625"},
+	{ 5, "30517578125"},
+	{ 5, "152587890625"},
+	{ 6, "762939453125"},
+	{ 6, "3814697265625"},
+	{ 6, "19073486328125"},
+	{ 7, "95367431640625"},
+	{ 7, "476837158203125"},
+	{ 7, "2384185791015625"},
+	{ 7, "11920928955078125"},
+	{ 8, "59604644775390625"},
+	{ 8, "298023223876953125"},
+	{ 8, "1490116119384765625"},
+	{ 9, "7450580596923828125"},
+	{ 9, "37252902984619140625"},
+	{ 9, "186264514923095703125"},
+	{10, "931322574615478515625"},
+	{10, "4656612873077392578125"},
+	{10, "23283064365386962890625"},
+	{10, "116415321826934814453125"},
+	{11, "582076609134674072265625"},
+	{11, "2910383045673370361328125"},
+	{11, "14551915228366851806640625"},
+	{12, "72759576141834259033203125"},
+	{12, "363797880709171295166015625"},
+	{12, "1818989403545856475830078125"},
+	{13, "9094947017729282379150390625"},
+	{13, "45474735088646411895751953125"},
+	{13, "227373675443232059478759765625"},
+	{13, "1136868377216160297393798828125"},
+	{14, "5684341886080801486968994140625"},
+	{14, "28421709430404007434844970703125"},
+	{14, "142108547152020037174224853515625"},
+	{15, "710542735760100185871124267578125"},
+	{15, "3552713678800500929355621337890625"},
+	{15, "17763568394002504646778106689453125"},
+	{16, "88817841970012523233890533447265625"},
+	{16, "444089209850062616169452667236328125"},
+	{16, "2220446049250313080847263336181640625"},
+	{16, "11102230246251565404236316680908203125"},
+	{17, "55511151231257827021181583404541015625"},
+	{17, "277555756156289135105907917022705078125"},
+	{17, "1387778780781445675529539585113525390625"},
+	{18, "6938893903907228377647697925567626953125"},
+	{18, "34694469519536141888238489627838134765625"},
+	{18, "173472347597680709441192448139190673828125"},
+	{19, "867361737988403547205962240695953369140625"},
+}
+
+shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
+	prefix_less :: #force_inline proc "contextless" (b: []byte, s: string) -> bool #no_bounds_check {
+		for i in 0..<len(s) {
+			if i >= len(b) {
+				return true
+			}
+			if b[i] != s[i] {
+				return b[i] < s[i]
+			}
+		}
+		return false
+	}
+
+	assert(k < 61)
+
+	delta := _shift_left_offsets[k].delta
+	if prefix_less(a.digits[:a.count], _shift_left_offsets[k].cutoff) {
+		delta -= 1
+	}
 
-	d := len(a.digits)
+	read_index  := a.count
+	write_index := a.count+delta
 
 	n: uint
-	for r -= 1; r >= 0; r -= 1 {
-		n += (uint(a.digits[r]) - '0') << k
+	for read_index -= 1; read_index >= 0; read_index -= 1 {
+		n += (uint(a.digits[read_index]) - '0') << k
 		quo := n/10
 		rem := n - 10*quo
-		w -= 1
-		if w < d {
-			a.digits[w] = byte('0' + rem)
+		write_index -= 1
+		if write_index < len(a.digits) {
+			a.digits[write_index] = byte('0' + rem)
 		} else if rem != 0 {
 			a.trunc = true
 		}
@@ -242,21 +345,18 @@ shift_left :: proc(a: ^Decimal, k: uint) {
 	for n > 0 {
 		quo := n/10
 		rem := n - 10*quo
-		w -= 1
-		if w < d {
-			a.digits[w] = byte('0' + rem)
+		write_index -= 1
+		if write_index < len(a.digits) {
+			a.digits[write_index] = byte('0' + rem)
 		} else if rem != 0 {
 			a.trunc = true
 		}
 		n = quo
 	}
 
-	// NOTE(bill): Remove unused buffer size
-	assert(w >= 0)
-	capacity -= w
+	a.decimal_point += delta
 
-	a.count = min(a.count+capacity, d)
-	a.decimal_point += capacity
+	a.count = clamp(a.count, 0, len(a.digits))
 	trim(a)
 }
 

+ 5 - 5
core/strconv/generic_float.odin

@@ -287,13 +287,13 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
 
 @(private)
 decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
-	end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (b: u64) {
-		bits := mant & (u64(1)<<info.mantbits - 1)
+	end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) {
+		bits = mant & (u64(1)<<info.mantbits - 1)
 		bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
 		if d.neg {
 			bits |= 1<< info.mantbits << info.expbits
 		}
-		return bits
+		return
 	}
 	set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
 		mant^ = 0
@@ -303,7 +303,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
 
 	mant: u64
 	exp: int
-	if d.decimal_point == 0 {
+	if d.count == 0 {
 		mant = 0
 		exp = info.bias
 		b = end(d, mant, exp, info)
@@ -326,7 +326,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
 	exp = 0
 	for d.decimal_point > 0 {
 		n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
-		decimal.shift(d, n)
+		decimal.shift(d, -n)
 		exp += n
 	}
 	for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {

+ 0 - 5
core/strconv/integers.odin

@@ -3,7 +3,6 @@ package strconv
 Int_Flag :: enum {
 	Prefix,
 	Plus,
-	Space,
 }
 Int_Flags :: bit_set[Int_Flag]
 
@@ -73,8 +72,6 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
 		i-=1; a[i] = '-'
 	case .Plus in flags:
 		i-=1; a[i] = '+'
-	case .Space in flags:
-		i-=1; a[i] = ' '
 	}
 
 	out := a[i:]
@@ -157,8 +154,6 @@ append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_si
 		i-=1; a[i] = '-'
 	case .Plus in flags:
 		i-=1; a[i] = '+'
-	case .Space in flags:
-		i-=1; a[i] = ' '
 	}
 
 	out := a[i:]

+ 1 - 2
core/strconv/strconv.odin

@@ -819,7 +819,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
 		}
 
 		if mantissa>>_f64_info.mantbits != 0 {
-			return
+			break trunc_block
 		}
 		f := f64(mantissa)
 		if neg {
@@ -841,7 +841,6 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
 			return f / pow10[-exp], true
 		}
 	}
-
 	d: decimal.Decimal
 	decimal.set(&d, str[:nr])
 	b, overflow := decimal_to_float_bits(&d, &_f64_info)

+ 95 - 30
core/sync/extended.odin

@@ -1,6 +1,8 @@
 package sync
 
 import "core:time"
+import vg "core:sys/valgrind"
+_ :: vg
 
 // A Wait_Group waits for a collection of threads to finish
 //
@@ -11,7 +13,7 @@ Wait_Group :: struct {
 	cond:    Cond,
 }
 
-wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
+wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
 	if delta == 0 {
 		return
 	}
@@ -20,32 +22,32 @@ wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
 
 	atomic_add(&wg.counter, delta)
 	if wg.counter < 0 {
-		panic("sync.Wait_Group negative counter")
+		_panic("sync.Wait_Group negative counter")
 	}
 	if wg.counter == 0 {
 		cond_broadcast(&wg.cond)
 		if wg.counter != 0 {
-			panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
+			_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
 		}
 	}
 }
 
-wait_group_done :: proc(wg: ^Wait_Group) {
+wait_group_done :: proc "contextless" (wg: ^Wait_Group) {
 	wait_group_add(wg, -1)
 }
 
-wait_group_wait :: proc(wg: ^Wait_Group) {
+wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
 	guard(&wg.mutex)
 
 	if wg.counter != 0 {
 		cond_wait(&wg.cond, &wg.mutex)
 		if wg.counter != 0 {
-			panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
+			_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
 		}
 	}
 }
 
-wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -> bool {
+wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}
@@ -56,7 +58,7 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
 			return false
 		}
 		if wg.counter != 0 {
-			panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
+			_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
 		}
 	}
 	return true
@@ -76,7 +78,7 @@ Example:
 
 	barrier := &sync.Barrier{}
 
-	main :: proc() {
+	main :: proc "contextless" () {
 		fmt.println("Start")
 
 		THREAD_COUNT :: 4
@@ -107,7 +109,10 @@ Barrier :: struct {
 	thread_count:  int,
 }
 
-barrier_init :: proc(b: ^Barrier, thread_count: int) {
+barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_barrier_resize_pre(b, uint(thread_count))
+	}
 	b.index = 0
 	b.generation_id = 0
 	b.thread_count = thread_count
@@ -115,7 +120,10 @@ barrier_init :: proc(b: ^Barrier, thread_count: int) {
 
 // Block the current thread until all threads have rendezvoused
 // Barrier can be reused after all threads rendezvoused once, and can be used continuously
-barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
+barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_barrier_wait_pre(b)
+	}
 	guard(&b.mutex)
 	local_gen := b.generation_id
 	b.index += 1
@@ -141,7 +149,7 @@ Auto_Reset_Event :: struct {
 	sema:   Sema,
 }
 
-auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
+auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {
 	old_status := atomic_load_explicit(&e.status, .Relaxed)
 	for {
 		new_status := old_status + 1 if old_status < 1 else 1
@@ -155,7 +163,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
 	}
 }
 
-auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
+auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
 	old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
 	if old_status < 1 {
 		sema_wait(&e.sema)
@@ -169,18 +177,18 @@ Ticket_Mutex :: struct {
 	serving: uint,
 }
 
-ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
+ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
 	ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
 	for ticket != atomic_load_explicit(&m.serving, .Acquire) {
 		cpu_relax()
 	}
 }
 
-ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
+ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
 	atomic_add_explicit(&m.serving, 1, .Relaxed)
 }
 @(deferred_in=ticket_mutex_unlock)
-ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
+ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
 	ticket_mutex_lock(m)
 	return true
 }
@@ -191,25 +199,25 @@ Benaphore :: struct {
 	sema:    Sema,
 }
 
-benaphore_lock :: proc(b: ^Benaphore) {
+benaphore_lock :: proc "contextless" (b: ^Benaphore) {
 	if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
 		sema_wait(&b.sema)
 	}
 }
 
-benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
+benaphore_try_lock :: proc "contextless" (b: ^Benaphore) -> bool {
 	v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
 	return v == 0
 }
 
-benaphore_unlock :: proc(b: ^Benaphore) {
+benaphore_unlock :: proc "contextless" (b: ^Benaphore) {
 	if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
 		sema_post(&b.sema)
 	}
 }
 
 @(deferred_in=benaphore_unlock)
-benaphore_guard :: proc(m: ^Benaphore) -> bool {
+benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
 	benaphore_lock(m)
 	return true
 }
@@ -221,7 +229,7 @@ Recursive_Benaphore :: struct {
 	sema:      Sema,
 }
 
-recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
+recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
 	tid := current_thread_id()
 	if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
 		if tid != b.owner {
@@ -233,7 +241,7 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
 	b.recursion += 1
 }
 
-recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
+recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool {
 	tid := current_thread_id()
 	if b.owner == tid {
 		atomic_add_explicit(&b.counter, 1, .Acquire)
@@ -248,9 +256,9 @@ recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
 	return true
 }
 
-recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
+recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
 	tid := current_thread_id()
-	assert(tid == b.owner)
+	_assert(tid == b.owner, "tid != b.owner")
 	b.recursion -= 1
 	recursion := b.recursion
 	if recursion == 0 {
@@ -265,7 +273,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
 }
 
 @(deferred_in=recursive_benaphore_unlock)
-recursive_benaphore_guard :: proc(m: ^Recursive_Benaphore) -> bool {
+recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {
 	recursive_benaphore_lock(m)
 	return true
 }
@@ -282,7 +290,15 @@ Once :: struct {
 }
 
 // once_do calls the procedure fn if and only if once_do is being called for the first for this instance of Once.
-once_do :: proc(o: ^Once, fn: proc()) {
+once_do :: proc{
+	once_do_without_data,
+	once_do_without_data_contextless,
+	once_do_with_data,
+	once_do_with_data_contextless,
+}
+
+// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
+once_do_without_data :: proc(o: ^Once, fn: proc()) {
 	@(cold)
 	do_slow :: proc(o: ^Once, fn: proc()) {
 		guard(&o.m)
@@ -292,12 +308,61 @@ once_do :: proc(o: ^Once, fn: proc()) {
 		}
 	}
 
-	
 	if atomic_load_explicit(&o.done, .Acquire) == false {
 		do_slow(o, fn)
 	}
 }
 
+// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
+once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {
+	@(cold)
+	do_slow :: proc(o: ^Once, fn: proc "contextless" ()) {
+		guard(&o.m)
+		if !o.done {
+			fn()
+			atomic_store_explicit(&o.done, true, .Release)
+		}
+	}
+
+	if atomic_load_explicit(&o.done, .Acquire) == false {
+		do_slow(o, fn)
+	}
+}
+
+// once_do_with_data calls the procedure fn if and only if once_do_with_data is being called for the first for this instance of Once.
+once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
+	@(cold)
+	do_slow :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
+		guard(&o.m)
+		if !o.done {
+			fn(data)
+			atomic_store_explicit(&o.done, true, .Release)
+		}
+	}
+
+	if atomic_load_explicit(&o.done, .Acquire) == false {
+		do_slow(o, fn, data)
+	}
+}
+
+// once_do_with_data_contextless calls the procedure fn if and only if once_do_with_data_contextless is being called for the first for this instance of Once.
+once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
+	@(cold)
+	do_slow :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
+		guard(&o.m)
+		if !o.done {
+			fn(data)
+			atomic_store_explicit(&o.done, true, .Release)
+		}
+	}
+
+	if atomic_load_explicit(&o.done, .Acquire) == false {
+		do_slow(o, fn, data)
+	}
+}
+
+
+
 
 
 // A Parker is an associated token which is initially not present:
@@ -314,7 +379,7 @@ Parker :: struct {
 // Blocks the current thread until the token is made available.
 //
 // Assumes this is only called by the thread that owns the Parker.
-park :: proc(p: ^Parker) {
+park :: proc "contextless" (p: ^Parker) {
 	EMPTY    :: 0
 	NOTIFIED :: 1
 	PARKED   :: max(u32)
@@ -333,7 +398,7 @@ park :: proc(p: ^Parker) {
 // for a limited duration.
 //
 // Assumes this is only called by the thread that owns the Parker
-park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
+park_with_timeout :: proc "contextless" (p: ^Parker, duration: time.Duration) {
 	EMPTY    :: 0
 	NOTIFIED :: 1
 	PARKED   :: max(u32)
@@ -345,7 +410,7 @@ park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
 }
 
 // Automatically makes thee token available if it was not already.
-unpark :: proc(p: ^Parker)  {
+unpark :: proc "contextless" (p: ^Parker)  {
 	EMPTY    :: 0
 	NOTIFIED :: 1
 	PARKED   :: max(Futex)

+ 7 - 7
core/sync/futex_darwin.odin

@@ -24,11 +24,11 @@ EINTR     :: -4
 EFAULT    :: -14
 ETIMEDOUT :: -60
 
-_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
 	return _futex_wait_with_timeout(f, expected, 0)
 }
 
-_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 	timeout_ns := u32(duration) * 1000
 	
 	s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
@@ -41,13 +41,13 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
 	case ETIMEDOUT:
 		return false
 	case:
-		panic("futex_wait failure")
+		_panic("futex_wait failure")
 	}
 	return true
 
 }
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	loop: for {
 		s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
 		if s >= 0 {
@@ -59,12 +59,12 @@ _futex_signal :: proc(f: ^Futex) {
 		case ENOENT:
 			return
 		case:
-			panic("futex_wake_single failure")
+			_panic("futex_wake_single failure")
 		}
 	}
 }
 
-_futex_broadcast :: proc(f: ^Futex) {
+_futex_broadcast :: proc "contextless" (f: ^Futex) {
 	loop: for {
 		s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
 		if s >= 0 {
@@ -76,7 +76,7 @@ _futex_broadcast :: proc(f: ^Futex) {
 		case ENOENT:
 			return
 		case:
-			panic("futex_wake_all failure")
+			_panic("futex_wake_all failure")
 		}
 	}
 }

+ 8 - 8
core/sync/futex_freebsd.odin

@@ -17,7 +17,7 @@ foreign libc {
 	__error :: proc "c" () -> ^c.int ---
 }
 
-_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
 	timeout := [2]i64{14400, 0} // 4 hours
 	for {
 		res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
@@ -30,12 +30,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
 			continue
 		}
 
-		panic("_futex_wait failure")
+		_panic("_futex_wait failure")
 	}
 	unreachable()
 }
 
-_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}
@@ -51,21 +51,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
 		return false
 	}
 
-	panic("_futex_wait_with_timeout failure")
+	_panic("_futex_wait_with_timeout failure")
 }
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
 
 	if res == -1 {
-		panic("_futex_signal failure")
+		_panic("_futex_signal failure")
 	}
 }
 
-_futex_broadcast :: proc(f: ^Futex)  {
+_futex_broadcast :: proc "contextless" (f: ^Futex)  {
 	res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
 
 	if res == -1 {
-		panic("_futex_broadcast failure")
+		_panic("_futex_broadcast failure")
 	}
 }

+ 10 - 10
core/sync/futex_linux.odin

@@ -21,20 +21,20 @@ EFAULT    :: -14
 EINVAL    :: -22
 ETIMEDOUT :: -110
 
-get_errno :: proc(r: int) -> int {
+get_errno :: proc "contextless" (r: int) -> int {
 	if -4096 < r && r < 0 {
 		return r
 	}
 	return 0
 }
 
-internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
+internal_futex :: proc "contextless" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
 	code := int(intrinsics.syscall(unix.SYS_futex, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
 	return get_errno(code)
 }
 
 
-_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
 	err := internal_futex(f, FUTEX_WAIT_PRIVATE | FUTEX_WAIT, expected, nil)
 	switch err {
 	case ESUCCESS, EINTR, EAGAIN, EINVAL:
@@ -44,12 +44,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
 	case EFAULT: 
 		fallthrough
 	case:
-		panic("futex_wait failure")
+		_panic("futex_wait failure")
 	}
 	return true
 }
 
-_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}
@@ -71,27 +71,27 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
 	case EFAULT: 
 		fallthrough
 	case:
-		panic("futex_wait_with_timeout failure")
+		_panic("futex_wait_with_timeout failure")
 	}
 	return true
 }
 
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, 1, nil)
 	switch err {
 	case ESUCCESS, EINVAL, EFAULT:
 		// okay
 	case:
-		panic("futex_wake_single failure")
+		_panic("futex_wake_single failure")
 	}
 }
-_futex_broadcast :: proc(f: ^Futex)  {
+_futex_broadcast :: proc "contextless" (f: ^Futex)  {
 	err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, u32(max(i32)), nil)
 	switch err {
 	case ESUCCESS, EINVAL, EFAULT:
 		// okay
 	case:
-		panic("_futex_wake_all failure")
+		_panic("_futex_wake_all failure")
 	}
 }

+ 8 - 8
core/sync/futex_openbsd.odin

@@ -21,7 +21,7 @@ foreign libc {
 	_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
 }
 
-_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
 	res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
 
 	if res != -1 {
@@ -32,10 +32,10 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
 		return false
 	}
 
-	panic("futex_wait failure")
+	_panic("futex_wait failure")
 }
 
-_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}
@@ -58,21 +58,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
 		return false
 	}
 
-	panic("futex_wait_with_timeout failure")
+	_panic("futex_wait_with_timeout failure")
 }
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
 
 	if res == -1 {
-		panic("futex_wake_single failure")
+		_panic("futex_wake_single failure")
 	}
 }
 
-_futex_broadcast :: proc(f: ^Futex)  {
+_futex_broadcast :: proc "contextless" (f: ^Futex)  {
 	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
 
 	if res == -1 {
-		panic("_futex_wake_all failure")
+		_panic("_futex_wake_all failure")
 	}
 }

+ 4 - 4
core/sync/futex_wasm.odin

@@ -5,18 +5,18 @@ package sync
 import "core:intrinsics"
 import "core:time"
 
-_futex_wait :: proc(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
 }
 
-_futex_wait_with_timeout :: proc(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
 
 }
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	loop: for {
 		s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
 		if s >= 1 {
@@ -25,7 +25,7 @@ _futex_signal :: proc(f: ^Futex) {
 	}
 }
 
-_futex_broadcast :: proc(f: ^Futex) {
+_futex_broadcast :: proc "contextless" (f: ^Futex) {
 	loop: for {
 		s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
 		if s >= 0 {

+ 4 - 4
core/sync/futex_windows.odin

@@ -39,22 +39,22 @@ CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr,
 }
 
 
-_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
+_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> bool {
 	expect := expect
 	return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
 }
 
-_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> bool {
 	expect := expect
 	// NOTE(bill): for some bizarre reason, this has be a negative number
 	timeout := -i64(duration / 100)
 	return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
 }
 
-_futex_signal :: proc(f: ^Futex) {
+_futex_signal :: proc "contextless" (f: ^Futex) {
 	WakeByAddressSingle(f)
 }
 
-_futex_broadcast :: proc(f: ^Futex) {
+_futex_broadcast :: proc "contextless" (f: ^Futex) {
 	WakeByAddressAll(f)
 }

+ 44 - 28
core/sync/primitives.odin

@@ -1,5 +1,6 @@
 package sync
 
+import "core:runtime"
 import "core:time"
 
 current_thread_id :: proc "contextless" () -> int {
@@ -15,17 +16,17 @@ Mutex :: struct {
 }
 
 // mutex_lock locks m
-mutex_lock :: proc(m: ^Mutex) {
+mutex_lock :: proc "contextless" (m: ^Mutex) {
 	_mutex_lock(m)
 }
 
 // mutex_unlock unlocks m
-mutex_unlock :: proc(m: ^Mutex) {
+mutex_unlock :: proc "contextless" (m: ^Mutex) {
 	_mutex_unlock(m)
 }
 
 // mutex_try_lock tries to lock m, will return true on success, and false on failure
-mutex_try_lock :: proc(m: ^Mutex) -> bool {
+mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
 	return _mutex_try_lock(m)
 }
 
@@ -36,7 +37,7 @@ Example:
 	}
 */
 @(deferred_in=mutex_unlock)
-mutex_guard :: proc(m: ^Mutex) -> bool {
+mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
 	mutex_lock(m)
 	return true
 }
@@ -52,32 +53,32 @@ RW_Mutex :: struct {
 
 // rw_mutex_lock locks rw for writing (with a single writer)
 // If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
-rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
 	_rw_mutex_lock(rw)
 }
 
 // rw_mutex_unlock unlocks rw for writing (with a single writer)
-rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 	_rw_mutex_unlock(rw)
 }
 
 // rw_mutex_try_lock tries to lock rw for writing (with a single writer)
-rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 	return _rw_mutex_try_lock(rw)
 }
 
 // rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
-rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
 	_rw_mutex_shared_lock(rw)
 }
 
 // rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
-rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 	_rw_mutex_shared_unlock(rw)
 }
 
 // rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
-rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 	return _rw_mutex_try_shared_lock(rw)
 }
 /*
@@ -87,7 +88,7 @@ Example:
 	}
 */
 @(deferred_in=rw_mutex_unlock)
-rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool {
+rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
 	rw_mutex_lock(m)
 	return true
 }
@@ -99,7 +100,7 @@ Example:
 	}
 */
 @(deferred_in=rw_mutex_shared_unlock)
-rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
+rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
 	rw_mutex_shared_lock(m)
 	return true
 }
@@ -114,15 +115,15 @@ Recursive_Mutex :: struct {
 	impl: _Recursive_Mutex,
 }
 
-recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
 	_recursive_mutex_lock(m)
 }
 
-recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
 	_recursive_mutex_unlock(m)
 }
 
-recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
 	return _recursive_mutex_try_lock(m)
 }
 
@@ -133,7 +134,7 @@ Example:
 	}
 */
 @(deferred_in=recursive_mutex_unlock)
-recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool {
+recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
 	recursive_mutex_lock(m)
 	return true
 }
@@ -147,22 +148,22 @@ Cond :: struct {
 	impl: _Cond,
 }
 
-cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
 	_cond_wait(c, m)
 }
 
-cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}
 	return _cond_wait_with_timeout(c, m, duration)
 }
 
-cond_signal :: proc(c: ^Cond) {
+cond_signal :: proc "contextless" (c: ^Cond) {
 	_cond_signal(c)
 }
 
-cond_broadcast :: proc(c: ^Cond) {
+cond_broadcast :: proc "contextless" (c: ^Cond) {
 	_cond_broadcast(c)
 }
 
@@ -175,15 +176,15 @@ Sema :: struct {
 	impl: _Sema,
 }
 
-sema_post :: proc(s: ^Sema, count := 1) {
+sema_post :: proc "contextless" (s: ^Sema, count := 1) {
 	_sema_post(s, count)
 }
 
-sema_wait :: proc(s: ^Sema) {
+sema_wait :: proc "contextless" (s: ^Sema) {
 	_sema_wait(s)
 }
 
-sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
+sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
 	return _sema_wait_with_timeout(s, duration)
 }
 
@@ -194,16 +195,16 @@ sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
 // An Futex must not be copied after first use
 Futex :: distinct u32
 
-futex_wait :: proc(f: ^Futex, expected: u32) {
+futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
 	if u32(atomic_load_explicit(f, .Acquire)) != expected {
 		return
 	}
 	
-	assert(_futex_wait(f, expected), "futex_wait failure")
+	_assert(_futex_wait(f, expected), "futex_wait failure")
 }
 
 // returns true if the wait happened within the duration, false if it exceeded the time duration
-futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
 	if u32(atomic_load_explicit(f, .Acquire)) != expected {
 		return true
 	}
@@ -214,10 +215,25 @@ futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duratio
 	return _futex_wait_with_timeout(f, expected, duration)
 }
 
-futex_signal :: proc(f: ^Futex) {
+futex_signal :: proc "contextless" (f: ^Futex) {
 	_futex_signal(f)
 }
 
-futex_broadcast :: proc(f: ^Futex) {
+futex_broadcast :: proc "contextless" (f: ^Futex) {
 	_futex_broadcast(f)
 }
+
+
+@(private)
+_assert :: proc "contextless" (cond: bool, msg: string) {
+	if !cond {
+		_panic(msg)
+	}
+}
+
+@(private)
+_panic :: proc "contextless" (msg: string) -> ! {
+	runtime.print_string(msg)
+	runtime.print_byte('\n')
+	runtime.trap()
+}

+ 26 - 26
core/sync/primitives_atomic.odin

@@ -18,9 +18,9 @@ Atomic_Mutex :: struct {
 }
 
 // atomic_mutex_lock locks m
-atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
+atomic_mutex_lock :: proc "contextless" (m: ^Atomic_Mutex) {
 	@(cold)
-	lock_slow :: proc(m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
+	lock_slow :: proc "contextless" (m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
 		new_state := curr_state // Make a copy of it
 
 		spin_lock: for spin in 0..<i32(100) {
@@ -58,9 +58,9 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
 }
 
 // atomic_mutex_unlock unlocks m
-atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
+atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
 	@(cold)
-	unlock_slow :: proc(m: ^Atomic_Mutex) {
+	unlock_slow :: proc "contextless" (m: ^Atomic_Mutex) {
 		futex_signal((^Futex)(&m.state))
 	}
 
@@ -76,7 +76,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
 }
 
 // atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
-atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
+atomic_mutex_try_lock :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
 	_, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
 	return ok
 }
@@ -88,7 +88,7 @@ Example:
 	}
 */
 @(deferred_in=atomic_mutex_unlock)
-atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
+atomic_mutex_guard :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
 	atomic_mutex_lock(m)
 	return true
 }
@@ -117,7 +117,7 @@ Atomic_RW_Mutex :: struct {
 
 // atomic_rw_mutex_lock locks rw for writing (with a single writer)
 // If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
-atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
+atomic_rw_mutex_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
 	_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer)
 	atomic_mutex_lock(&rw.mutex)
 
@@ -128,13 +128,13 @@ atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
 }
 
 // atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
-atomic_rw_mutex_unlock :: proc(rw: ^Atomic_RW_Mutex) {
+atomic_rw_mutex_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
 	_ = atomic_and(&rw.state, ~Atomic_RW_Mutex_State_Is_Writing)
 	atomic_mutex_unlock(&rw.mutex)
 }
 
 // atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
-atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
+atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
 	if atomic_mutex_try_lock(&rw.mutex) {
 		state := atomic_load(&rw.state)
 		if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
@@ -148,7 +148,7 @@ atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
 }
 
 // atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
-atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
+atomic_rw_mutex_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
 	state := atomic_load(&rw.state)
 	for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
 		ok: bool
@@ -164,7 +164,7 @@ atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
 }
 
 // atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
-atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
+atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
 	state := atomic_sub(&rw.state, Atomic_RW_Mutex_State_Reader)
 
 	if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
@@ -174,7 +174,7 @@ atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
 }
 
 // atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
-atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
+atomic_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
 	state := atomic_load(&rw.state)
 	if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
 		_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
@@ -198,7 +198,7 @@ Example:
 	}
 */
 @(deferred_in=atomic_rw_mutex_unlock)
-atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
+atomic_rw_mutex_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
 	atomic_rw_mutex_lock(m)
 	return true
 }
@@ -210,7 +210,7 @@ Example:
 	}
 */
 @(deferred_in=atomic_rw_mutex_shared_unlock)
-atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
+atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
 	atomic_rw_mutex_shared_lock(m)
 	return true
 }
@@ -228,7 +228,7 @@ Atomic_Recursive_Mutex :: struct {
 	mutex: Mutex,
 }
 
-atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
+atomic_recursive_mutex_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
 	tid := current_thread_id()
 	if tid != m.owner {
 		mutex_lock(&m.mutex)
@@ -238,9 +238,9 @@ atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
 	m.recursion += 1
 }
 
-atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
+atomic_recursive_mutex_unlock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
 	tid := current_thread_id()
-	assert(tid == m.owner)
+	_assert(tid == m.owner, "tid != m.owner")
 	m.recursion -= 1
 	recursion := m.recursion
 	if recursion == 0 {
@@ -253,7 +253,7 @@ atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
 
 }
 
-atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
+atomic_recursive_mutex_try_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
 	tid := current_thread_id()
 	if m.owner == tid {
 		return mutex_try_lock(&m.mutex)
@@ -274,7 +274,7 @@ Example:
 	}
 */
 @(deferred_in=atomic_recursive_mutex_unlock)
-atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
+atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
 	atomic_recursive_mutex_lock(m)
 	return true
 }
@@ -289,7 +289,7 @@ Atomic_Cond :: struct {
 	state: Futex,
 }
 
-atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
+atomic_cond_wait :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex) {
 	state := u32(atomic_load_explicit(&c.state, .Relaxed))
 	unlock(m)
 	futex_wait(&c.state, state)
@@ -297,7 +297,7 @@ atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
 
 }
 
-atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
+atomic_cond_wait_with_timeout :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
 	state := u32(atomic_load_explicit(&c.state, .Relaxed))
 	unlock(m)
 	ok = futex_wait_with_timeout(&c.state, state, duration)
@@ -306,12 +306,12 @@ atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duratio
 }
 
 
-atomic_cond_signal :: proc(c: ^Atomic_Cond) {
+atomic_cond_signal :: proc "contextless" (c: ^Atomic_Cond) {
 	atomic_add_explicit(&c.state, 1, .Release)
 	futex_signal(&c.state)
 }
 
-atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
+atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) {
 	atomic_add_explicit(&c.state, 1, .Release)
 	futex_broadcast(&c.state)
 }
@@ -324,7 +324,7 @@ Atomic_Sema :: struct {
 	count: Futex,
 }
 
-atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
+atomic_sema_post :: proc "contextless" (s: ^Atomic_Sema, count := 1) {
 	atomic_add_explicit(&s.count, Futex(count), .Release)
 	if count == 1 {
 		futex_signal(&s.count)
@@ -333,7 +333,7 @@ atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
 	}
 }
 
-atomic_sema_wait :: proc(s: ^Atomic_Sema) {
+atomic_sema_wait :: proc "contextless" (s: ^Atomic_Sema) {
 	for {
 		original_count := atomic_load_explicit(&s.count, .Relaxed)
 		for original_count == 0 {
@@ -346,7 +346,7 @@ atomic_sema_wait :: proc(s: ^Atomic_Sema) {
 	}
 }
 
-atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration) -> bool {
+atomic_sema_wait_with_timeout :: proc "contextless" (s: ^Atomic_Sema, duration: time.Duration) -> bool {
 	if duration <= 0 {
 		return false
 	}

+ 78 - 19
core/sync/primitives_internal.odin

@@ -2,20 +2,31 @@
 package sync
 
 import "core:time"
+import vg "core:sys/valgrind"
+_ :: vg
 
 _Sema :: struct {
 	atomic: Atomic_Sema,
 }
 
-_sema_post :: proc(s: ^Sema, count := 1) {
+_sema_post :: proc "contextless" (s: ^Sema, count := 1) {
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_sem_post_pre(s)
+	}
 	atomic_sema_post(&s.impl.atomic, count)
 }
 
-_sema_wait :: proc(s: ^Sema) {
+_sema_wait :: proc "contextless" (s: ^Sema) {
 	atomic_sema_wait(&s.impl.atomic)
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_sem_wait_post(s)
+	}
 }
 
-_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
+_sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
+	when ODIN_VALGRIND_SUPPORT {
+		defer vg.helgrind_sem_wait_post(s)
+	}
 	return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
 }
 
@@ -25,7 +36,12 @@ _Recursive_Mutex :: struct {
 	recursion: i32,
 }
 
-_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+_recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_mutex_lock_pre(m, false)
+		defer vg.helgrind_mutex_lock_post(m)
+	}
+
 	tid := Futex(current_thread_id())
 	for {
 		prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
@@ -40,7 +56,12 @@ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
 	}
 }
 
-_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+_recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
+	when ODIN_VALGRIND_SUPPORT {
+		vg.helgrind_mutex_unlock_pre(m)
+		defer vg.helgrind_mutex_unlock_post(m)
+	}
+
 	m.impl.recursion -= 1
 	if m.impl.recursion != 0 {
 		return
@@ -52,7 +73,7 @@ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
 
 }
 
-_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+_recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
 	tid := Futex(current_thread_id())
 	prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
 	switch prev_owner {
@@ -70,15 +91,27 @@ when ODIN_OS != .Windows {
 		mutex: Atomic_Mutex,
 	}
 
-	_mutex_lock :: proc(m: ^Mutex) {
+	_mutex_lock :: proc "contextless" (m: ^Mutex) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_mutex_lock_pre(m, false)
+			defer vg.helgrind_mutex_lock_post(m)
+		}
 		atomic_mutex_lock(&m.impl.mutex)
 	}
 
-	_mutex_unlock :: proc(m: ^Mutex) {
+	_mutex_unlock :: proc "contextless" (m: ^Mutex) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_mutex_unlock_pre(m)
+			defer vg.helgrind_mutex_unlock_post(m)
+		}
 		atomic_mutex_unlock(&m.impl.mutex)
 	}
 
-	_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+	_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_mutex_lock_pre(m, true)
+			defer vg.helgrind_mutex_lock_post(m)
+		}
 		return atomic_mutex_try_lock(&m.impl.mutex)
 	}
 
@@ -86,19 +119,33 @@ when ODIN_OS != .Windows {
 		cond: Atomic_Cond,
 	}
 
-	_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+	_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
+		when ODIN_VALGRIND_SUPPORT {
+			_ = vg.helgrind_cond_wait_pre(c, m)
+			defer _ = vg.helgrind_cond_wait_post(c, m)
+		}
 		atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
 	}
 
-	_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+	_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+		when ODIN_VALGRIND_SUPPORT {
+			_ = vg.helgrind_cond_wait_pre(c, m)
+			defer _ = vg.helgrind_cond_wait_post(c, m)
+		}
 		return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
 	}
 
-	_cond_signal :: proc(c: ^Cond) {
+	_cond_signal :: proc "contextless" (c: ^Cond) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_cond_signal_pre(c)
+		}
 		atomic_cond_signal(&c.impl.cond)
 	}
 
-	_cond_broadcast :: proc(c: ^Cond) {
+	_cond_broadcast :: proc "contextless" (c: ^Cond) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_cond_broadcast_pre(c)
+		}
 		atomic_cond_broadcast(&c.impl.cond)
 	}
 
@@ -107,27 +154,39 @@ when ODIN_OS != .Windows {
 		mutex: Atomic_RW_Mutex,
 	}
 
-	_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+	_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_rwlock_lock_pre(rw, true)
+		}
 		atomic_rw_mutex_lock(&rw.impl.mutex)
 	}
 
-	_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+	_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 		atomic_rw_mutex_unlock(&rw.impl.mutex)
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_rwlock_unlock_post(rw, true)
+		}
 	}
 
-	_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+	_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 		return atomic_rw_mutex_try_lock(&rw.impl.mutex)
 	}
 
-	_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+	_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_rwlock_lock_pre(rw, false)
+		}
 		atomic_rw_mutex_shared_lock(&rw.impl.mutex)
 	}
 
-	_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+	_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 		atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
+		when ODIN_VALGRIND_SUPPORT {
+			vg.helgrind_rwlock_unlock_post(rw, false)
+		}
 	}
 
-	_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+	_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 		return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
 	}
 }

+ 13 - 13
core/sync/primitives_windows.odin

@@ -13,15 +13,15 @@ _Mutex :: struct {
 	srwlock: win32.SRWLOCK,
 }
 
-_mutex_lock :: proc(m: ^Mutex) {
+_mutex_lock :: proc "contextless" (m: ^Mutex) {
 	win32.AcquireSRWLockExclusive(&m.impl.srwlock)
 }
 
-_mutex_unlock :: proc(m: ^Mutex) {
+_mutex_unlock :: proc "contextless" (m: ^Mutex) {
 	win32.ReleaseSRWLockExclusive(&m.impl.srwlock)
 }
 
-_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
 	return bool(win32.TryAcquireSRWLockExclusive(&m.impl.srwlock))
 }
 
@@ -29,27 +29,27 @@ _RW_Mutex :: struct {
 	srwlock: win32.SRWLOCK,
 }
 
-_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
 	win32.AcquireSRWLockExclusive(&rw.impl.srwlock)
 }
 
-_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 	win32.ReleaseSRWLockExclusive(&rw.impl.srwlock)
 }
 
-_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 	return bool(win32.TryAcquireSRWLockExclusive(&rw.impl.srwlock))
 }
 
-_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
 	win32.AcquireSRWLockShared(&rw.impl.srwlock)
 }
 
-_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
 	win32.ReleaseSRWLockShared(&rw.impl.srwlock)
 }
 
-_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
 	return bool(win32.TryAcquireSRWLockShared(&rw.impl.srwlock))
 }
 
@@ -58,22 +58,22 @@ _Cond :: struct {
 	cond: win32.CONDITION_VARIABLE,
 }
 
-_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
 	_ = win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, win32.INFINITE, 0)
 }
 
-_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
 	duration := u32(duration / time.Millisecond)
 	ok := win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, duration, 0)
 	return bool(ok)
 }
 
 
-_cond_signal :: proc(c: ^Cond) {
+_cond_signal :: proc "contextless" (c: ^Cond) {
 	win32.WakeConditionVariable(&c.impl.cond)
 }
 
-_cond_broadcast :: proc(c: ^Cond) {
+_cond_broadcast :: proc "contextless" (c: ^Cond) {
 	win32.WakeAllConditionVariable(&c.impl.cond)
 }
 

+ 38 - 0
core/sys/windows/dwmapi.odin

@@ -3,7 +3,45 @@ package sys_windows
 
 foreign import dwmapi "system:Dwmapi.lib"
 
+DWMWINDOWATTRIBUTE :: enum {
+	DWMWA_NCRENDERING_ENABLED,
+	DWMWA_NCRENDERING_POLICY,
+	DWMWA_TRANSITIONS_FORCEDISABLED,
+	DWMWA_ALLOW_NCPAINT,
+	DWMWA_CAPTION_BUTTON_BOUNDS,
+	DWMWA_NONCLIENT_RTL_LAYOUT,
+	DWMWA_FORCE_ICONIC_REPRESENTATION,
+	DWMWA_FLIP3D_POLICY,
+	DWMWA_EXTENDED_FRAME_BOUNDS,
+	DWMWA_HAS_ICONIC_BITMAP,
+	DWMWA_DISALLOW_PEEK,
+	DWMWA_EXCLUDED_FROM_PEEK,
+	DWMWA_CLOAK,
+	DWMWA_CLOAKED,
+	DWMWA_FREEZE_REPRESENTATION,
+	DWMWA_PASSIVE_UPDATE_MODE,
+	DWMWA_USE_HOSTBACKDROPBRUSH,
+	DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
+	DWMWA_WINDOW_CORNER_PREFERENCE = 33,
+	DWMWA_BORDER_COLOR,
+	DWMWA_CAPTION_COLOR,
+	DWMWA_TEXT_COLOR,
+	DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
+	DWMWA_SYSTEMBACKDROP_TYPE,
+  	DWMWA_LAST,
+}
+
+DWMNCRENDERINGPOLICY :: enum {
+	DWMNCRP_USEWINDOWSTYLE,
+	DWMNCRP_DISABLED,
+	DWMNCRP_ENABLED,
+	DWMNCRP_LAST,
+}
+
 @(default_calling_convention="stdcall")
 foreign dwmapi {
 	DwmFlush :: proc() -> HRESULT ---
+	DwmIsCompositionEnabled :: proc(pfEnabled: ^BOOL) -> HRESULT ---
+	DwmExtendFrameIntoClientArea :: proc(hWnd: HWND, pMarInset: PMARGINS) -> HRESULT ---
+	DwmSetWindowAttribute :: proc(hWnd: HWND, dwAttribute: DWORD, pvAttribute: LPCVOID, cbAttribute: DWORD) -> HRESULT ---
 }

+ 3 - 0
core/sys/windows/gdi32.odin

@@ -62,6 +62,7 @@ foreign gdi32 {
 
 	SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
 	ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
+	DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: c_int, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
 	SwapBuffers :: proc(HDC) -> BOOL ---
 
 	SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
@@ -78,6 +79,8 @@ foreign gdi32 {
 	TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
 	GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
 	GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
+
+	CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
 }
 
 RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {

+ 48 - 0
core/sys/windows/kernel32.odin

@@ -370,6 +370,8 @@ foreign kernel32 {
 		lpTotalNumberOfBytes: PULARGE_INTEGER,
 		lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
 	) -> BOOL ---
+
+	GetLogicalProcessorInformation :: proc(buffer: ^SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returnedLength: PDWORD) -> BOOL ---
 }
 
 
@@ -999,3 +1001,49 @@ foreign kernel32 {
 	ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
 	SwitchToFiber :: proc(lpFiber: LPVOID) ---
 }
+
+LOGICAL_PROCESSOR_RELATIONSHIP :: enum c_int {
+	RelationProcessorCore,
+	RelationNumaNode,
+	RelationCache,
+	RelationProcessorPackage,
+	RelationGroup,
+	RelationProcessorDie,
+	RelationNumaNodeEx,
+	RelationProcessorModule,
+	RelationAll = 0xffff,
+}
+
+PROCESSOR_CACHE_TYPE :: enum c_int {
+	CacheUnified,
+	CacheInstruction,
+	CacheData,
+	CacheTrace,
+}
+
+CACHE_DESCRIPTOR :: struct {
+	Level: BYTE,
+	Associativity: BYTE,
+	LineSize: WORD,
+	Size: DWORD,
+	Type: PROCESSOR_CACHE_TYPE,
+}
+
+ProcessorCore :: struct {
+	Flags: BYTE,
+}
+NumaNode :: struct {
+	NodeNumber: DWORD,
+}
+DUMMYUNIONNAME_u :: struct #raw_union {
+	Core: ProcessorCore,
+	Node: NumaNode,
+	Cache: CACHE_DESCRIPTOR,
+	Reserved: [2]ULONGLONG,
+}
+
+SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
+	ProcessorMask: ULONG_PTR,
+	Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
+	DummyUnion: DUMMYUNIONNAME_u,
+}

+ 33 - 0
core/sys/windows/shell32.odin

@@ -22,4 +22,37 @@ foreign shell32 {
 	) -> c_int ---
 	SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
 	SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
+	SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR --- 
 }
+
+APPBARDATA :: struct {
+	cbSize: DWORD,
+	hWnd: HWND,
+	uCallbackMessage: UINT,
+	uEdge: UINT,
+	rc: RECT,
+	lParam: LPARAM,
+}
+PAPPBARDATA :: ^APPBARDATA
+ 
+ABM_NEW              :: 0x00000000
+ABM_REMOVE           :: 0x00000001
+ABM_QUERYPOS         :: 0x00000002
+ABM_SETPOS           :: 0x00000003
+ABM_GETSTATE         :: 0x00000004
+ABM_GETTASKBARPOS    :: 0x00000005
+ABM_ACTIVATE         :: 0x00000006 
+ABM_GETAUTOHIDEBAR   :: 0x00000007
+ABM_SETAUTOHIDEBAR   :: 0x00000008 
+ABM_WINDOWPOSCHANGED :: 0x0000009
+ABM_SETSTATE         :: 0x0000000a
+ABN_STATECHANGE      :: 0x0000000
+ABN_POSCHANGED       :: 0x0000001
+ABN_FULLSCREENAPP    :: 0x0000002
+ABN_WINDOWARRANGE    :: 0x0000003
+ABS_AUTOHIDE         :: 0x0000001
+ABS_ALWAYSONTOP      :: 0x0000002
+ABE_LEFT             :: 0
+ABE_TOP              :: 1
+ABE_RIGHT            :: 2
+ABE_BOTTOM           :: 3

+ 20 - 0
core/sys/windows/types.odin

@@ -38,6 +38,7 @@ HHOOK :: distinct HANDLE
 HKEY :: distinct HANDLE
 HDESK :: distinct HANDLE
 HFONT :: distinct HANDLE
+HRGN :: distinct HANDLE
 BOOL :: distinct b32
 BYTE :: distinct u8
 BOOLEAN :: distinct b8
@@ -1554,6 +1555,25 @@ WA_INACTIVE    :: 0
 WA_ACTIVE      :: 1
 WA_CLICKACTIVE :: 2
 
+// Struct pointed to by WM_GETMINMAXINFO lParam
+MINMAXINFO :: struct {
+	ptReserved: POINT,
+	ptMaxSize: POINT,
+	ptMaxPosition: POINT,
+	ptMinTrackSize: POINT,
+	ptMaxTrackSize: POINT,
+}
+PMINMAXINFO  :: ^MINMAXINFO
+LPMINMAXINFO :: PMINMAXINFO
+
+MONITORINFO :: struct {
+	cbSize: DWORD,
+	rcMonitor: RECT,
+	rcWork: RECT,
+	dwFlags: DWORD,
+}
+LPMONITORINFO :: ^MONITORINFO
+
 // SetWindowsHook() codes
 WH_MIN             :: -1
 WH_MSGFILTER       :: -1

+ 38 - 0
core/sys/windows/user32.odin

@@ -100,6 +100,7 @@ foreign user32 {
 	AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
 
 	SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
+	GetMonitorInfoW :: proc(hMonitor: HMONITOR, lpmi: LPMONITORINFO) -> BOOL ---
 
 	GetWindowDC :: proc(hWnd: HWND) -> HDC ---
 	GetDC :: proc(hWnd: HWND) -> HDC ---
@@ -122,6 +123,8 @@ foreign user32 {
 
 	GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
 	GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+	
+	GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
 
 	MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
 
@@ -202,6 +205,17 @@ foreign user32 {
 	GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
 	GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
 	RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
+
+	SetLayeredWindowAttributes  :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
+
+	FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
+	EqualRect :: proc(lprc1: ^RECT, lprc2: ^RECT) -> BOOL ---
+
+	GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL ---
+	GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
+	SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
+	CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
+	GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
 }
 
 CreateWindowW :: #force_inline proc "stdcall" (
@@ -432,3 +446,27 @@ RI_MOUSE_BUTTON_5_DOWN :: 0x0100
 RI_MOUSE_BUTTON_5_UP :: 0x0200
 RI_MOUSE_WHEEL :: 0x0400
 RI_MOUSE_HWHEEL :: 0x0800
+
+WINDOWPLACEMENT :: struct {
+	length: UINT,
+	flags: UINT,
+	showCmd: UINT,
+	ptMinPosition: POINT,
+  	ptMaxPosition: POINT,
+  	rcNormalPosition: RECT,
+  	rcDevice: RECT,
+}
+
+WINDOWINFO :: struct {
+	cbSize: DWORD,
+	rcWindow: RECT,
+	rcClient: RECT,
+	dwStyle: DWORD,
+	dwExStyle: DWORD,
+	dwWindowStatus: DWORD,
+	cxWindowBorders: UINT,
+	cyWindowBorders: UINT,
+	atomWindowType: ATOM,
+	wCreatorVersion: WORD,
+}
+PWINDOWINFO :: ^WINDOWINFO

+ 12 - 0
core/sys/windows/ux_theme.odin

@@ -0,0 +1,12 @@
+// +build windows
+package sys_windows
+
+foreign import uxtheme "system:UxTheme.lib"
+
+MARGINS :: distinct [4]int
+PMARGINS :: ^MARGINS
+
+@(default_calling_convention="stdcall")
+foreign uxtheme {
+    IsThemeActive :: proc() -> BOOL ---
+}

+ 959 - 0
core/text/match/strlib.odin

@@ -0,0 +1,959 @@
+package text_match
+
+import "core:runtime"
+import "core:unicode"
+import "core:unicode/utf8"
+import "core:strings"
+
+MAX_CAPTURES :: 32
+
+Capture :: struct {
+	init: int,
+	len: int,
+}
+
+Match :: struct {
+	byte_start, byte_end: int,
+}
+
+Error :: enum {
+	OK,
+	OOB,
+	Invalid_Capture_Index,
+	Invalid_Pattern_Capture,
+	Unfinished_Capture,
+	Malformed_Pattern,
+	Rune_Error,
+	Match_Invalid,
+}
+
+L_ESC :: '%'
+CAP_POSITION :: -2
+CAP_UNFINISHED :: -1
+INVALID :: -1
+
+Match_State :: struct {
+	src: string,
+	pattern: string,
+	level: int,
+	capture: [MAX_CAPTURES]Capture,
+}
+
+match_class :: proc(c: rune, cl: rune) -> (res: bool) {
+	switch unicode.to_lower(cl) {
+	case 'a': res = is_alpha(c)
+	case 'c': res = is_cntrl(c)
+	case 'd': res = is_digit(c)
+	case 'g': res = is_graph(c)
+	case 'l': res = is_lower(c)
+	case 'p': res = is_punct(c)
+	case 's': res = is_space(c)
+	case 'u': res = is_upper(c)
+	case 'w': res = is_alnum(c)
+	case 'x': res = is_xdigit(c)
+	case: return cl == c
+	}
+
+	return is_lower(cl) ? res : !res
+}
+
+is_alpha :: unicode.is_alpha
+is_digit :: unicode.is_digit
+is_lower :: unicode.is_lower
+is_upper :: unicode.is_upper
+is_punct :: unicode.is_punct
+is_space :: unicode.is_space
+is_cntrl :: unicode.is_control
+
+is_alnum :: proc(c: rune) -> bool {
+	return unicode.is_alpha(c) || unicode.is_digit(c)
+}
+
+is_graph :: proc(c: rune) -> bool {
+	return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
+}
+
+is_xdigit :: proc(c: rune) -> bool {
+	return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
+}
+
+// find the first utf8 charater and its size, return an error if the character is an error
+utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
+	c, size = utf8.decode_rune_in_string(bytes)
+
+	if c == utf8.RUNE_ERROR {
+		err = .Rune_Error
+	}
+
+	return
+}
+
+// find the first utf8 charater and its size and advance the index
+// return an error if the character is an error
+utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
+	size: int
+	c, size = utf8.decode_rune_in_string(bytes[index^:])
+
+	if c == utf8.RUNE_ERROR {
+		err = .Rune_Error
+	}
+
+	index^ += size
+	return
+}
+
+// continuation byte?
+is_cont :: proc(b: byte) -> bool {
+	return b & 0xc0 == 0x80
+}
+
+utf8_prev :: proc(bytes: string, a, b: int) -> int {
+	b := b
+
+	for a < b && is_cont(bytes[b - 1]) {
+		b -= 1
+	}
+
+	return a < b ? b - 1 : a
+}
+
+utf8_next :: proc(bytes: string, a: int) -> int {
+	a := a
+	b := len(bytes)
+
+	for a < b - 1 && is_cont(bytes[a + 1]) {
+		a += 1
+	}
+
+	return a < b ? a + 1 : b
+}
+
+check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
+	l := int(l - '1')
+	
+	if l < 0 || l >= ms.level || ms.capture[l].len == CAP_UNFINISHED {
+		return 0, .Invalid_Capture_Index
+	}
+
+	return l, .OK
+}
+
+capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
+	level := ms.level - 1
+
+	for level >= 0 {
+		if ms.capture[level].len == CAP_UNFINISHED {
+			return level, .OK
+		}
+
+		level -= 1
+	}
+
+	return 0, .Invalid_Pattern_Capture
+}
+
+class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
+	step = p
+	ch := utf8_advance(ms.pattern, &step) or_return
+
+	switch ch {
+	case L_ESC: 
+		if step == len(ms.pattern) {
+			err = .Malformed_Pattern
+			return
+		}
+
+		utf8_advance(ms.pattern, &step) or_return
+
+	case '[': 
+		// fine with step by 1
+		if step + 1 < len(ms.pattern) && ms.pattern[step] == '^' {
+			step += 1
+		}
+
+		// run till end is reached
+		for {
+			if step == len(ms.pattern) {
+				err = .Malformed_Pattern
+				return
+			}
+
+			if ms.pattern[step] == ']' {
+				break
+			}
+
+			// dont care about utf8 here
+			step += 1
+
+			if step < len(ms.pattern) && ms.pattern[step] == L_ESC {
+				// skip escapes like '%'
+				step += 1
+			}
+		}
+
+		// advance last time
+		step += 1
+	}
+
+	return
+}
+
+match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
+	sig = true
+	p := p
+
+	if ms.pattern[p + 1] == '^' {
+		p += 1
+		sig = false
+	}
+
+	// while inside of class range
+	for p < ec {
+		char := utf8_advance(ms.pattern, &p) or_return
+
+		// e.g. %a
+		if char == L_ESC { 
+			next := utf8_advance(ms.pattern, &p) or_return
+
+			if match_class(c, next) {
+				return
+			}
+		} else {
+			next, next_size := utf8_peek(ms.pattern[p:]) or_return
+
+			// TODO test case for [a-???] where ??? is missing
+			if next == '-' && p + next_size < len(ms.pattern) {
+				// advance 2 codepoints
+				p += next_size
+				last := utf8_advance(ms.pattern, &p) or_return
+
+				if char <= c && c <= last {
+					return
+				}
+			} else if char == c {
+				return
+			}
+		}
+	}
+
+	sig = !sig
+	return
+}
+
+single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
+	if s >= len(ms.src) {
+		return
+	}
+
+	pchar, psize := utf8_peek(ms.pattern[p:]) or_return
+	schar, ssize := utf8_peek(ms.src[s:]) or_return
+	schar_size = ssize
+
+	switch pchar {
+	case '.': matched = true
+	case L_ESC: 
+		pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
+		matched = match_class(schar, pchar_next)
+	case '[': matched = match_bracket_class(ms, schar, p, ep - 1) or_return
+	case: matched = schar == pchar
+	}
+
+	return
+}
+
+match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
+	if p >= len(ms.pattern) - 1 {
+		return INVALID, .Invalid_Pattern_Capture
+	}
+
+	schar, ssize := utf8_peek(ms.src[s:]) or_return
+	pchar, psize := utf8_peek(ms.pattern[p:]) or_return
+
+	// skip until the src and pattern match
+	if schar != pchar {
+		return INVALID, .OK
+	}
+
+	s_begin := s
+	cont := 1
+	s := s + ssize
+	begin := pchar
+	end, _ := utf8_peek(ms.pattern[p + psize:]) or_return
+
+	for s < len(ms.src) {
+		ch := utf8_advance(ms.src, &s) or_return
+
+		switch ch{
+		case end:
+			cont -= 1
+
+			if cont == 0 {
+				return s, .OK
+			}
+
+		case begin:
+			cont += 1
+		}
+	}
+
+	return INVALID, .OK
+}
+
+max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
+	m := s
+
+	// count up matches
+	for {
+		matched, size := single_match(ms, m, p, ep) or_return
+		
+		if !matched {
+			break
+		}
+
+		m += size
+	}
+
+	for s <= m {
+		result := match(ms, m, ep + 1) or_return
+
+		if result != INVALID {
+			return result, .OK
+		}
+
+		if s == m {
+			break
+		}
+
+		m = utf8_prev(ms.src, s, m)
+	}
+
+	return INVALID, .OK
+}
+
+min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
+	s := s
+
+	for {
+		result := match(ms, s, ep + 1) or_return
+
+		if result != INVALID {
+			return result, .OK
+		} else {
+			// TODO receive next step maybe?
+			matched, rune_size := single_match(ms, s, p, ep) or_return
+
+			if matched {
+				s += rune_size
+			} else {
+				return INVALID, .OK
+			}
+		}
+	}
+}
+
+start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
+	level := ms.level
+
+	ms.capture[level].init = s
+	ms.capture[level].len = what
+	ms.level += 1
+
+	res = match(ms, s, p) or_return
+	if res == INVALID {
+		ms.level -= 1
+	}
+	return
+}
+
+end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
+	l := capture_to_close(ms) or_return
+	
+	// TODO double check, could do string as int index
+	ms.capture[l].len = s - ms.capture[l].init
+
+	res = match(ms, s, p) or_return
+	if res == INVALID {
+		ms.capture[l].len = CAP_UNFINISHED
+	}
+	return
+}
+
+match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
+	index := check_capture(ms, char) or_return
+	length := ms.capture[index].len
+
+	if len(ms.src) - s >= length {
+		return s + length, .OK
+	}
+
+	return INVALID, .OK
+}
+
+match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
+	s := s
+	p := p
+
+	if p == len(ms.pattern) {
+		return s, .OK
+	}
+
+	// NOTE we can walk by ascii steps if we know the characters are ascii
+	char, _ := utf8_peek(ms.pattern[p:]) or_return
+	switch char {
+	case '(': 
+		if p + 1 < len(ms.pattern) && ms.pattern[p + 1] == ')' {
+			s = start_capture(ms, s, p + 2, CAP_POSITION) or_return
+		} else {
+			s = start_capture(ms, s, p + 1, CAP_UNFINISHED) or_return
+		}
+
+	case ')': 
+		s = end_capture(ms, s, p + 1) or_return
+
+	case '$': 
+		if p + 1 != len(ms.pattern) {
+			return match_default(ms, s, p)
+		} 
+
+		if len(ms.src) != s {
+			s = INVALID
+		}
+
+	case L_ESC: 
+		// stop short patterns like "%" only
+		if p + 1 >= len(ms.pattern) {
+			err = .OOB
+			return
+		}
+
+		switch ms.pattern[p + 1] {
+		// balanced string
+		case 'b': 
+			s = match_balance(ms, s, p + 2) or_return
+
+			if s != INVALID {
+				// eg after %b()
+				return match(ms, s, p + 4)
+			}
+
+		// frontier
+		case 'f':
+			p += 2
+			
+			if ms.pattern[p] != '[' {
+				return INVALID, .Invalid_Pattern_Capture
+			}
+
+			ep := class_end(ms, p) or_return
+			previous, current: rune
+
+			// get previous
+			if s != 0 {
+				temp := utf8_prev(ms.src, 0, s)
+				previous, _ = utf8_peek(ms.src[temp:]) or_return
+			}
+
+			// get current
+			if s != len(ms.src) {
+				current, _ = utf8_peek(ms.src[s:]) or_return
+			}
+
+			m1 := match_bracket_class(ms, previous, p, ep - 1) or_return
+			m2 := match_bracket_class(ms, current, p, ep - 1) or_return
+
+			if !m1 && m2 {
+				return match(ms, s, ep)
+			}
+
+			s = INVALID
+
+		// capture group
+		case '0'..<'9':
+			s = match_capture(ms, s, rune(ms.pattern[p + 1])) or_return
+
+			if s != INVALID {
+				return match(ms, s, p + 2)
+			}
+
+		case: return match_default(ms, s, p)
+	}
+
+	case: 
+		return match_default(ms, s, p)
+	}
+
+	return s, .OK
+}
+
+match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
+	s := s
+	ep := class_end(ms, p) or_return
+	single_matched, ssize := single_match(ms, s, p, ep) or_return
+
+	if !single_matched {
+		epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
+
+		switch epc {
+		case '*', '?', '-': return match(ms, s, ep + 1)
+		case: s = INVALID
+		}
+	} else {
+		epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
+
+		switch epc {
+		case '?':
+			result := match(ms, s + ssize, ep + 1) or_return
+			
+			if result != INVALID {
+				s = result
+			} else {
+				return match(ms, s, ep + 1)
+			}
+
+		case '+': s = max_expand(ms, s + ssize, p, ep) or_return
+		case '*': s = max_expand(ms, s, p, ep) or_return
+		case '-': s = min_expand(ms, s, p, ep) or_return
+		case: return match(ms, s + ssize, ep)
+		}
+	}
+
+	return s, .OK
+}
+
+push_onecapture :: proc(ms: ^Match_State,  i: int,  s: int, e: int, matches: []Match) -> (err: Error) {
+	if i >= ms.level {
+		if i == 0 {
+			matches[0] = { 0, e - s }
+		} else {
+			err = .Invalid_Capture_Index
+		}
+	} else {
+		init := ms.capture[i].init 
+		length := ms.capture[i].len
+
+		switch length {
+		case CAP_UNFINISHED: err = .Unfinished_Capture
+		case CAP_POSITION: matches[i] = { init, init + 1 }
+		case: matches[i] = { init, init + length }
+		}
+	}
+
+	return
+}
+
+push_captures :: proc(
+	ms: ^Match_State,
+	s: int,
+	e: int,
+	matches: []Match,
+) -> (nlevels: int, err: Error) {
+	nlevels = 1 if ms.level == 0 && s != -1 else ms.level
+
+	for i in 0..<nlevels {
+		push_onecapture(ms, i, s, e, matches) or_return
+	}
+
+	return
+}
+
+// SPECIALS := "^$*+?.([%-"
+// all special characters inside a small ascii array
+SPECIALS_TABLE := [256]bool {
+	'^' = true,
+	'$' = true,
+	'*' = true,
+	'+' = true,
+	'?' = true,
+	'.' = true,
+	'(' = true,
+	'[' = true,
+	'%' = true,
+	'-' = true,
+}
+
+// helper call to quick search for special characters
+index_special :: proc(text: string) -> int {
+	for i in 0..<len(text) {
+		if SPECIALS_TABLE[text[i]] {
+			return i
+		}
+	}
+
+	return -1
+}
+
+lmem_find :: proc(s1, s2: string) -> int {
+	l1 := len(s1)
+	l2 := len(s2)
+
+	if l2 == 0 {
+		return 0
+	} else if l2 > l1 {
+		return -1
+	} else {
+		init := strings.index_byte(s1, s2[0])
+		end := init + l2
+
+		for end <= l1 && init != -1 {
+			init += 1
+
+			if s1[init - 1:end] == s2 {
+				return init - 1
+			} else {
+				next := strings.index_byte(s1[init:], s2[0])
+
+				if next == -1 {
+					return -1
+				} else {
+					init = init + next
+					end = init + l2
+				}
+			}
+		}
+	}
+
+	return -1
+}
+
+// find a pattern with in a haystack with an offset
+// allow_memfind will speed up simple searches
+find_aux :: proc(
+	haystack: string, 
+	pattern: string, 
+	offset: int,
+	allow_memfind: bool,
+	matches: ^[MAX_CAPTURES]Match,
+) -> (captures: int, err: Error) {
+	s := offset
+	p := 0
+
+	specials_idx := index_special(pattern)
+	if allow_memfind && specials_idx == -1 {
+		if index := lmem_find(haystack[s:], pattern); index != -1 {
+			matches[0] = { index + s, index + s + len(pattern) }
+			captures = 1
+			return
+		} else {
+			return
+		}
+	}
+
+	pattern := pattern
+	anchor: bool
+	if len(pattern) > 0 && pattern[0] == '^' {
+		anchor = true
+		pattern = pattern[1:]
+	}
+
+	ms := Match_State {
+		src = haystack,
+		pattern = pattern,
+	}
+
+	for {
+		res := match(&ms, s, p) or_return
+
+		if res != INVALID {
+			// disallow non advancing match
+			if s == res {
+				err = .Match_Invalid
+			} 
+			
+			// NOTE(Skytrias): first result is reserved for a full match
+			matches[0] = { s, res }
+
+			// rest are the actual captures
+			captures = push_captures(&ms, -1, -1, matches[1:]) or_return
+			captures += 1
+
+			return
+		}
+
+		s += 1
+
+		if !(s < len(ms.src) && !anchor) {
+			break
+		}
+	}
+
+	return
+}
+
+// iterative matching which returns the 0th/1st match
+// rest has to be used from captures
+gmatch :: proc(
+	haystack: ^string,
+	pattern: string,
+	captures: ^[MAX_CAPTURES]Match,
+) -> (res: string, ok: bool) {
+	if len(haystack) > 0 {
+		length, err := find_aux(haystack^, pattern, 0, false, captures)
+
+		if length != 0 && err == .OK {
+			ok = true
+			first := length > 1 ? 1 : 0
+			cap := captures[first]
+			res = haystack[cap.byte_start:cap.byte_end]
+			haystack^ = haystack[cap.byte_end:]
+		}
+	} 
+
+	return
+}
+
+// gsub with builder, replace patterns found with the replace content
+gsub_builder :: proc(
+	builder: ^strings.Builder,
+	haystack: string,
+	pattern: string,
+	replace: string,
+) -> string {
+	// find matches
+	captures: [MAX_CAPTURES]Match
+	haystack := haystack
+
+	for {
+		length, err := find_aux(haystack, pattern, 0, false, &captures)
+
+		// done
+		if length == 0 {
+			break
+		}
+
+		if err != .OK {
+			return {}
+		}
+
+		cap := captures[0]
+
+		// write front till capture
+		strings.write_string(builder, haystack[:cap.byte_start])
+
+		// write replacements
+		strings.write_string(builder, replace)
+
+		// advance string till end
+		haystack = haystack[cap.byte_end:]
+	}
+
+	strings.write_string(builder, haystack[:])
+	return strings.to_string(builder^)
+}
+
+// uses temp builder to build initial string - then allocates the result
+gsub_allocator :: proc(
+	haystack: string,
+	pattern: string,
+	replace: string,
+	allocator := context.allocator,
+) -> string {
+	builder := strings.builder_make(0, 256, context.temp_allocator)
+	return gsub_builder(&builder, haystack, pattern, replace)
+}
+
+Gsub_Proc :: proc(
+	// optional passed data
+	data: rawptr, 
+	// word match found
+	word: string, 
+	// current haystack for found captures
+	haystack: string, 
+	// found captures - empty for no captures
+	captures: []Match,
+)
+
+// call a procedure on every match in the haystack
+gsub_with :: proc(
+	haystack: string,
+	pattern: string,
+	data: rawptr,
+	call: Gsub_Proc,
+) {
+	// find matches
+	captures: [MAX_CAPTURES]Match
+	haystack := haystack
+
+	for {
+		length, err := find_aux(haystack, pattern, 0, false, &captures)
+
+		// done
+		if length == 0 || err != .OK {
+			break
+		}
+
+		cap := captures[0]
+
+		word := haystack[cap.byte_start:cap.byte_end]
+		call(data, word, haystack, captures[1:length])
+
+		// advance string till end
+		haystack = haystack[cap.byte_end:]
+	}
+}
+
+gsub :: proc { gsub_builder, gsub_allocator }
+
+// iterative find with zeroth capture only
+gfind :: proc(
+	haystack: ^string,
+	pattern: string,
+	captures: ^[MAX_CAPTURES]Match,
+) -> (res: string, ok: bool) {
+	if len(haystack) > 0 {
+		length, err := find_aux(haystack^, pattern, 0, true, captures)
+
+		if length != 0 && err == .OK {
+			ok = true
+			cap := captures[0]
+			res = haystack[cap.byte_start:cap.byte_end]
+			haystack^ = haystack[cap.byte_end:]
+		}
+	} 
+
+	return
+}
+
+// rebuilds a pattern into a case insensitive pattern
+pattern_case_insensitive_builder :: proc(
+	builder: ^strings.Builder, 
+	pattern: string,
+) -> (res: string) {
+	p := pattern
+	last_percent: bool
+
+	for len(p) > 0 {
+		char, size := utf8.decode_rune_in_string(p)
+
+		if unicode.is_alpha(char) && !last_percent {
+			// write character class in manually
+			strings.write_byte(builder, '[')
+			strings.write_rune(builder, unicode.to_lower(char))
+			strings.write_rune(builder, unicode.to_upper(char))
+			strings.write_byte(builder, ']')
+		} else {
+			strings.write_rune(builder, char)
+		}
+
+		last_percent = char == L_ESC 
+		p = p[size:]
+	}
+
+	return strings.to_string(builder^)
+}
+
+pattern_case_insensitive_allocator :: proc(
+	pattern: string, 
+	cap: int = 256,
+	allocator := context.allocator,
+) -> (res: string) {
+	builder := strings.builder_make(0, cap, context.temp_allocator)
+	return pattern_case_insensitive_builder(&builder, pattern)	
+}
+
+pattern_case_insensitive :: proc { pattern_case_insensitive_builder, pattern_case_insensitive_allocator }
+
+// Matcher helper struct that stores optional data you might want to use or not
+// as lua is far more dynamic this helps dealing with too much data
+// this also allows use of find/match/gmatch at through one struct
+Matcher :: struct {
+	haystack: string,
+	pattern: string,
+	captures: [MAX_CAPTURES]Match,
+	captures_length: int,
+	offset: int,
+	err: Error,
+
+	// changing content for iterators
+	iter: string,
+	iter_index: int,
+}
+
+// init using haystack & pattern and an optional byte offset
+matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
+	res.haystack = haystack
+	res.pattern = pattern
+	res.offset = offset
+	res.iter = haystack
+	return
+}
+
+// find the first match and return the byte start / end position in the string, true on success
+matcher_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bounds_check {
+	matcher.captures_length, matcher.err = find_aux(
+		matcher.haystack, 
+		matcher.pattern, 
+		matcher.offset, 
+		true, 
+		&matcher.captures,
+	)
+	ok = matcher.captures_length > 0 && matcher.err == .OK
+	match := matcher.captures[0]
+	start = match.byte_start
+	end = match.byte_end
+	return
+}
+
+// find the first match and return the matched word, true on success
+matcher_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_check {
+	matcher.captures_length, matcher.err = find_aux(
+		matcher.haystack, 
+		matcher.pattern, 
+		matcher.offset, 
+		false, 
+		&matcher.captures,
+	)
+	ok = matcher.captures_length > 0 && matcher.err == .OK
+	match := matcher.captures[0]
+	word = matcher.haystack[match.byte_start:match.byte_end]
+	return
+}
+
+// get the capture at the "correct" spot, as spot 0 is reserved for the first match
+matcher_capture :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> string #no_bounds_check {
+	runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
+	cap := matcher.captures[index + 1]
+	return matcher.haystack[cap.byte_start:cap.byte_end]
+}
+
+// get the raw match out of the captures, skipping spot 0
+matcher_capture_raw :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> Match #no_bounds_check {
+	runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
+	return matcher.captures[index + 1]
+}
+
+// alias
+matcher_gmatch :: matcher_match_iter
+
+// iteratively match the haystack till it cant find any matches
+matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
+	if len(matcher.iter) > 0 {
+		matcher.captures_length, matcher.err = find_aux(
+			matcher.iter, 
+			matcher.pattern, 
+			matcher.offset, 
+			false, 
+			&matcher.captures,
+		)
+
+		if matcher.captures_length != 0 && matcher.err == .OK {
+			ok = true
+			first := matcher.captures_length > 1 ? 1 : 0
+			match := matcher.captures[first]
+			
+			// output
+			res = matcher.iter[match.byte_start:match.byte_end]
+			index = matcher.iter_index
+			
+			// advance
+			matcher.iter_index += 1
+			matcher.iter = matcher.iter[match.byte_end:]
+		}
+	}
+
+	return
+}
+
+// get a slice of all valid captures above the first match
+matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
+	return matcher.captures[1:matcher.captures_length]
+}

+ 76 - 76
src/array.cpp

@@ -10,45 +10,45 @@ struct Array {
 
 	T &operator[](isize index) {
 		#if !defined(NO_ARRAY_BOUNDS_CHECK)
-			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+			GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
 		#endif
 		return data[index];
 	}
 
 	T const &operator[](isize index) const {
 		#if !defined(NO_ARRAY_BOUNDS_CHECK)
-			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+			GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
 		#endif
 		return data[index];
 	}
 };
 
-template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a);
-template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a, isize count);
-template <typename T> void     array_init          (Array<T> *array, gbAllocator const &a, isize count, isize capacity);
-template <typename T> Array<T> array_make          (gbAllocator const &a);
-template <typename T> Array<T> array_make          (gbAllocator const &a, isize count);
-template <typename T> Array<T> array_make          (gbAllocator const &a, isize count, isize capacity);
-template <typename T> Array<T> array_make_from_ptr (T *data, isize count, isize capacity);
-template <typename T> void     array_free          (Array<T> *array);
-template <typename T> void     array_add           (Array<T> *array, T const &t);
-template <typename T> T *      array_add_and_get   (Array<T> *array);
-template <typename T> void     array_add_elems     (Array<T> *array, T const *elems, isize elem_count);
-template <typename T> T        array_pop           (Array<T> *array);
-template <typename T> void     array_clear         (Array<T> *array);
-template <typename T> void     array_reserve       (Array<T> *array, isize capacity);
-template <typename T> void     array_resize        (Array<T> *array, isize count);
-template <typename T> void     array_set_capacity  (Array<T> *array, isize capacity);
-template <typename T> Array<T> array_slice         (Array<T> const &array, isize lo, isize hi);
-template <typename T> Array<T> array_clone         (gbAllocator const &a, Array<T> const &array);
+template <typename T> gb_internal void     array_init          (Array<T> *array, gbAllocator const &a);
+template <typename T> gb_internal void     array_init          (Array<T> *array, gbAllocator const &a, isize count);
+template <typename T> gb_internal void     array_init          (Array<T> *array, gbAllocator const &a, isize count, isize capacity);
+template <typename T> gb_internal Array<T> array_make          (gbAllocator const &a);
+template <typename T> gb_internal Array<T> array_make          (gbAllocator const &a, isize count);
+template <typename T> gb_internal Array<T> array_make          (gbAllocator const &a, isize count, isize capacity);
+template <typename T> gb_internal Array<T> array_make_from_ptr (T *data, isize count, isize capacity);
+template <typename T> gb_internal void     array_free          (Array<T> *array);
+template <typename T> gb_internal void     array_add           (Array<T> *array, T const &t);
+template <typename T> gb_internal T *      array_add_and_get   (Array<T> *array);
+template <typename T> gb_internal void     array_add_elems     (Array<T> *array, T const *elems, isize elem_count);
+template <typename T> gb_internal T        array_pop           (Array<T> *array);
+template <typename T> gb_internal void     array_clear         (Array<T> *array);
+template <typename T> gb_internal void     array_reserve       (Array<T> *array, isize capacity);
+template <typename T> gb_internal void     array_resize        (Array<T> *array, isize count);
+template <typename T> gb_internal void     array_set_capacity  (Array<T> *array, isize capacity);
+template <typename T> gb_internal Array<T> array_slice         (Array<T> const &array, isize lo, isize hi);
+template <typename T> gb_internal Array<T> array_clone         (gbAllocator const &a, Array<T> const &array);
 
-template <typename T> void array_ordered_remove  (Array<T> *array, isize index);
-template <typename T> void array_unordered_remove(Array<T> *array, isize index);
+template <typename T> gb_internal void array_ordered_remove  (Array<T> *array, isize index);
+template <typename T> gb_internal void array_unordered_remove(Array<T> *array, isize index);
 
-template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset);
-template <typename T> void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count);
+template <typename T> gb_internal void array_copy(Array<T> *array, Array<T> const &data, isize offset);
+template <typename T> gb_internal void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count);
 
-template <typename T> T *array_end_ptr(Array<T> *array);
+template <typename T> gb_internal T *array_end_ptr(Array<T> *array);
 
 
 template <typename T>
@@ -56,27 +56,27 @@ struct Slice {
 	T *data;
 	isize count;
 
-	T &operator[](isize index) {
+	gb_inline T &operator[](isize index) {
 		#if !defined(NO_ARRAY_BOUNDS_CHECK)
-			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+			GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
 		#endif
 		return data[index];
 	}
 
-	T const &operator[](isize index) const {
+	gb_inline T const &operator[](isize index) const {
 		#if !defined(NO_ARRAY_BOUNDS_CHECK)
-			GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+			GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
 		#endif
 		return data[index];
 	}
 };
 
-template <typename T> Slice<T> slice_from_array(Array<T> const &a);
+template <typename T> gb_internal Slice<T> slice_from_array(Array<T> const &a);
 
 
 
 template <typename T>
-Slice<T> slice_make(gbAllocator const &allocator, isize count) {
+gb_internal Slice<T> slice_make(gbAllocator const &allocator, isize count) {
 	GB_ASSERT(count >= 0);
 	Slice<T> s = {};
 	s.data = gb_alloc_array(allocator, T, count);
@@ -86,7 +86,7 @@ Slice<T> slice_make(gbAllocator const &allocator, isize count) {
 }
 
 template <typename T>
-void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
+gb_internal void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
 	GB_ASSERT(count >= 0);
 	s->data = gb_alloc_array(allocator, T, count);
 	if (count > 0) {
@@ -96,23 +96,23 @@ void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
 }
 
 template <typename T>
-void slice_free(Slice<T> *s, gbAllocator const &allocator) {
+gb_internal void slice_free(Slice<T> *s, gbAllocator const &allocator) {
 	gb_free(allocator, s->data);
 }
 
 template <typename T>
-void slice_resize(Slice<T> *s, gbAllocator const &allocator, isize new_count) {
+gb_internal void slice_resize(Slice<T> *s, gbAllocator const &allocator, isize new_count) {
 	resize_array_raw(&s->data, allocator, s->count, new_count);
 	s->count = new_count;
 }
 
 
 template <typename T>
-Slice<T> slice_from_array(Array<T> const &a) {
+gb_internal Slice<T> slice_from_array(Array<T> const &a) {
 	return {a.data, a.count};
 }
 template <typename T>
-Slice<T> slice_array(Array<T> const &array, isize lo, isize hi) {
+gb_internal Slice<T> slice_array(Array<T> const &array, isize lo, isize hi) {
 	GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
 	Slice<T> out = {};
 	isize len = hi-lo;
@@ -125,30 +125,30 @@ Slice<T> slice_array(Array<T> const &array, isize lo, isize hi) {
 
 
 template <typename T>
-Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
+gb_internal Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
 	T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
 	return {data, a.count};
 }
 
 template <typename T>
-Slice<T> slice_clone_from_array(gbAllocator const &allocator, Array<T> const &a) {
+gb_internal Slice<T> slice_clone_from_array(gbAllocator const &allocator, Array<T> const &a) {
 	auto c = array_clone(allocator, a);
 	return {c.data, c.count};
 }
 
 
 template <typename T>
-void slice_copy(Slice<T> *slice, Slice<T> const &data) {
+gb_internal void slice_copy(Slice<T> *slice, Slice<T> const &data) {
 	isize n = gb_min(slice->count, data.count);
 	gb_memmove(slice->data, data.data, gb_size_of(T)*n);
 }
 template <typename T>
-void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset) {
+gb_internal void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset) {
 	isize n = gb_clamp(slice->count-offset, 0, data.count);
 	gb_memmove(slice->data+offset, data.data, gb_size_of(T)*n);
 }
 template <typename T>
-void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count) {
+gb_internal void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count) {
 	isize n = gb_clamp(slice->count-offset, 0, gb_min(data.count, count));
 	gb_memmove(slice->data+offset, data.data, gb_size_of(T)*n);
 }
@@ -156,7 +156,7 @@ void slice_copy(Slice<T> *slice, Slice<T> const &data, isize offset, isize count
 
 
 template <typename T>
-gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi) {
+gb_internal gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi) {
 	GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
 	Slice<T> out = {};
 	isize len = hi-lo;
@@ -169,7 +169,7 @@ gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi) {
 
 
 template <typename T>
-void slice_ordered_remove(Slice<T> *array, isize index) {
+gb_internal void slice_ordered_remove(Slice<T> *array, isize index) {
 	GB_ASSERT(0 <= index && index < array->count);
 
 	isize bytes = gb_size_of(T) * (array->count-(index+1));
@@ -178,7 +178,7 @@ void slice_ordered_remove(Slice<T> *array, isize index) {
 }
 
 template <typename T>
-void slice_unordered_remove(Slice<T> *array, isize index) {
+gb_internal void slice_unordered_remove(Slice<T> *array, isize index) {
 	GB_ASSERT(0 <= index && index < array->count);
 
 	isize n = array->count-1;
@@ -190,18 +190,18 @@ void slice_unordered_remove(Slice<T> *array, isize index) {
 
 
 template <typename T>
-void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
+gb_internal void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
 	gb_memmove(array->data+offset, data.data, gb_size_of(T)*data.count);
 }
 template <typename T>
-void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count) {
+gb_internal void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count) {
 	gb_memmove(array->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count));
 }
 
 
 
 template <typename T>
-T *array_end_ptr(Array<T> *array) {
+gb_internal T *array_end_ptr(Array<T> *array) {
 	if (array->count > 0) {
 		return &array->data[array->count-1];
 	}
@@ -210,18 +210,18 @@ T *array_end_ptr(Array<T> *array) {
 
 
 template <typename T>
-gb_inline void array_init(Array<T> *array, gbAllocator const &a) {
+gb_internal gb_inline void array_init(Array<T> *array, gbAllocator const &a) {
 	isize cap = ARRAY_GROW_FORMULA(0);
 	array_init(array, a, 0, cap);
 }
 
 template <typename T>
-gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count) {
+gb_internal gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count) {
 	array_init(array, a, count, count);
 }
 
 template <typename T>
-gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count, isize capacity) {
+gb_internal gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count, isize capacity) {
 	array->allocator = a;
 	array->data = nullptr;
 	if (capacity > 0) {
@@ -234,7 +234,7 @@ gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count, is
 
 
 template <typename T>
-gb_inline Array<T> array_make_from_ptr(T *data, isize count, isize capacity) {
+gb_internal gb_inline Array<T> array_make_from_ptr(T *data, isize count, isize capacity) {
 	Array<T> a = {0};
 	a.data = data;
 	a.count = count;
@@ -244,7 +244,7 @@ gb_inline Array<T> array_make_from_ptr(T *data, isize count, isize capacity) {
 
 
 template <typename T>
-gb_inline Array<T> array_make(gbAllocator const &a) {
+gb_internal gb_inline Array<T> array_make(gbAllocator const &a) {
 	isize capacity = ARRAY_GROW_FORMULA(0);
 	Array<T> array = {};
 	array.allocator = a;
@@ -254,7 +254,7 @@ gb_inline Array<T> array_make(gbAllocator const &a) {
 	return array;
 }
 template <typename T>
-gb_inline Array<T> array_make(gbAllocator const &a, isize count) {
+gb_internal gb_inline Array<T> array_make(gbAllocator const &a, isize count) {
 	Array<T> array = {};
 	array.allocator = a;
 	array.data = gb_alloc_array(a, T, count);
@@ -263,7 +263,7 @@ gb_inline Array<T> array_make(gbAllocator const &a, isize count) {
 	return array;
 }
 template <typename T>
-gb_inline Array<T> array_make(gbAllocator const &a, isize count, isize capacity) {
+gb_internal gb_inline Array<T> array_make(gbAllocator const &a, isize count, isize capacity) {
 	Array<T> array = {};
 	array.allocator = a;
 	array.data = gb_alloc_array(a, T, capacity);
@@ -275,7 +275,7 @@ gb_inline Array<T> array_make(gbAllocator const &a, isize count, isize capacity)
 
 
 template <typename T>
-gb_inline void array_free(Array<T> *array) {
+gb_internal gb_inline void array_free(Array<T> *array) {
 	if (array->allocator.proc != nullptr) {
 		gb_free(array->allocator, array->data);
 	}
@@ -284,7 +284,7 @@ gb_inline void array_free(Array<T> *array) {
 }
 
 template <typename T>
-void array__grow(Array<T> *array, isize min_capacity) {
+gb_internal void array__grow(Array<T> *array, isize min_capacity) {
 	isize new_capacity = ARRAY_GROW_FORMULA(array->capacity);
 	if (new_capacity < min_capacity) {
 		new_capacity = min_capacity;
@@ -293,7 +293,7 @@ void array__grow(Array<T> *array, isize min_capacity) {
 }
 
 template <typename T>
-void array_add(Array<T> *array, T const &t) {
+gb_internal void array_add(Array<T> *array, T const &t) {
 	if (array->capacity < array->count+1) {
 		array__grow(array, 0);
 	}
@@ -302,7 +302,7 @@ void array_add(Array<T> *array, T const &t) {
 }
 
 template <typename T>
-T *array_add_and_get(Array<T> *array) {
+gb_internal T *array_add_and_get(Array<T> *array) {
 	if (array->count < array->capacity) {
 		return &array->data[array->count++];
 	}
@@ -314,7 +314,7 @@ T *array_add_and_get(Array<T> *array) {
 
 
 template <typename T>
-void array_add_elems(Array<T> *array, T const *elems, isize elem_count) {
+gb_internal void array_add_elems(Array<T> *array, T const *elems, isize elem_count) {
 	GB_ASSERT(elem_count >= 0);
 	if (array->capacity < array->count+elem_count) {
 		array__grow(array, array->count+elem_count);
@@ -325,26 +325,26 @@ void array_add_elems(Array<T> *array, T const *elems, isize elem_count) {
 
 
 template <typename T>
-gb_inline T array_pop(Array<T> *array) {
+gb_internal gb_inline T array_pop(Array<T> *array) {
 	GB_ASSERT(array->count > 0);
 	array->count--;
 	return array->data[array->count];
 }
 
 template <typename T>
-void array_clear(Array<T> *array) {
+gb_internal void array_clear(Array<T> *array) {
 	array->count = 0;
 }
 
 template <typename T>
-void array_reserve(Array<T> *array, isize capacity) {
+gb_internal void array_reserve(Array<T> *array, isize capacity) {
 	if (array->capacity < capacity) {
 		array_set_capacity(array, capacity);
 	}
 }
 
 template <typename T>
-void array_resize(Array<T> *array, isize count) {
+gb_internal void array_resize(Array<T> *array, isize count) {
 	if (array->capacity < count) {
 		array__grow(array, count);
 	}
@@ -352,7 +352,7 @@ void array_resize(Array<T> *array, isize count) {
 }
 
 template <typename T>
-void array_set_capacity(Array<T> *array, isize capacity) {
+gb_internal void array_set_capacity(Array<T> *array, isize capacity) {
 	if (capacity == array->capacity) {
 		return;
 	}
@@ -381,7 +381,7 @@ void array_set_capacity(Array<T> *array, isize capacity) {
 
 
 template <typename T>
-gb_inline Array<T> array_slice(Array<T> const &array, isize lo, isize hi) {
+gb_internal gb_inline Array<T> array_slice(Array<T> const &array, isize lo, isize hi) {
 	GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
 	Array<T> out = {};
 	isize len = hi-lo;
@@ -394,7 +394,7 @@ gb_inline Array<T> array_slice(Array<T> const &array, isize lo, isize hi) {
 }
 
 template <typename T>
-Array<T> array_clone(gbAllocator const &allocator, Array<T> const &array) {
+gb_internal Array<T> array_clone(gbAllocator const &allocator, Array<T> const &array) {
 	auto clone = array_make<T>(allocator, array.count, array.count);
 	array_copy(&clone, array, 0);
 	return clone;
@@ -402,7 +402,7 @@ Array<T> array_clone(gbAllocator const &allocator, Array<T> const &array) {
 
 
 template <typename T>
-void array_ordered_remove(Array<T> *array, isize index) {
+gb_internal void array_ordered_remove(Array<T> *array, isize index) {
 	GB_ASSERT(0 <= index && index < array->count);
 
 	isize bytes = gb_size_of(T) * (array->count-(index+1));
@@ -411,7 +411,7 @@ void array_ordered_remove(Array<T> *array, isize index) {
 }
 
 template <typename T>
-void array_unordered_remove(Array<T> *array, isize index) {
+gb_internal void array_unordered_remove(Array<T> *array, isize index) {
 	GB_ASSERT(0 <= index && index < array->count);
 
 	isize n = array->count-1;
@@ -424,35 +424,35 @@ void array_unordered_remove(Array<T> *array, isize index) {
 
 
 template <typename T>
-T *begin(Array<T> &array) {
+gb_internal T *begin(Array<T> &array) {
 	return array.data;
 }
 template <typename T>
-T const *begin(Array<T> const &array) {
+gb_internal T const *begin(Array<T> const &array) {
 	return array.data;
 }
 template <typename T>
-T *end(Array<T> &array) {
+gb_internal T *end(Array<T> &array) {
 	return array.data + array.count;
 }
 template <typename T>
-T const *end(Array<T> const &array) {
+gb_internal T const *end(Array<T> const &array) {
 	return array.data + array.count;
 }
 
 template <typename T>
-T *begin(Slice<T> &array) {
+gb_internal T *begin(Slice<T> &array) {
 	return array.data;
 }
 template <typename T>
-T const *begin(Slice<T> const &array) {
+gb_internal T const *begin(Slice<T> const &array) {
 	return array.data;
 }
 template <typename T>
-T *end(Slice<T> &array) {
+gb_internal T *end(Slice<T> &array) {
 	return array.data + array.count;
 }
 template <typename T>
-T const *end(Slice<T> const &array) {
+gb_internal T const *end(Slice<T> const &array) {
 	return array.data + array.count;
 }

+ 128 - 104
src/big_int.cpp

@@ -37,86 +37,86 @@ void MP_FREE(void *mem, size_t size) {
 
 typedef mp_int BigInt;
 
-void big_int_from_u64(BigInt *dst, u64 x);
-void big_int_from_i64(BigInt *dst, i64 x);
-void big_int_init    (BigInt *dst, BigInt const *src);
-void big_int_from_string(BigInt *dst, String const &s, bool *success);
+gb_internal void big_int_from_u64(BigInt *dst, u64 x);
+gb_internal void big_int_from_i64(BigInt *dst, i64 x);
+gb_internal void big_int_init    (BigInt *dst, BigInt const *src);
+gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success);
 
-void big_int_dealloc(BigInt *dst) {
+gb_internal void big_int_dealloc(BigInt *dst) {
 	mp_clear(dst);
 }
 
-BigInt big_int_make(BigInt const *b, bool abs=false);
-BigInt big_int_make_abs(BigInt const *b);
-BigInt big_int_make_u64(u64 x);
-BigInt big_int_make_i64(i64 x);
+gb_internal BigInt big_int_make(BigInt const *b, bool abs=false);
+gb_internal BigInt big_int_make_abs(BigInt const *b);
+gb_internal BigInt big_int_make_u64(u64 x);
+gb_internal BigInt big_int_make_i64(i64 x);
 
-u64    big_int_to_u64   (BigInt const *x);
-i64    big_int_to_i64   (BigInt const *x);
-f64    big_int_to_f64   (BigInt const *x);
-String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base = 10);
+gb_internal u64    big_int_to_u64   (BigInt const *x);
+gb_internal i64    big_int_to_i64   (BigInt const *x);
+gb_internal f64    big_int_to_f64   (BigInt const *x);
+gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base = 10);
 
-void big_int_add    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_sub    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_shl    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_shr    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_mul    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y);
+gb_internal void big_int_add    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_sub    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_shl    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_shr    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_mul    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y);
 
-void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r);
-void big_int_quo    (BigInt *z, BigInt const *x, BigInt const *y);
-void big_int_rem    (BigInt *z, BigInt const *x, BigInt const *y);
+gb_internal void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r);
+gb_internal void big_int_quo    (BigInt *z, BigInt const *x, BigInt const *y);
+gb_internal void big_int_rem    (BigInt *z, BigInt const *x, BigInt const *y);
 
-void big_int_and    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_xor    (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_or     (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_not    (BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed);
+gb_internal void big_int_and    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_xor    (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_or     (BigInt *dst, BigInt const *x, BigInt const *y);
+gb_internal void big_int_not    (BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed);
 
 
-void big_int_add_eq(BigInt *dst, BigInt const *x);
-void big_int_sub_eq(BigInt *dst, BigInt const *x);
-void big_int_shl_eq(BigInt *dst, BigInt const *x);
-void big_int_shr_eq(BigInt *dst, BigInt const *x);
-void big_int_mul_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_add_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_sub_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_shl_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_shr_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_mul_eq(BigInt *dst, BigInt const *x);
 
-void big_int_quo_eq(BigInt *dst, BigInt const *x);
-void big_int_rem_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_quo_eq(BigInt *dst, BigInt const *x);
+gb_internal void big_int_rem_eq(BigInt *dst, BigInt const *x);
 
-bool big_int_is_neg(BigInt const *x);
-void big_int_neg(BigInt *dst, BigInt const *x);
+gb_internal bool big_int_is_neg(BigInt const *x);
+gb_internal void big_int_neg(BigInt *dst, BigInt const *x);
 
-void big_int_add_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_add_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_add(dst, &res, x);
 }
-void big_int_sub_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_sub_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_sub(dst, &res, x);
 }
-void big_int_shl_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_shl_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_shl(dst, &res, x);
 }
-void big_int_shr_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_shr_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_shr(dst, &res, x);
 }
-void big_int_mul_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_mul_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_mul(dst, &res, x);
 }
-void big_int_quo_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_quo_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_quo(dst, &res, x);
 }
-void big_int_rem_eq(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_rem_eq(BigInt *dst, BigInt const *x) {
 	BigInt res = {};
 	big_int_init(&res, dst);
 	big_int_rem(dst, &res, x);
@@ -124,7 +124,7 @@ void big_int_rem_eq(BigInt *dst, BigInt const *x) {
 
 
 
-i64 big_int_sign(BigInt const *x) {
+gb_internal i64 big_int_sign(BigInt const *x) {
 	if (mp_iszero(x)) {
 		return 0;
 	}
@@ -132,44 +132,44 @@ i64 big_int_sign(BigInt const *x) {
 }
 
 
-void big_int_from_u64(BigInt *dst, u64 x) {
+gb_internal void big_int_from_u64(BigInt *dst, u64 x) {
 	mp_init_u64(dst, x);
 }
-void big_int_from_i64(BigInt *dst, i64 x) {
+gb_internal void big_int_from_i64(BigInt *dst, i64 x) {
 	mp_init_i64(dst, x);
 
 }
-void big_int_init(BigInt *dst, BigInt const *src) {
+gb_internal void big_int_init(BigInt *dst, BigInt const *src) {
 	if (dst == src) {
 		return;
 	}
 	mp_init_copy(dst, src);
 }
 
-BigInt big_int_make(BigInt const *b, bool abs) {
+gb_internal BigInt big_int_make(BigInt const *b, bool abs) {
 	BigInt i = {};
 	big_int_init(&i, b);
 	if (abs) mp_abs(&i, &i);
 	return i;
 }
-BigInt big_int_make_abs(BigInt const *b) {
+gb_internal BigInt big_int_make_abs(BigInt const *b) {
 	return big_int_make(b, true);
 }
 
 
-BigInt big_int_make_u64(u64 x) {
+gb_internal BigInt big_int_make_u64(u64 x) {
 	BigInt i = {};
 	big_int_from_u64(&i, x);
 	return i;
 }
-BigInt big_int_make_i64(i64 x) {
+gb_internal BigInt big_int_make_i64(i64 x) {
 	BigInt i = {};
 	big_int_from_i64(&i, x);
 	return i;
 }
 
 
-void big_int_from_string(BigInt *dst, String const &s, bool *success) {
+gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success) {
 	*success = true;
 
 	bool is_negative = false;
@@ -262,66 +262,66 @@ void big_int_from_string(BigInt *dst, String const &s, bool *success) {
 
 
 
-u64 big_int_to_u64(BigInt const *x) {
+gb_internal u64 big_int_to_u64(BigInt const *x) {
 	GB_ASSERT(x->sign == 0);
 	return mp_get_u64(x);
 }
 
-i64 big_int_to_i64(BigInt const *x) {
+gb_internal i64 big_int_to_i64(BigInt const *x) {
 	return mp_get_i64(x);
 }
 
-f64 big_int_to_f64(BigInt const *x) {
+gb_internal f64 big_int_to_f64(BigInt const *x) {
 	return mp_get_double(x);
 }
 
 
-void big_int_neg(BigInt *dst, BigInt const *x) {
+gb_internal void big_int_neg(BigInt *dst, BigInt const *x) {
 	mp_neg(x, dst);
 }
 
 
-int big_int_cmp(BigInt const *x, BigInt const *y) {
+gb_internal int big_int_cmp(BigInt const *x, BigInt const *y) {
 	return mp_cmp(x, y);
 }
 
-int big_int_cmp_zero(BigInt const *x) {
+gb_internal int big_int_cmp_zero(BigInt const *x) {
 	if (mp_iszero(x)) {
 		return 0;
 	}
 	return x->sign ? -1 : +1;
 }
 
-bool big_int_is_zero(BigInt const *x) {
+gb_internal bool big_int_is_zero(BigInt const *x) {
 	return mp_iszero(x);
 }
 
 
 
 
-void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_add(x, y, dst);
 }
 
 
-void big_int_sub(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_sub(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_sub(x, y, dst);
 }
 
 
-void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) {
 	u32 yy = mp_get_u32(y);
 	mp_mul_2d(x, yy, dst);
 }
 
-void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) {
 	u32 yy = mp_get_u32(y);
 	BigInt d = {};
 	mp_div_2d(x, yy, dst, &d);
 	big_int_dealloc(&d);
 }
 
-void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
+gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
 	BigInt d = {};
 	big_int_from_u64(&d, y);
 	mp_mul(x, &d, dst);
@@ -329,12 +329,12 @@ void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
 }
 
 
-void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_mul(x, y, dst);
 }
 
 
-u64 leading_zeros_u64(u64 x) {
+gb_internal u64 leading_zeros_u64(u64 x) {
 #if defined(GB_COMPILER_MSVC)
 	#if defined(GB_ARCH_64_BIT)
 		return __lzcnt64(x);
@@ -367,23 +367,23 @@ u64 leading_zeros_u64(u64 x) {
 //
 // q = x/y with the result truncated to zero
 // r = x - y*q
-void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q_, BigInt *r_) {
+gb_internal void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q_, BigInt *r_) {
 	mp_div(x, y, q_, r_);
 }
 
-void big_int_quo(BigInt *z, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_quo(BigInt *z, BigInt const *x, BigInt const *y) {
 	BigInt r = {};
 	big_int_quo_rem(x, y, z, &r);
 	big_int_dealloc(&r);
 }
 
-void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) {
 	BigInt q = {};
 	big_int_quo_rem(x, y, &q, z);
 	big_int_dealloc(&q);
 }
 
-void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) {
 	BigInt y0 = {};
 	big_int_init(&y0, y);
 
@@ -400,11 +400,11 @@ void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) {
 
 
 
-void big_int_and(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_and(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_and(x, y, dst);
 }
 
-void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
 	if (mp_iszero(x)) {
 		big_int_init(dst, y);
 		return;
@@ -467,22 +467,23 @@ void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
 	return;
 }
 
-void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_xor(x, y, dst);
 }
 
 
-void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) {
+gb_internal void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) {
 	mp_or(x, y, dst);
 }
 
-void debug_print_big_int(BigInt const *x) {
+gb_internal void debug_print_big_int(BigInt const *x) {
+	TEMPORARY_ALLOCATOR_GUARD();
 	String s = big_int_to_string(temporary_allocator(), x, 10);
 	gb_printf_err("[DEBUG] %.*s\n", LIT(s));
 }
 
 
-void big_int_not(BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed) {
+gb_internal void big_int_not(BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed) {
 	GB_ASSERT(bit_count >= 0);
 	if (bit_count == 0) {
 		big_int_from_u64(dst, 0);
@@ -530,7 +531,7 @@ void big_int_not(BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed) {
 	big_int_dealloc(&v);
 }
 
-bool big_int_is_neg(BigInt const *x) {
+gb_internal bool big_int_is_neg(BigInt const *x) {
 	if (x == nullptr) {
 		return false;
 	}
@@ -538,7 +539,7 @@ bool big_int_is_neg(BigInt const *x) {
 }
 
 
-char digit_to_char(u8 digit) {
+gb_internal char digit_to_char(u8 digit) {
 	GB_ASSERT(digit < 16);
 	if (digit <= 9) {
 		return digit + '0';
@@ -548,7 +549,7 @@ char digit_to_char(u8 digit) {
 	return '0';
 }
 
-String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) {
+gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) {
 	GB_ASSERT(base <= 16);
 
 	if (mp_iszero(x)) {
@@ -560,40 +561,63 @@ String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) {
 	Array<char> buf = {};
 	array_init(&buf, allocator, 0, 32);
 
-	BigInt v = {};
-	mp_init_copy(&v, x);
+	if (x->used >= 498) { // 2^498 ~ 10^150
+		mp_int val = {};
+		mp_abs(x, &val);
+		int exp = 0;
+		mp_err err = mp_log_n(&val, 10, &exp);
+		GB_ASSERT(err == MP_OKAY);
+		GB_ASSERT(exp >= 100);
 
-	if (v.sign) {
-		array_add(&buf, '-');
-		mp_abs(&v, &v);
-	}
+		mp_int thousand_below = {};
+		mp_int thousand_above = {};
+		mp_init_i32(&thousand_below, 10);
 
-	isize first_word_idx = buf.count;
+		mp_expt_n(&thousand_below, exp-3, &thousand_below);
+		mp_div(&val, &thousand_below, &thousand_above, nullptr);
 
-	BigInt r = {};
-	BigInt b = {};
-	big_int_from_u64(&b, base);
+		double mant = 1.0e-3 * mp_get_double(&thousand_above);
+
+		char val_buf[256] = {};
+		isize n = gb_snprintf(val_buf, gb_size_of(val_buf)-1, "~ %s%.fe%u", (x->sign ? "-" : ""), mant, exp);
+
+		array_add_elems(&buf, val_buf, n-1);
+	} else {
+		BigInt v = {};
+		mp_init_copy(&v, x);
+
+		if (v.sign) {
+			array_add(&buf, '-');
+			mp_abs(&v, &v);
+		}
 
-	u8 digit = 0;
-	while (big_int_cmp(&v, &b) >= 0) {
-		big_int_quo_rem(&v, &b, &v, &r);
+		isize first_word_idx = buf.count;
+
+		BigInt r = {};
+		BigInt b = {};
+		big_int_from_u64(&b, base);
+
+		u8 digit = 0;
+		while (big_int_cmp(&v, &b) >= 0) {
+			big_int_quo_rem(&v, &b, &v, &r);
+			digit = cast(u8)big_int_to_u64(&r);
+			array_add(&buf, digit_to_char(digit));
+		}
+
+		big_int_rem(&r, &v, &b);
 		digit = cast(u8)big_int_to_u64(&r);
 		array_add(&buf, digit_to_char(digit));
-	}
 
-	big_int_rem(&r, &v, &b);
-	digit = cast(u8)big_int_to_u64(&r);
-	array_add(&buf, digit_to_char(digit));
+		big_int_dealloc(&r);
+		big_int_dealloc(&b);
 
-	big_int_dealloc(&r);
-	big_int_dealloc(&b);
+		for (isize i = first_word_idx; i < buf.count/2; i++) {
+			isize j = buf.count + first_word_idx - i - 1;
+			char tmp = buf[i];
+			buf[i] = buf[j];
+			buf[j] = tmp;
+		}
 
-	for (isize i = first_word_idx; i < buf.count/2; i++) {
-		isize j = buf.count + first_word_idx - i - 1;
-		char tmp = buf[i];
-		buf[i] = buf[j];
-		buf[j] = tmp;
 	}
-
 	return make_string(cast(u8 *)buf.data, buf.count);
 }

+ 6 - 6
src/bug_report.cpp

@@ -30,7 +30,7 @@
 	NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
 */
 #if defined(GB_SYSTEM_WINDOWS)
-void report_windows_product_type(DWORD ProductType) {
+gb_internal void report_windows_product_type(DWORD ProductType) {
 	switch (ProductType) {
 	case PRODUCT_ULTIMATE:
 		gb_printf("Ultimate");
@@ -154,7 +154,7 @@ void report_windows_product_type(DWORD ProductType) {
 }
 #endif
 
-void odin_cpuid(int leaf, int result[]) {
+gb_internal void odin_cpuid(int leaf, int result[]) {
 	#if defined(GB_CPU_ARM)
 		return;
 
@@ -169,7 +169,7 @@ void odin_cpuid(int leaf, int result[]) {
 	#endif
 }
 
-void report_cpu_info() {
+gb_internal void report_cpu_info() {
 	gb_printf("\tCPU:  ");
 
 	#if defined(GB_CPU_X86)
@@ -220,7 +220,7 @@ void report_cpu_info() {
 /*
 	Report the amount of installed RAM.
 */
-void report_ram_info() {
+gb_internal void report_ram_info() {
 	gb_printf("\tRAM:  ");
 
 	#if defined(GB_SYSTEM_WINDOWS)
@@ -271,7 +271,7 @@ void report_ram_info() {
 	#endif
 }
 
-void report_os_info() {
+gb_internal void report_os_info() {
 	gb_printf("\tOS:   ");
 
 	#if defined(GB_SYSTEM_WINDOWS)
@@ -966,7 +966,7 @@ void report_os_info() {
 }
 
 // NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
-void print_bug_report_help() {
+gb_internal void print_bug_report_help() {
 	gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
 	gb_printf("\tWebsite: https://odin-lang.org\n");
 	gb_printf("\tGitHub:  https://github.com/odin-lang/Odin/issues\n");

+ 99 - 68
src/build_settings.cpp

@@ -57,7 +57,7 @@ enum TargetABIKind : u16 {
 };
 
 
-String target_os_names[TargetOs_COUNT] = {
+gb_global String target_os_names[TargetOs_COUNT] = {
 	str_lit(""),
 	str_lit("windows"),
 	str_lit("darwin"),
@@ -72,7 +72,7 @@ String target_os_names[TargetOs_COUNT] = {
 	str_lit("freestanding"),
 };
 
-String target_arch_names[TargetArch_COUNT] = {
+gb_global String target_arch_names[TargetArch_COUNT] = {
 	str_lit(""),
 	str_lit("amd64"),
 	str_lit("i386"),
@@ -82,19 +82,19 @@ String target_arch_names[TargetArch_COUNT] = {
 	str_lit("wasm64"),
 };
 
-String target_endian_names[TargetEndian_COUNT] = {
+gb_global String target_endian_names[TargetEndian_COUNT] = {
 	str_lit(""),
 	str_lit("little"),
 	str_lit("big"),
 };
 
-String target_abi_names[TargetABI_COUNT] = {
+gb_global String target_abi_names[TargetABI_COUNT] = {
 	str_lit(""),
 	str_lit("win64"),
 	str_lit("sysv"),
 };
 
-TargetEndianKind target_endians[TargetArch_COUNT] = {
+gb_global TargetEndianKind target_endians[TargetArch_COUNT] = {
 	TargetEndian_Invalid,
 	TargetEndian_Little,
 	TargetEndian_Little,
@@ -107,7 +107,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
 #define ODIN_VERSION_RAW "dev-unknown-unknown"
 #endif
 
-String const ODIN_VERSION = str_lit(ODIN_VERSION_RAW);
+gb_global String const ODIN_VERSION = str_lit(ODIN_VERSION_RAW);
 
 
 
@@ -149,7 +149,6 @@ enum CommandKind : u32 {
 	Command_run             = 1<<0,
 	Command_build           = 1<<1,
 	Command_check           = 1<<3,
-	Command_query           = 1<<4,
 	Command_doc             = 1<<5,
 	Command_version         = 1<<6,
 	Command_test            = 1<<7,
@@ -157,16 +156,15 @@ enum CommandKind : u32 {
 	Command_strip_semicolon = 1<<8,
 	Command_bug_report      = 1<<9,
 
-	Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test|Command_strip_semicolon,
+	Command__does_check = Command_run|Command_build|Command_check|Command_doc|Command_test|Command_strip_semicolon,
 	Command__does_build = Command_run|Command_build|Command_test,
 	Command_all = ~(u32)0,
 };
 
-char const *odin_command_strings[32] = {
+gb_global char const *odin_command_strings[32] = {
 	"run",
 	"build",
 	"check",
-	"query",
 	"doc",
 	"version",
 	"test",
@@ -293,13 +291,14 @@ struct BuildContext {
 	bool   show_error_line;
 
 	bool   ignore_lazy;
+	bool   ignore_llvm_build;
 
 	bool   use_subsystem_windows;
 	bool   ignore_microsoft_magic;
 	bool   linker_map_file;
 
 	bool   use_separate_modules;
-	bool   threaded_checker;
+	bool   no_threaded_checker;
 
 	bool   show_debug_messages;
 	
@@ -316,8 +315,6 @@ struct BuildContext {
 	u32 cmd_doc_flags;
 	Array<String> extra_packages;
 
-	QueryDataSetSettings query_data_set_settings;
-
 	StringSet test_names;
 
 	gbAffinity affinity;
@@ -333,10 +330,15 @@ struct BuildContext {
 
 gb_global BuildContext build_context = {0};
 
-bool global_warnings_as_errors(void) {
+gb_internal bool IS_ODIN_DEBUG(void) {
+	return build_context.ODIN_DEBUG;
+}
+
+
+gb_internal bool global_warnings_as_errors(void) {
 	return build_context.warnings_as_errors;
 }
-bool global_ignore_warnings(void) {
+gb_internal bool global_ignore_warnings(void) {
 	return build_context.ignore_warnings;
 }
 
@@ -398,7 +400,7 @@ gb_global TargetMetrics target_darwin_arm64 = {
 	TargetArch_arm64,
 	8, 8, 16,
 	str_lit("arm64-apple-macosx11.0.0"),
-	str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
+	str_lit("e-m:o-i64:64-i128:128-n32:64-S128"),
 };
 
 gb_global TargetMetrics target_freebsd_i386 = {
@@ -502,9 +504,9 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
 };
 
-NamedTargetMetrics *selected_target_metrics;
+gb_global NamedTargetMetrics *selected_target_metrics;
 
-TargetOsKind get_target_os_from_string(String str) {
+gb_internal TargetOsKind get_target_os_from_string(String str) {
 	for (isize i = 0; i < TargetOs_COUNT; i++) {
 		if (str_eq_ignore_case(target_os_names[i], str)) {
 			return cast(TargetOsKind)i;
@@ -513,7 +515,7 @@ TargetOsKind get_target_os_from_string(String str) {
 	return TargetOs_Invalid;
 }
 
-TargetArchKind get_target_arch_from_string(String str) {
+gb_internal TargetArchKind get_target_arch_from_string(String str) {
 	for (isize i = 0; i < TargetArch_COUNT; i++) {
 		if (str_eq_ignore_case(target_arch_names[i], str)) {
 			return cast(TargetArchKind)i;
@@ -523,7 +525,7 @@ TargetArchKind get_target_arch_from_string(String str) {
 }
 
 
-bool is_excluded_target_filename(String name) {
+gb_internal bool is_excluded_target_filename(String name) {
 	String original_name = name;
 	name = remove_extension_from_path(name);
 
@@ -588,23 +590,22 @@ struct LibraryCollections {
 
 gb_global Array<LibraryCollections> library_collections = {0};
 
-void add_library_collection(String name, String path) {
-	// TODO(bill): Check the path is valid and a directory
+gb_internal void add_library_collection(String name, String path) {
 	LibraryCollections lc = {name, string_trim_whitespace(path)};
 	array_add(&library_collections, lc);
 }
 
-bool find_library_collection_path(String name, String *path) {
-	for_array(i, library_collections) {
-		if (library_collections[i].name == name) {
-			if (path) *path = library_collections[i].path;
+gb_internal bool find_library_collection_path(String name, String *path) {
+	for (auto const &lc : library_collections) {
+		if (lc.name == name) {
+			if (path) *path = lc.path;
 			return true;
 		}
 	}
 	return false;
 }
 
-bool is_arch_wasm(void) {
+gb_internal bool is_arch_wasm(void) {
 	switch (build_context.metrics.arch) {
 	case TargetArch_wasm32:
 	case TargetArch_wasm64:
@@ -613,7 +614,7 @@ bool is_arch_wasm(void) {
 	return false;
 }
 
-bool is_arch_x86(void) {
+gb_internal bool is_arch_x86(void) {
 	switch (build_context.metrics.arch) {
 	case TargetArch_i386:
 	case TargetArch_amd64:
@@ -622,7 +623,7 @@ bool is_arch_x86(void) {
 	return false;
 }
 
-bool allow_check_foreign_filepath(void) {
+gb_internal bool allow_check_foreign_filepath(void) {
 	switch (build_context.metrics.arch) {
 	case TargetArch_wasm32:
 	case TargetArch_wasm64:
@@ -638,13 +639,14 @@ bool allow_check_foreign_filepath(void) {
 // is_abs_path
 // has_subdir
 
-String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
-String const NIX_SEPARATOR_STRING   = {cast(u8 *)"/",  1};
+gb_global String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
+gb_global String const NIX_SEPARATOR_STRING   = {cast(u8 *)"/",  1};
+
+gb_global String const WASM_MODULE_NAME_SEPARATOR = str_lit("..");
 
-String const WASM_MODULE_NAME_SEPARATOR = str_lit("..");
+gb_internal String internal_odin_root_dir(void);
 
-String internal_odin_root_dir(void);
-String odin_root_dir(void) {
+gb_internal String odin_root_dir(void) {
 	if (global_module_path_set) {
 		return global_module_path;
 	}
@@ -670,7 +672,7 @@ String odin_root_dir(void) {
 
 
 #if defined(GB_SYSTEM_WINDOWS)
-String internal_odin_root_dir(void) {
+gb_internal String internal_odin_root_dir(void) {
 	String path = global_module_path;
 	isize len, i;
 	wchar_t *text;
@@ -723,9 +725,9 @@ String internal_odin_root_dir(void) {
 
 #include <mach-o/dyld.h>
 
-String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s);
 
-String internal_odin_root_dir(void) {
+gb_internal String internal_odin_root_dir(void) {
 	String path = global_module_path;
 	isize len, i;
 	u8 *text;
@@ -777,9 +779,9 @@ String internal_odin_root_dir(void) {
 // NOTE: Linux / Unix is unfinished and not tested very well.
 #include <sys/stat.h>
 
-String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s);
 
-String internal_odin_root_dir(void) {
+gb_internal String internal_odin_root_dir(void) {
 	String path = global_module_path;
 	isize len, i;
 	u8 *text;
@@ -938,18 +940,22 @@ String internal_odin_root_dir(void) {
 gb_global BlockingMutex fullpath_mutex;
 
 #if defined(GB_SYSTEM_WINDOWS)
-String path_to_fullpath(gbAllocator a, String s) {
+gb_internal String path_to_fullpath(gbAllocator a, String s) {
 	String result = {};
-	mutex_lock(&fullpath_mutex);
-	defer (mutex_unlock(&fullpath_mutex));
 
 	String16 string16 = string_to_string16(heap_allocator(), s);
 	defer (gb_free(heap_allocator(), string16.text));
 
-	DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
+	DWORD len;
+
+	mutex_lock(&fullpath_mutex);
+
+	len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
 	if (len != 0) {
 		wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
 		GetFullPathNameW(&string16[0], len, text, nullptr);
+		mutex_unlock(&fullpath_mutex);
+
 		text[len] = 0;
 		result = string16_to_string(a, make_string16(text, len));
 		result = string_trim_whitespace(result);
@@ -960,12 +966,14 @@ String path_to_fullpath(gbAllocator a, String s) {
 				result.text[i] = '/';
 			}
 		}
+	} else {
+		mutex_unlock(&fullpath_mutex);
 	}
 
 	return result;
 }
 #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
-String path_to_fullpath(gbAllocator a, String s) {
+gb_internal String path_to_fullpath(gbAllocator a, String s) {
 	char *p;
 	mutex_lock(&fullpath_mutex);
 	p = realpath(cast(char *)s.text, 0);
@@ -978,7 +986,7 @@ String path_to_fullpath(gbAllocator a, String s) {
 #endif
 
 
-String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
+gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
 	u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
 	defer (gb_free(heap_allocator(), str));
 
@@ -1004,7 +1012,7 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
 }
 
 
-String get_fullpath_core(gbAllocator a, String path) {
+gb_internal String get_fullpath_core(gbAllocator a, String path) {
 	String module_dir = odin_root_dir();
 
 	String core = str_lit("core/");
@@ -1024,11 +1032,11 @@ String get_fullpath_core(gbAllocator a, String path) {
 	return path_to_fullpath(a, res);
 }
 
-bool show_error_line(void) {
+gb_internal bool show_error_line(void) {
 	return build_context.show_error_line;
 }
 
-bool has_asm_extension(String const &path) {
+gb_internal bool has_asm_extension(String const &path) {
 	String ext = path_extension(path);
 	if (ext == ".asm") {
 		return true;
@@ -1041,7 +1049,7 @@ bool has_asm_extension(String const &path) {
 }
 
 // temporary
-char *token_pos_to_string(TokenPos const &pos) {
+gb_internal char *token_pos_to_string(TokenPos const &pos) {
 	gbString s = gb_string_make_reserve(temporary_allocator(), 128);
 	String file = get_file_path_string(pos.file_id);
 	switch (build_context.ODIN_ERROR_POS_STYLE) {
@@ -1056,7 +1064,7 @@ char *token_pos_to_string(TokenPos const &pos) {
 	return s;
 }
 
-void init_build_context(TargetMetrics *cross_target) {
+gb_internal void init_build_context(TargetMetrics *cross_target) {
 	BuildContext *bc = &build_context;
 
 	gb_affinity_init(&bc->affinity);
@@ -1245,7 +1253,14 @@ void init_build_context(TargetMetrics *cross_target) {
 
 	bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
 
-	bc->ODIN_VALGRIND_SUPPORT = is_arch_x86() && build_context.metrics.os != TargetOs_windows;
+	bc->ODIN_VALGRIND_SUPPORT = false;
+	if (build_context.metrics.os != TargetOs_windows) {
+		switch (bc->metrics.arch) {
+		case TargetArch_amd64:
+			bc->ODIN_VALGRIND_SUPPORT = true;
+			break;
+		}
+	}
 
 	#undef LINK_FLAG_X64
 	#undef LINK_FLAG_386
@@ -1258,7 +1273,7 @@ void init_build_context(TargetMetrics *cross_target) {
 #endif
 
 
-Array<String> split_by_comma(String const &list) {
+gb_internal Array<String> split_by_comma(String const &list) {
 	isize n = 1;
 	for (isize i = 0; i < list.len; i++) {
 		if (list.text[i] == ',') {
@@ -1280,20 +1295,19 @@ Array<String> split_by_comma(String const &list) {
 	return res;
 }
 
-bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
+gb_internal bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
 	// TODO(bill): check_target_feature_is_valid
 	return true;
 }
 
-bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
+gb_internal bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
 	BuildContext *bc = &build_context;
 	mutex_lock(&bc->target_features_mutex);
 	defer (mutex_unlock(&bc->target_features_mutex));
 
 	auto items = split_by_comma(target_feature_list);
 	array_free(&items);
-	for_array(i, items) {
-		String const &item = items.data[i];
+	for (String const &item : items) {
 		if (!check_target_feature_is_valid(pos, item)) {
 			error(pos, "Target feature '%.*s' is not valid", LIT(item));
 			return false;
@@ -1307,14 +1321,13 @@ bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_
 	return true;
 }
 
-void enable_target_feature(TokenPos pos, String const &target_feature_list) {
+gb_internal void enable_target_feature(TokenPos pos, String const &target_feature_list) {
 	BuildContext *bc = &build_context;
 	mutex_lock(&bc->target_features_mutex);
 	defer (mutex_unlock(&bc->target_features_mutex));
 
 	auto items = split_by_comma(target_feature_list);
-	for_array(i, items) {
-		String const &item = items.data[i];
+	for (String const &item : items) {
 		if (!check_target_feature_is_valid(pos, item)) {
 			error(pos, "Target feature '%.*s' is not valid", LIT(item));
 			continue;
@@ -1326,25 +1339,26 @@ void enable_target_feature(TokenPos pos, String const &target_feature_list) {
 }
 
 
-char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
+gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
 	isize len = 0;
-	for_array(i, build_context.target_features_set.entries) {
+	isize i = 0;
+	for (String const &feature : build_context.target_features_set) {
 		if (i != 0) {
 			len += 1;
 		}
-		String feature = build_context.target_features_set.entries[i].value;
 		len += feature.len;
 		if (with_quotes) len += 2;
+		i += 1;
 	}
 	char *features = gb_alloc_array(allocator, char, len+1);
 	len = 0;
-	for_array(i, build_context.target_features_set.entries) {
+	i = 0;
+	for (String const &feature : build_context.target_features_set) {
 		if (i != 0) {
 			features[len++] = ',';
 		}
 
 		if (with_quotes) features[len++] = '"';
-		String feature = build_context.target_features_set.entries[i].value;
 		gb_memmove(features + len, feature.text, feature.len);
 		len += feature.len;
 		if (with_quotes) features[len++] = '"';
@@ -1356,15 +1370,14 @@ char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quot
 
 // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
 // We've previously called `parse_build_flags`, so `out_filepath` should be set.
-bool init_build_paths(String init_filename) {
+gb_internal bool init_build_paths(String init_filename) {
 	gbAllocator   ha = heap_allocator();
 	BuildContext *bc = &build_context;
 
 	// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
 	array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
 
-	string_set_init(&bc->target_features_set, heap_allocator(), 1024);
-	mutex_init(&bc->target_features_mutex);
+	string_set_init(&bc->target_features_set, 1024);
 
 	// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
 	bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
@@ -1408,7 +1421,6 @@ bool init_build_paths(String init_filename) {
 		if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
 			// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
 			Find_Result find_result = find_visual_studio_and_windows_sdk();
-			defer (mc_free_all());
 
 			if (find_result.windows_sdk_version == 0) {
 				gb_printf_err("Windows SDK not found.\n");
@@ -1533,6 +1545,25 @@ bool init_build_paths(String init_filename) {
 			output_name = remove_extension_from_path(output_name);
 			output_name = copy_string(ha, string_trim_whitespace(output_name));
 			output_path = path_from_string(ha, output_name);
+			
+			// Note(Dragos): This is a fix for empty filenames
+			// Turn the trailing folder into the file name
+			if (output_path.name.len == 0) {
+				isize len = output_path.basename.len;
+				while (len > 1 && output_path.basename[len - 1] != '/') {
+					len -= 1;
+				}
+				// We reached the slash
+				String old_basename = output_path.basename;
+				output_path.basename.len = len - 1; // Remove the slash
+				output_path.name = substring(old_basename, len, old_basename.len);
+				output_path.basename = copy_string(ha, output_path.basename);
+				output_path.name = copy_string(ha, output_path.name);
+				// The old basename is wrong. Delete it
+				gb_free(ha, old_basename.text);
+				
+				
+			}
 
 			// Replace extension.
 			if (output_path.ext.len > 0) {

+ 67 - 43
src/check_builtin.cpp

@@ -1,6 +1,6 @@
 typedef bool (BuiltinTypeIsProc)(Type *t);
 
-BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = {
+gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = {
 	nullptr, // BuiltinProc__type_simple_boolean_begin
 
 	is_type_boolean,
@@ -51,7 +51,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
 };
 
 
-void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type) {
+gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type) {
 	if (right_type == nullptr) {
 		return;
 	}
@@ -62,7 +62,7 @@ void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name,
 	}
 }
 
-void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
+gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
 	Type *left_type = nullptr;
 	Type *right_type = nullptr;
 	if (x->type->kind == Type_Tuple) {
@@ -88,8 +88,7 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
 }
 
 
-void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
-	// TODO(bill): better error message
+gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
 	gbString t = type_to_string(x.type);
 	error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
 	if (is_type_union(type_deref(x.type))) {
@@ -97,8 +96,7 @@ void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Op
 		gbString th = nullptr;
 		if (type_hint != nullptr) {
 			GB_ASSERT(bsrc->kind == Type_Union);
-			for_array(i, bsrc->Union.variants) {
-				Type *vt = bsrc->Union.variants[i];
+			for (Type *vt : bsrc->Union.variants) {
 				if (are_types_identical(vt, type_hint)) {
 					th = type_to_string(type_hint);
 					break;
@@ -118,7 +116,7 @@ void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Op
 }
 
 
-void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
+gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
 	Type *left_type = nullptr;
 	Type *right_type = nullptr;
 	if (x->type->kind == Type_Tuple) {
@@ -144,7 +142,7 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
 }
 
 
-bool does_require_msgSend_stret(Type *return_type) {
+gb_internal bool does_require_msgSend_stret(Type *return_type) {
 	if (return_type == nullptr) {
 		return false;
 	}
@@ -165,7 +163,7 @@ bool does_require_msgSend_stret(Type *return_type) {
 	return false;
 }
 
-ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) {
 	if (return_type == nullptr) {
 		return ObjcMsg_normal;
 	}
@@ -189,7 +187,7 @@ ObjcMsgKind get_objc_proc_kind(Type *return_type) {
 	return ObjcMsg_normal;
 }
 
-void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
 	ObjcMsgKind kind = get_objc_proc_kind(return_type);
 
 	Scope *scope = create_scope(c->info, nullptr);
@@ -199,8 +197,7 @@ void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<T
 	{
 		auto variables = array_make<Entity *>(permanent_allocator(), 0, param_types.count);
 
-		for_array(i, param_types)  {
-			Type *type = param_types[i];
+		for (Type *type : param_types) {
 			Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
 			array_add(&variables, param);
 		}
@@ -230,7 +227,7 @@ void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<T
 	try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
 }
 
-bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
+gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
 	Operand op = {};
 	check_expr(c, &op, expr);
 	if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
@@ -245,7 +242,7 @@ bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr
 	return false;
 }
 
-bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 	String const &builtin_name = builtin_procs[id].name;
 
 	if (build_context.metrics.os != TargetOs_darwin) {
@@ -387,7 +384,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
 	}
 }
 
-bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
+gb_internal bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
 	Operand x = {};
 	check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
 	if (x.mode == Addressing_Invalid) {
@@ -417,7 +414,7 @@ bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String con
 }
 
 
-bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 	ast_node(ce, CallExpr, call);
 
 	String const &builtin_name = builtin_procs[id].name;
@@ -1081,7 +1078,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call
 	return false;
 }
 
-bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) {
+gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) {
 	ast_node(ce, CallExpr, call);
 	ast_node(bd, BasicDirective, ce->proc);
 	String builtin_name = bd->name.string;
@@ -1111,7 +1108,7 @@ bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &origi
 			new_cache->path = path;
 			new_cache->data = data;
 			new_cache->file_error = file_error;
-			string_map_init(&new_cache->hashes, heap_allocator(), 32);
+			string_map_init(&new_cache->hashes, 32);
 			string_map_set(&c->info->load_file_cache, path, new_cache);
 			if (cache_) *cache_ = new_cache;
 		} else {
@@ -1121,8 +1118,8 @@ bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &origi
 		}
 	});
 
-	char *c_str = alloc_cstring(heap_allocator(), path);
-	defer (gb_free(heap_allocator(), c_str));
+	TEMPORARY_ALLOCATOR_GUARD();
+	char *c_str = alloc_cstring(temporary_allocator(), path);
 
 	gbFile f = {};
 	if (cache == nullptr) {
@@ -1170,7 +1167,7 @@ bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &origi
 }
 
 
-bool is_valid_type_for_load(Type *type) {
+gb_internal bool is_valid_type_for_load(Type *type) {
 	if (type == t_invalid) {
 		return false;
 	} else if (is_type_string(type)) {
@@ -1191,7 +1188,16 @@ bool is_valid_type_for_load(Type *type) {
 	return false;
 }
 
-LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
+gb_internal bool check_atomic_ptr_argument(Operand *operand, String const &builtin_name, Type *elem) {
+	if (!is_type_valid_atomic_type(elem)) {
+		error(operand->expr, "Only an integer, floating-point, boolean, or pointer can be used as an atomic for '%.*s'", LIT(builtin_name));
+		return false;
+	}
+	return true;
+
+}
+
+gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
 	ast_node(ce, CallExpr, call);
 	ast_node(bd, BasicDirective, ce->proc);
 	String name = bd->name.string;
@@ -1256,7 +1262,7 @@ LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, As
 }
 
 
-bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
+gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
 	ast_node(ce, CallExpr, call);
 	ast_node(bd, BasicDirective, ce->proc);
 	String name = bd->name.string;
@@ -1581,7 +1587,7 @@ bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast
 	return true;
 }
 
-bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 	ast_node(ce, CallExpr, call);
 	if (ce->inlining != ProcInlining_none) {
 		error(call, "Inlining operators are not allowed on built-in procedures");
@@ -1690,7 +1696,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc___entry_point:
 		operand->mode = Addressing_NoValue;
 		operand->type = nullptr;
-		mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
+		mpsc_enqueue(&c->info->intrinsics_entry_point_usage, call);
 		break;
 
 	case BuiltinProc_DIRECTIVE:
@@ -2559,7 +2565,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		if (is_type_struct(type)) {
 			isize variable_count = type->Struct.fields.count;
 			slice_init(&tuple->Tuple.variables, a, variable_count);
-			// TODO(bill): Should I copy each of the entities or is this good enough?
+			// NOTE(bill): don't copy the entities, this should be good enough
 			gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count);
 		} else if (is_type_array(type)) {
 			isize variable_count = cast(isize)type->Array.count;
@@ -3066,14 +3072,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	}
 
 	case BuiltinProc_soa_zip: {
+		TEMPORARY_ALLOCATOR_GUARD();
 		auto types = array_make<Type *>(temporary_allocator(), 0, ce->args.count);
 		auto names = array_make<String>(temporary_allocator(), 0, ce->args.count);
 
 		bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
 
 		bool fail = false;
-		for_array(i, ce->args) {
-			Ast *arg = ce->args[i];
+		for (Ast *arg : ce->args) {
 			bool mix = false;
 			if (first_is_field_value) {
 				mix = arg->kind != Ast_FieldValue;
@@ -3087,11 +3093,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			}
 		}
 		StringSet name_set = {};
-		string_set_init(&name_set, heap_allocator(), 2*ce->args.count);
+		string_set_init(&name_set, 2*ce->args.count);
 
-		for_array(i, ce->args) {
+		for (Ast *arg : ce->args) {
 			String name = {};
-			Ast *arg = ce->args[i];
 			if (arg->kind == Ast_FieldValue) {
 				Ast *ename = arg->FieldValue.field;
 				if (!fail && ename->kind != Ast_Ident) {
@@ -3126,13 +3131,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			}
 
 
-			if (string_set_exists(&name_set, name)) {
+			if (string_set_update(&name_set, name)) {
 				error(op.expr, "Field argument name '%.*s' already exists", LIT(name));
 			} else {
 				array_add(&types, arg_type->Slice.elem);
 				array_add(&names, name);
-
-				string_set_add(&name_set, name);
 			}
 		}
 
@@ -3455,9 +3458,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			error(ce->args[0], "Expected a constant string for '%.*s'", LIT(builtin_name));
 		} else if (operand->value.kind == ExactValue_String) {
 			String pkg_name = operand->value.value_string;
-			// TODO(bill): probably should have this be a `StringMap` eventually
-			for_array(i, c->info->packages.entries) {
-				AstPackage *pkg = c->info->packages.entries[i].value;
+			for (auto const &entry : c->info->packages) {
+				AstPackage *pkg = entry.value;
 				if (pkg->name == pkg_name) {
 					value = true;
 					break;
@@ -3769,9 +3771,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				mp_err err = mp_pack(rop, max_count, &written, MP_LSB_FIRST, size, endian, nails, &x.value.value_integer);
 				GB_ASSERT(err == MP_OKAY);
 
-				if (id == BuiltinProc_reverse_bits) {
-					// TODO(bill): Should this even be allowed at compile time?
-				} else {
+				if (id != BuiltinProc_reverse_bits) {
 					u64 v = 0;
 					switch (id) {
 					case BuiltinProc_count_ones:
@@ -4255,6 +4255,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (id == BuiltinProc_atomic_store && !check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
 			check_assignment(c, &x, elem, builtin_name);
@@ -4271,6 +4274,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
 			check_assignment(c, &x, elem, builtin_name);
@@ -4303,6 +4309,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (id == BuiltinProc_atomic_load && !check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
+
 			operand->type = elem;
 			operand->mode = Addressing_Value;
 			break;
@@ -4315,6 +4325,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 
 			OdinAtomicMemoryOrder memory_order = {};
 			if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
@@ -4346,6 +4359,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
 			check_assignment(c, &x, elem, builtin_name);
@@ -4383,6 +4399,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
 			check_assignment(c, &x, elem, builtin_name);
@@ -4421,6 +4440,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			Operand y = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
@@ -4448,6 +4470,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
 				return false;
 			}
+			if (!check_atomic_ptr_argument(operand, builtin_name, elem)) {
+				return false;
+			}
 			Operand x = {};
 			Operand y = {};
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
@@ -4993,8 +5018,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 			bool is_variant = false;
 
-			for_array(i, u->Union.variants) {
-				Type *vt = u->Union.variants[i];
+			for (Type *vt : u->Union.variants) {
 				if (are_types_identical(v, vt)) {
 					is_variant = true;
 					break;

+ 93 - 88
src/check_decl.cpp

@@ -1,7 +1,7 @@
-void check_stmt          (CheckerContext *ctx, Ast *node, u32 flags);
+gb_internal void check_stmt(CheckerContext *ctx, Ast *node, u32 flags);
 
 // NOTE(bill): 'content_name' is for debugging and error messages
-Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, String context_name) {
+gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, String context_name) {
 	if (operand->mode == Addressing_Invalid ||
 		operand->type == t_invalid ||
 		e->type == t_invalid) {
@@ -10,7 +10,6 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
 			gbString expr_str = expr_to_string(operand->expr);
 
 			// TODO(bill): is this a good enough error message?
-			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error(operand->expr,
 				  "Cannot assign built-in procedure '%s' in %.*s",
 				  expr_str,
@@ -46,7 +45,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
 	if (operand->mode == Addressing_Type) {
 		if (e->type != nullptr && is_type_typeid(e->type)) {
 			add_type_info_type(ctx, operand->type);
-			add_type_and_value(ctx->info, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type));
+			add_type_and_value(ctx, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type));
 			return e->type;
 		} else {
 			gbString t = type_to_string(operand->type);
@@ -110,7 +109,7 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
 	return e->type;
 }
 
-void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice<Ast *> const &inits, String context_name) {
+gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Slice<Ast *> const &inits, String context_name) {
 	if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
 		return;
 	}
@@ -118,17 +117,11 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Sl
 
 	// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
 	// an extra allocation
+	TEMPORARY_ALLOCATOR_GUARD();
 	auto operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
 	check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
 
 	isize rhs_count = operands.count;
-	for_array(i, operands) {
-		if (operands[i].mode == Addressing_Invalid) {
-			// TODO(bill): Should I ignore invalid parameters?
-			// rhs_count--;
-		}
-	}
-
 	isize max = gb_min(lhs_count, rhs_count);
 	for (isize i = 0; i < max; i++) {
 		Entity *e = lhs[i];
@@ -144,7 +137,7 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Sl
 	}
 }
 
-void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
+gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
 	if (operand->mode == Addressing_Invalid ||
 		operand->type == t_invalid ||
 		e->type == t_invalid) {
@@ -184,7 +177,7 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
 }
 
 
-bool is_type_distinct(Ast *node) {
+gb_internal bool is_type_distinct(Ast *node) {
 	for (;;) {
 		if (node == nullptr) {
 			return false;
@@ -217,7 +210,7 @@ bool is_type_distinct(Ast *node) {
 	return false;
 }
 
-Ast *remove_type_alias_clutter(Ast *node) {
+gb_internal Ast *remove_type_alias_clutter(Ast *node) {
 	for (;;) {
 		if (node == nullptr) {
 			return nullptr;
@@ -232,17 +225,7 @@ Ast *remove_type_alias_clutter(Ast *node) {
 	}
 }
 
-isize total_attribute_count(DeclInfo *decl) {
-	isize attribute_count = 0;
-	for_array(i, decl->attributes) {
-		Ast *attr = decl->attributes[i];
-		if (attr->kind != Ast_Attribute) continue;
-		attribute_count += attr->Attribute.elems.count;
-	}
-	return attribute_count;
-}
-
-Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+gb_internal Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
 	// NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
 	//
 	//         X :: enum {A, B, C}
@@ -288,7 +271,7 @@ Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named
 	return et;
 }
 
-void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
+gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
 	GB_ASSERT(e->type == nullptr);
 
 	DeclInfo *decl = decl_info_of_entity(e);
@@ -372,8 +355,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 
 			Type *t = base_type(e->type);
 			if (t->kind == Type_Enum) {
-				for_array(i, t->Enum.fields) {
-					Entity *f = t->Enum.fields[i];
+				for (Entity *f : t->Enum.fields) {
 					if (f->kind != Entity_Constant) {
 						continue;
 					}
@@ -390,7 +372,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 }
 
 
-void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
+gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
 	// NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
 	// e.g. file entity inserted into its package scope
 	String original_name = original_entity->token.string;
@@ -400,8 +382,8 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
 	if (found_scope == nullptr) {
 		return;
 	}
-	mutex_lock(&found_scope->mutex);
-	defer (mutex_unlock(&found_scope->mutex));
+	rw_mutex_lock(&found_scope->mutex);
+	defer (rw_mutex_unlock(&found_scope->mutex));
 
 	// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
 	// original entity was still used check checked, but the checking was only
@@ -433,7 +415,7 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
 
 
 
-void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
+gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
 	GB_ASSERT(e->type == nullptr);
 	GB_ASSERT(e->kind == Entity_Constant);
 	init = unparen_expr(init);
@@ -609,12 +591,12 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 
 
 typedef bool TypeCheckSig(Type *t);
-bool sig_compare(TypeCheckSig *a, Type *x, Type *y) {
+gb_internal bool sig_compare(TypeCheckSig *a, Type *x, Type *y) {
 	x = core_type(x);
 	y = core_type(y);
 	return (a(x) && a(y));
 }
-bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y) {
+gb_internal bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y) {
 	x = core_type(x);
 	y = core_type(y);
 	if (a == b) {
@@ -623,7 +605,7 @@ bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y) {
 	return ((a(x) && b(y)) || (b(x) && a(y)));
 }
 
-bool signature_parameter_similar_enough(Type *x, Type *y) {
+gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
 	if (sig_compare(is_type_pointer, x, y)) {
 		return true;
 	}
@@ -674,7 +656,7 @@ bool signature_parameter_similar_enough(Type *x, Type *y) {
 }
 
 
-bool are_signatures_similar_enough(Type *a_, Type *b_) {
+gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) {
 	GB_ASSERT(a_->kind == Type_Proc);
 	GB_ASSERT(b_->kind == Type_Proc);
 	TypeProc *a = &a_->Proc;
@@ -704,7 +686,7 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) {
 	return true;
 }
 
-Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) {
+gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) {
 	Ast *ident = nullptr;
 	Entity **foreign_library = nullptr;
 
@@ -747,7 +729,7 @@ Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) {
 	return nullptr;
 }
 
-String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) {
+gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) {
 	if (link_prefix.len > 0) {
 		if (link_name.len > 0) {
 			error(token, "'link_name' and 'link_prefix' cannot be used together");
@@ -764,7 +746,7 @@ String handle_link_name(CheckerContext *ctx, Token token, String link_name, Stri
 	return link_name;
 }
 
-void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
+gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	GB_ASSERT(e->type == nullptr);
 	if (d->proc_lit->kind != Ast_ProcLit) {
 		// TOOD(bill): Better error message
@@ -1004,7 +986,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 		GB_ASSERT(pl->body->kind == Ast_BlockStmt);
 		if (!pt->is_polymorphic) {
-			check_procedure_later(ctx, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
+			check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
 		}
 	} else if (!is_foreign) {
 		if (e->Procedure.is_export) {
@@ -1028,7 +1010,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 	if (ac.deferred_procedure.entity != nullptr) {
 		e->Procedure.deferred_procedure = ac.deferred_procedure;
-		mpmc_enqueue(&ctx->checker->procs_with_deferred_to_check, e);
+		mpsc_enqueue(&ctx->checker->procs_with_deferred_to_check, e);
 	}
 
 	if (is_foreign) {
@@ -1121,7 +1103,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 }
 
-void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
+gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
 	GB_ASSERT(e->type == nullptr);
 	GB_ASSERT(e->kind == Entity_Variable);
 
@@ -1142,7 +1124,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
 
 	if (ac.require_declaration) {
 		e->flags |= EntityFlag_Require;
-		mpmc_enqueue(&ctx->info->required_global_variable_queue, e);
+		mpsc_enqueue(&ctx->info->required_global_variable_queue, e);
 	}
 
 
@@ -1239,7 +1221,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
 	check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
 }
 
-void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
+gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
 	GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
 	auto *pge = &pg_entity->ProcGroup;
 	String proc_group_name = pg_entity->token.string;
@@ -1253,10 +1235,9 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
 	pg_entity->type = t_invalid;
 
 	PtrSet<Entity *> entity_set = {};
-	ptr_set_init(&entity_set, heap_allocator(), 2*pg->args.count);
+	ptr_set_init(&entity_set, 2*pg->args.count);
 
-	for_array(i, pg->args) {
-		Ast *arg = pg->args[i];
+	for (Ast *arg : pg->args) {
 		Entity *e = nullptr;
 		Operand o = {};
 		if (arg->kind == Ast_Ident) {
@@ -1289,7 +1270,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
 
 	ptr_set_destroy(&entity_set);
 
-	for_array(j, pge->entities) {
+	for (isize j = 0; j < pge->entities.count; j++) {
 		Entity *p = pge->entities[j];
 		if (p->type == t_invalid) {
 			// NOTE(bill): This invalid overload has already been handled
@@ -1367,7 +1348,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
 
 }
 
-void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
+gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
 	if (e->state == EntityState_Resolved)  {
 		return;
 	}
@@ -1431,15 +1412,46 @@ end:;
 }
 
 
+gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
+	if (decl && decl->parent) {
+		Scope *ps = decl->parent->scope;
+		if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) {
+			return;
+		} else {
+			// NOTE(bill): Add the dependencies from the procedure literal (lambda)
+			// But only at the procedure level
+			rw_mutex_shared_lock(&decl->deps_mutex);
+			rw_mutex_lock(&decl->parent->deps_mutex);
+
+			for (Entity *e : decl->deps) {
+				ptr_set_add(&decl->parent->deps, e);
+			}
+
+			rw_mutex_unlock(&decl->parent->deps_mutex);
+			rw_mutex_shared_unlock(&decl->deps_mutex);
+
+			rw_mutex_shared_lock(&decl->type_info_deps_mutex);
+			rw_mutex_lock(&decl->parent->type_info_deps_mutex);
+
+			for (Type *t : decl->type_info_deps) {
+				ptr_set_add(&decl->parent->type_info_deps, t);
+			}
+
+			rw_mutex_unlock(&decl->parent->type_info_deps_mutex);
+			rw_mutex_shared_unlock(&decl->type_info_deps_mutex);
+		}
+	}
+}
+
 struct ProcUsingVar {
 	Entity *e;
 	Entity *uvar;
 };
 
 
-void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
+gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
 	if (body == nullptr) {
-		return;
+		return false;
 	}
 	GB_ASSERT(body->kind == Ast_BlockStmt);
 
@@ -1480,8 +1492,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 	{
 		if (type->Proc.param_count > 0) {
 			TypeTuple *params = &type->Proc.params->Tuple;
-			for_array(i, params->variables) {
-				Entity *e = params->variables[i];
+			for (Entity *e : params->variables) {
 				if (e->kind != Entity_Variable) {
 					continue;
 				}
@@ -1489,7 +1500,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 					continue;
 				}
 				if (is_blank_ident(e->token)) {
-                    error(e->token, "'using' a procedure parameter requires a non blank identifier");
+					error(e->token, "'using' a procedure parameter requires a non blank identifier");
 					break;
 				}
 
@@ -1499,8 +1510,9 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 				if (t->kind == Type_Struct) {
 					Scope *scope = t->Struct.scope;
 					GB_ASSERT(scope != nullptr);
-					MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
-						Entity *f = scope->elements.entries[i].value;
+					rw_mutex_lock(&scope->mutex);
+					for (auto const &entry : scope->elements) {
+						Entity *f = entry.value;
 						if (f->kind == Entity_Variable) {
 							Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
 							if (is_value) uvar->flags |= EntityFlag_Value;
@@ -1509,6 +1521,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 							array_add(&using_entities, puv);
 						}
 					}
+					rw_mutex_unlock(&scope->mutex);
 				} else {
 					error(e->token, "'using' can only be applied to variables of type struct");
 					break;
@@ -1517,41 +1530,50 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 		}
 	}
 
-	MUTEX_GUARD_BLOCK(ctx->scope->mutex) for_array(i, using_entities) {
-		Entity *e = using_entities[i].e;
-		Entity *uvar = using_entities[i].uvar;
-		Entity *prev = scope_insert(ctx->scope, uvar, false);
+	rw_mutex_lock(&ctx->scope->mutex);
+	for (auto const &entry : using_entities) {
+		Entity *e = entry.e;
+		Entity *uvar = entry.uvar;
+		Entity *prev = scope_insert_no_mutex(ctx->scope, uvar);
 		if (prev != nullptr) {
 			error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
 			error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
 			break;
 		}
 	}
+	rw_mutex_unlock(&ctx->scope->mutex);
 
 
 	bool where_clause_ok = evaluate_where_clauses(ctx, nullptr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, !decl->where_clauses_evaluated);
 	if (!where_clause_ok) {
 		// NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed
-		return;
+		return false;
 	}
 
 	check_open_scope(ctx, body);
 	{
-		for_array(i, using_entities) {
-			Entity *uvar = using_entities[i].uvar;
+		for (auto const &entry : using_entities) {
+			Entity *uvar = entry.uvar;
 			Entity *prev = scope_insert(ctx->scope, uvar);
 			gb_unused(prev);
 			// NOTE(bill): Don't err here
 		}
 
+		GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked);
+		if (decl->defer_use_checked) {
+			GB_ASSERT(is_type_polymorphic(type, true));
+			error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string));
+			GB_ASSERT(decl->defer_use_checked == false);
+		}
+
 		check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
 
-		for_array(i, bs->stmts) {
-			Ast *stmt = bs->stmts[i];
+		decl->defer_use_checked = true;
+
+		for (Ast *stmt : bs->stmts) {
 			if (stmt->kind == Ast_ValueDecl) {
 				ast_node(vd, ValueDecl, stmt);
-				for_array(j, vd->names) {
-					Ast *name = vd->names[j];
+				for (Ast *name : vd->names) {
 					if (!is_blank_ident(name)) {
 						if (name->kind == Ast_Ident) {
 							GB_ASSERT(name->Ident.entity != nullptr);
@@ -1580,30 +1602,13 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 				}
 			}
 		}
+
 	}
 	check_close_scope(ctx);
 
 	check_scope_usage(ctx->checker, ctx->scope);
 
-	if (decl->parent != nullptr) {
-		Scope *ps = decl->parent->scope;
-		if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) {
-			return;
-		} else {
-			mutex_lock(&ctx->info->deps_mutex);
+	add_deps_from_child_to_parent(decl);
 
-			// NOTE(bill): Add the dependencies from the procedure literal (lambda)
-			// But only at the procedure level
-			for_array(i, decl->deps.entries) {
-				Entity *e = decl->deps.entries[i].ptr;
-				ptr_set_add(&decl->parent->deps, e);
-			}
-			for_array(i, decl->type_info_deps.entries) {
-				Type *t = decl->type_info_deps.entries[i].ptr;
-				ptr_set_add(&decl->parent->type_info_deps, t);
-			}
-
-			mutex_unlock(&ctx->info->deps_mutex);
-		}
-	}
+	return true;
 }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 200 - 184
src/check_expr.cpp


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 556 - 305
src/check_stmt.cpp


+ 144 - 132
src/check_type.cpp

@@ -1,6 +1,6 @@
-ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
+gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
 
-void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
+gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
 	t = base_type(t);
 	GB_ASSERT(t->kind == Type_Array);
 	Entity *e = scope_lookup_current(ctx->scope, name);
@@ -27,7 +27,7 @@ void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field,
 	}
 }
 
-void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field, Type *t) {
+gb_internal void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field, Type *t) {
 	if (t == nullptr) {
 		return;
 	}
@@ -81,7 +81,7 @@ void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field
 	}
 }
 
-bool does_field_type_allow_using(Type *t) {
+gb_internal bool does_field_type_allow_using(Type *t) {
 	t = base_type(t);
 	if (is_type_struct(t)) {
 		return true;
@@ -91,8 +91,8 @@ bool does_field_type_allow_using(Type *t) {
 	return false;
 }
 
-void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields, String **tags, Slice<Ast *> const &params,
-                         isize init_field_capacity, Type *struct_type, String context) {
+gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields, String **tags, Slice<Ast *> const &params,
+                                     isize init_field_capacity, Type *struct_type, String context) {
 	auto fields_array = array_make<Entity *>(heap_allocator(), 0, init_field_capacity);
 	auto tags_array = array_make<String>(heap_allocator(), 0, init_field_capacity);
 
@@ -219,7 +219,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 }
 
 
-bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
+gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
 	GB_ASSERT(align_ != nullptr);
 	Operand o = {};
 	check_expr(ctx, &o, node);
@@ -256,71 +256,88 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
 }
 
 
-Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
-	mutex_lock(&ctx->info->gen_types_mutex);
-	defer (mutex_unlock(&ctx->info->gen_types_mutex));
+gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
+	rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global
 
 	auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
-	if (found_gen_types != nullptr) {
-		// GB_ASSERT_MSG(ordered_operands.count >= param_count, "%td >= %td", ordered_operands.count, param_count);
-
-		for_array(i, *found_gen_types) {
-			Entity *e = (*found_gen_types)[i];
-			Type *t = base_type(e->type);
-			TypeTuple *tuple = get_record_polymorphic_params(t);
-			GB_ASSERT(param_count == tuple->variables.count);
-
-			bool skip = false;
-
-			for (isize j = 0; j < param_count; j++) {
-				Entity *p = tuple->variables[j];
-				Operand o = {};
-				if (j < ordered_operands.count) {
-					o = ordered_operands[j];
+	if (found_gen_types == nullptr) {
+		rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
+		return nullptr;
+	}
+
+	rw_mutex_shared_lock(&found_gen_types->mutex); // @@local
+	defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local
+
+	rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
+
+	for (Entity *e : found_gen_types->types) {
+		Type *t = base_type(e->type);
+		TypeTuple *tuple = nullptr;
+		switch (t->kind) {
+		case Type_Struct:
+			if (t->Struct.polymorphic_params) {
+				tuple = &t->Struct.polymorphic_params->Tuple;
+			}
+			break;
+		case Type_Union:
+			if (t->Union.polymorphic_params) {
+				tuple = &t->Union.polymorphic_params->Tuple;
+			}
+			break;
+		}
+		GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
+		GB_ASSERT(param_count == tuple->variables.count);
+
+		bool skip = false;
+
+		for (isize j = 0; j < param_count; j++) {
+			Entity *p = tuple->variables[j];
+			Operand o = {};
+			if (j < ordered_operands.count) {
+				o = ordered_operands[j];
+			}
+			if (o.expr == nullptr) {
+				continue;
+			}
+			Entity *oe = entity_of_node(o.expr);
+			if (p == oe) {
+				// NOTE(bill): This is the same type, make sure that it will be be same thing and use that
+				// Saves on a lot of checking too below
+				continue;
+			}
+
+			if (p->kind == Entity_TypeName) {
+				if (is_type_polymorphic(o.type)) {
+					// NOTE(bill): Do not add polymorphic version to the gen_types
+					skip = true;
+					break;
 				}
-				if (o.expr == nullptr) {
-					continue;
+				if (!are_types_identical(o.type, p->type)) {
+					skip = true;
+					break;
 				}
-				Entity *oe = entity_of_node(o.expr);
-				if (p == oe) {
-					// NOTE(bill): This is the same type, make sure that it will be be same thing and use that
-					// Saves on a lot of checking too below
-					continue;
+			} else if (p->kind == Entity_Constant) {
+				if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
+					skip = true;
+					break;
 				}
-
-				if (p->kind == Entity_TypeName) {
-					if (is_type_polymorphic(o.type)) {
-						// NOTE(bill): Do not add polymorphic version to the gen_types
-						skip = true;
-						break;
-					}
-					if (!are_types_identical(o.type, p->type)) {
-						skip = true;
-						break;
-					}
-				} else if (p->kind == Entity_Constant) {
-					if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
-						skip = true;
-						break;
-					}
-					if (!are_types_identical(o.type, p->type)) {
-						skip = true;
-						break;
-					}
-				} else {
-					GB_PANIC("Unknown entity kind");
+				if (!are_types_identical(o.type, p->type)) {
+					skip = true;
+					break;
 				}
+			} else {
+				GB_PANIC("Unknown entity kind");
 			}
-			if (!skip) {
-				return e;
-			}
+		}
+		if (!skip) {
+			return e;
 		}
 	}
 	return nullptr;
 }
 
 
-void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) {
+gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) {
 	GB_ASSERT(is_type_named(named_type));
 	gbAllocator a = heap_allocator();
 	Scope *s = ctx->scope->parent;
@@ -346,22 +363,24 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
 	// TODO(bill): Is this even correct? Or should the metadata be copied?
 	e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
 
-	mutex_lock(&ctx->info->gen_types_mutex);
+	rw_mutex_lock(&ctx->info->gen_types_mutex);
 	auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
 	if (found_gen_types) {
-		array_add(found_gen_types, e);
+		rw_mutex_lock(&found_gen_types->mutex);
+		array_add(&found_gen_types->types, e);
+		rw_mutex_unlock(&found_gen_types->mutex);
 	} else {
-		auto array = array_make<Entity *>(heap_allocator());
-		array_add(&array, e);
-		map_set(&ctx->info->gen_types, original_type, array);
+		GenTypesData gen_types = {};
+		gen_types.types = array_make<Entity *>(heap_allocator());
+		array_add(&gen_types.types, e);
+		map_set(&ctx->info->gen_types, original_type, gen_types);
 	}
-	mutex_unlock(&ctx->info->gen_types_mutex);
+	rw_mutex_unlock(&ctx->info->gen_types_mutex);
 }
 
-Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
-                                      bool *is_polymorphic_,
-                                      Ast *node, Array<Operand> *poly_operands,
-                                      Type *named_type, Type *original_type_for_poly) {
+gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
+                                                  bool *is_polymorphic_,
+                                                  Ast *node, Array<Operand> *poly_operands) {
 	Type *polymorphic_params_type = nullptr;
 	bool can_check_fields = true;
 	GB_ASSERT(is_polymorphic_ != nullptr);
@@ -528,11 +547,6 @@ Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_para
 		}
 	}
 
-	if (original_type_for_poly != nullptr) {
-		GB_ASSERT(named_type != nullptr);
-		add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
-	}
-
 	if (!*is_polymorphic_) {
 		*is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr;
 	}
@@ -540,7 +554,7 @@ Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_para
 	return polymorphic_params_type;
 }
 
-bool check_record_poly_operand_specialization(CheckerContext *ctx, Type *record_type, Array<Operand> *poly_operands, bool *is_polymorphic_) {
+gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, Type *record_type, Array<Operand> *poly_operands, bool *is_polymorphic_) {
 	if (poly_operands == nullptr) {
 		return false;
 	}
@@ -569,7 +583,7 @@ bool check_record_poly_operand_specialization(CheckerContext *ctx, Type *record_
 }
 
 
-void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
+gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
 	GB_ASSERT(is_type_struct(struct_type));
 	ast_node(st, StructType, node);
 
@@ -600,10 +614,13 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 	struct_type->Struct.polymorphic_params = check_record_polymorphic_params(
 		ctx, st->polymorphic_params,
 		&struct_type->Struct.is_polymorphic,
-		node, poly_operands,
-		named_type, original_type_for_poly
-	);;
+		node, poly_operands
+	);
 	struct_type->Struct.is_poly_specialized = check_record_poly_operand_specialization(ctx, struct_type, poly_operands, &struct_type->Struct.is_polymorphic);
+	if (original_type_for_poly) {
+		GB_ASSERT(named_type != nullptr);
+		add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
+	}
 
 	if (!struct_type->Struct.is_polymorphic) {
 		if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) {
@@ -626,7 +643,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
 		}
 	}
 }
-void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
+gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
 	GB_ASSERT(is_type_union(union_type));
 	ast_node(ut, UnionType, node);
 
@@ -635,10 +652,13 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 	union_type->Union.polymorphic_params = check_record_polymorphic_params(
 		ctx, ut->polymorphic_params,
 		&union_type->Union.is_polymorphic,
-		node, poly_operands,
-		named_type, original_type_for_poly
+		node, poly_operands
 	);
 	union_type->Union.is_poly_specialized = check_record_poly_operand_specialization(ctx, union_type, poly_operands, &union_type->Union.is_polymorphic);
+	if (original_type_for_poly) {
+		GB_ASSERT(named_type != nullptr);
+		add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly);
+	}
 
 	if (!union_type->Union.is_polymorphic) {
 		if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) {
@@ -709,7 +729,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
 	}
 }
 
-void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast *node) {
+gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast *node) {
 	ast_node(et, EnumType, node);
 	GB_ASSERT(is_type_enum(enum_type));
 
@@ -851,7 +871,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
 	enum_type->Enum.max_value_index = max_value_index;
 }
 
-bool is_type_valid_bit_set_range(Type *t) {
+gb_internal bool is_type_valid_bit_set_range(Type *t) {
 	if (is_type_integer(t)) {
 		return true;
 	}
@@ -861,7 +881,7 @@ bool is_type_valid_bit_set_range(Type *t) {
 	return false;
 }
 
-void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *node) {
+gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *node) {
 	ast_node(bs, BitSetType, node);
 	GB_ASSERT(type->kind == Type_BitSet);
 	type->BitSet.node = node;
@@ -1102,7 +1122,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
 }
 
 
-bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Type *type, bool compound, bool modify_type) {
+gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Type *type, bool compound, bool modify_type) {
 	if (type == nullptr ||
 	    type == t_invalid) {
 		return true;
@@ -1229,7 +1249,7 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ
 }
 
 
-Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) {
+gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) {
 	bool modify_type = !ctx->no_polymorphic_errors;
 	bool show_error = modify_type && !ctx->hide_polymorphic_errors;
 	if (!is_operand_value(operand)) {
@@ -1256,7 +1276,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
 	return t_invalid;
 }
 
-bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
+gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
 	if (expr == nullptr) {
 		return false;
 	}
@@ -1275,7 +1295,7 @@ bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
 }
 
 
-ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
+gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
 	ParameterValue param_value = {};
 	param_value.original_ast_expr = expr;
 	if (expr == nullptr) {
@@ -1370,7 +1390,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
 }
 
 
-Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> *operands) {
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> *operands) {
 	if (_params == nullptr) {
 		return nullptr;
 	}
@@ -1599,10 +1619,6 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 					}
 				}
 
-				if (p->flags&FieldFlag_auto_cast) {
-					error(name, "'auto_cast' can only be applied to variable fields");
-					p->flags &= ~FieldFlag_auto_cast;
-				}
 				if (p->flags&FieldFlag_const) {
 					error(name, "'#const' can only be applied to variable fields");
 					p->flags &= ~FieldFlag_const;
@@ -1639,6 +1655,12 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 						} else if (!ctx->no_polymorphic_errors) {
 							// NOTE(bill): The type should be determined now and thus, no need to determine the type any more
 							is_type_polymorphic_type = false;
+							Entity *proc_entity = entity_from_expr(op.expr);
+							if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) {
+								if (is_type_polymorphic(proc_entity->type, false)) {
+									error(op.expr, "Cannot determine complete type of partial polymorphic procedure");
+								}
+							}
 						}
 					}
 					if (is_poly_name) {
@@ -1661,11 +1683,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 					}
 					if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
 						bool ok = true;
-						if (p->flags&FieldFlag_auto_cast) {
-							if (!check_is_castable_to(ctx, &op, type)) {
-								ok = false;
-							}
-						} else if (p->flags&FieldFlag_any_int) {
+						if (p->flags&FieldFlag_any_int) {
 							if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
 								ok = false;
 							} else if (!check_is_castable_to(ctx, &op, type)) {
@@ -1711,10 +1729,6 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 						error(name, "'#no_alias' can only be applied to non constant values");
 						p->flags &= ~FieldFlag_no_alias; // Remove the flag
 					}
-					if (p->flags&FieldFlag_auto_cast) {
-						error(name, "'auto_cast' can only be applied to variable fields");
-						p->flags &= ~FieldFlag_auto_cast;
-					}
 					if (p->flags&FieldFlag_any_int) {
 						error(name, "'#any_int' can only be applied to variable fields");
 						p->flags &= ~FieldFlag_any_int;
@@ -1745,9 +1759,6 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 			if (p->flags&FieldFlag_no_alias) {
 				param->flags |= EntityFlag_NoAlias;
 			}
-			if (p->flags&FieldFlag_auto_cast) {
-				param->flags |= EntityFlag_AutoCast;
-			}
 			if (p->flags&FieldFlag_any_int) {
 				if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
 					gbString str = type_to_string(param->type);
@@ -1791,8 +1802,8 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 
 	isize specialization_count = 0;
 	if (scope != nullptr) {
-		for_array(i, scope->elements.entries) {
-			Entity *e = scope->elements.entries[i].value;
+		for (auto const &entry : scope->elements) {
+			Entity *e = entry.value;
 			if (e->kind == Entity_TypeName) {
 				Type *t = e->type;
 				if (t->kind == Type_Generic &&
@@ -1814,7 +1825,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 	return tuple;
 }
 
-Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
+gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
 	if (_results == nullptr) {
 		return nullptr;
 	}
@@ -1928,7 +1939,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
 
 
 // NOTE(bill): 'operands' is for generating non generic procedure type
-bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> *operands) {
+gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> *operands) {
 	ast_node(pt, ProcType, proc_type_node);
 
 	if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) {
@@ -2084,7 +2095,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 }
 
 
-i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
+gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
 	if (e == nullptr) {
 		return 0;
 	}
@@ -2169,7 +2180,7 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
 	return 0;
 }
 
-Type *make_optional_ok_type(Type *value, bool typed) {
+gb_internal Type *make_optional_ok_type(Type *value, bool typed) {
 	gbAllocator a = permanent_allocator();
 	Type *t = alloc_type_tuple();
 	slice_init(&t->Tuple.variables, a, 2);
@@ -2181,23 +2192,23 @@ Type *make_optional_ok_type(Type *value, bool typed) {
 
 // IMPORTANT NOTE(bill): This must match the definition in dynamic_map_internal.odin
 enum : i64 {
-	MAP_CACHE_LINE_LOG2 = 6,
-	MAP_CACHE_LINE_SIZE = 1 << MAP_CACHE_LINE_LOG2
+	MAP_CELL_CACHE_LINE_LOG2 = 6,
+	MAP_CELL_CACHE_LINE_SIZE = 1 << MAP_CELL_CACHE_LINE_LOG2,
 };
-GB_STATIC_ASSERT(MAP_CACHE_LINE_SIZE >= 64);
-void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
+GB_STATIC_ASSERT(MAP_CELL_CACHE_LINE_SIZE >= 64);
+gb_internal void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
 	i64 elem_sz = type_size_of(type);
 
 	i64 len = 1;
-	if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
-		len = MAP_CACHE_LINE_SIZE / elem_sz;
+	if (0 < elem_sz && elem_sz < MAP_CELL_CACHE_LINE_SIZE) {
+		len = MAP_CELL_CACHE_LINE_SIZE / elem_sz;
 	}
-	i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
+	i64 size = align_formula(elem_sz * len, MAP_CELL_CACHE_LINE_SIZE);
 	if (size_) *size_ = size;
 	if (len_)  *len_ = len;
 }
 
-void init_map_internal_types(Type *type) {
+gb_internal void init_map_internal_types(Type *type) {
 	GB_ASSERT(type->kind == Type_Map);
 	GB_ASSERT(t_allocator != nullptr);
 	if (type->Map.lookup_result_type != nullptr) return;
@@ -2210,7 +2221,7 @@ void init_map_internal_types(Type *type) {
 	type->Map.lookup_result_type = make_optional_ok_type(value);
 }
 
-void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
+gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
 	key = core_type(key);
 
 	if (is_type_cstring(key)) {
@@ -2249,7 +2260,7 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
 	}
 }
 
-void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
+gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 	GB_ASSERT(type->kind == Type_Map);
 	ast_node(mt, MapType, node);
 
@@ -2282,7 +2293,7 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 	// error(node, "'map' types are not yet implemented");
 }
 
-void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
+gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
 	ast_node(mt, MatrixType, node);
 	
 	Operand row = {};
@@ -2346,7 +2357,7 @@ type_assign:;
 
 
 
-Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) {
+gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) {
 	Type *bt_elem = base_type(elem);
 
 	bool is_polymorphic = is_type_polymorphic(elem);
@@ -2398,7 +2409,8 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
 		}
 		soa_struct->Struct.soa_count = cast(i32)count;
 
-		scope = create_scope(ctx->info, ctx->scope, 8);
+		scope = create_scope(ctx->info, ctx->scope);
+		string_map_init(&scope->elements, 8);
 		soa_struct->Struct.scope = scope;
 
 		String params_xyzw[4] = {
@@ -2501,20 +2513,20 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
 }
 
 
-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_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type) {
 	return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, count, generic_type, StructSoa_Fixed);
 }
 
-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) {
 	return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Slice);
 }
 
 
-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) {
 	return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic);
 }
 
-bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
+gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
 	GB_ASSERT_NOT_NULL(type);
 	if (e == nullptr) {
 		*type = t_invalid;
@@ -2997,7 +3009,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 	return false;
 }
 
-Type *check_type(CheckerContext *ctx, Ast *e) {
+gb_internal Type *check_type(CheckerContext *ctx, Ast *e) {
 	CheckerContext c = *ctx;
 	c.type_path = new_checker_type_path();
 	defer (destroy_checker_type_path(c.type_path));
@@ -3005,7 +3017,7 @@ Type *check_type(CheckerContext *ctx, Ast *e) {
 	return check_type_expr(&c, e, nullptr);
 }
 
-Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
+gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
 	Type *type = nullptr;
 	bool ok = check_type_internal(ctx, e, &type, named_type);
 
@@ -3045,7 +3057,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
 	#endif
 
 	if (is_type_typed(type)) {
-		add_type_and_value(ctx->info, e, Addressing_Type, type, empty_exact_value);
+		add_type_and_value(ctx, e, Addressing_Type, type, empty_exact_value);
 	} else {
 		gbString name = type_to_string(type);
 		error(e, "Invalid type definition of %s", name);

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 235 - 256
src/checker.cpp


+ 104 - 87
src/checker.hpp

@@ -20,7 +20,7 @@ struct ExprInfo {
 	ExactValue     value;
 };
 
-gb_inline ExprInfo *make_expr_info(AddressingMode mode, Type *type, ExactValue const &value, bool is_lhs) {
+gb_internal gb_inline ExprInfo *make_expr_info(AddressingMode mode, Type *type, ExactValue const &value, bool is_lhs) {
 	ExprInfo *ei = gb_alloc_item(permanent_allocator(), ExprInfo);
 	ei->mode   = mode;
 	ei->type   = type;
@@ -130,7 +130,7 @@ struct AttributeContext {
 	String enable_target_feature;  // will be enabled for the procedure only
 };
 
-AttributeContext make_attribute_context(String link_prefix) {
+gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) {
 	AttributeContext ac = {};
 	ac.link_prefix = link_prefix;
 	return ac;
@@ -139,12 +139,31 @@ AttributeContext make_attribute_context(String link_prefix) {
 #define DECL_ATTRIBUTE_PROC(_name) bool _name(CheckerContext *c, Ast *elem, String name, Ast *value, AttributeContext *ac)
 typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
 
-void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
+gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
 
 
+enum ProcCheckedState : u8 {
+	ProcCheckedState_Unchecked,
+	ProcCheckedState_InProgress,
+	ProcCheckedState_Checked,
+
+	ProcCheckedState_COUNT
+};
+
+char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
+	"Unchecked",
+	"In Progress",
+	"Checked",
+};
+
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
 struct DeclInfo {
 	DeclInfo *    parent; // NOTE(bill): only used for procedure literals at the moment
+
+	BlockingMutex next_mutex;
+	DeclInfo *    next_child;
+	DeclInfo *    next_sibling;
+
 	Scope *       scope;
 
 	Entity *entity;
@@ -157,13 +176,22 @@ struct DeclInfo {
 	Type *        gen_proc_type; // Precalculated
 	bool          is_using;
 	bool          where_clauses_evaluated;
-	bool          proc_checked;
+	std::atomic<ProcCheckedState> proc_checked_state;
+	BlockingMutex proc_checked_mutex;
+	isize         defer_used;
+	bool          defer_use_checked;
 
 	CommentGroup *comment;
 	CommentGroup *docs;
 
-	PtrSet<Entity *>  deps;
+	RwMutex          deps_mutex;
+	PtrSet<Entity *> deps;
+
+	RwMutex     type_info_deps_mutex;
 	PtrSet<Type *>    type_info_deps;
+
+	BlockingMutex type_and_value_mutex;
+
 	Array<BlockLabel> labels;
 };
 
@@ -195,7 +223,7 @@ enum ScopeFlag : i32 {
 	ScopeFlag_ContextDefined = 1<<16,
 };
 
-enum { DEFAULT_SCOPE_CAPACITY = 29 };
+enum { DEFAULT_SCOPE_CAPACITY = 32 };
 
 struct Scope {
 	Ast *         node;
@@ -203,7 +231,7 @@ struct Scope {
 	std::atomic<Scope *> next;
 	std::atomic<Scope *> head_child;
 
-	BlockingMutex mutex;
+	RwMutex mutex;
 	StringMap<Entity *> elements;
 	PtrSet<Scope *> imported;
 
@@ -275,7 +303,6 @@ struct UntypedExprInfo {
 };
 
 typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 
-typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
 
 enum ObjcMsgKind : u32 {
 	ObjcMsg_normal,
@@ -294,6 +321,16 @@ struct LoadFileCache {
 	StringMap<u64> hashes;
 };
 
+struct GenProcsData {
+	Array<Entity *> procs;
+	RwMutex         mutex;
+};
+
+struct GenTypesData {
+	Array<Entity *> types;
+	RwMutex         mutex;
+};
+
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 	Checker *checker;
@@ -308,7 +345,7 @@ struct CheckerInfo {
 	Scope *               init_scope;
 	Entity *              entry_point;
 	PtrSet<Entity *>      minimum_dependency_set;
-	PtrSet<isize>         minimum_dependency_type_info_set;
+	PtrMap</*type info index*/isize, /*min dep index*/isize>  minimum_dependency_type_info_set;
 
 
 
@@ -321,30 +358,17 @@ struct CheckerInfo {
 
 
 	// Below are accessed within procedures
-	// NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded,
-	// these variables will be of contention
-
-	Semaphore collect_semaphore;
-
+	RwMutex            global_untyped_mutex;
 	UntypedExprInfoMap global_untyped; // NOTE(bill): This needs to be a map and not on the Ast
 	                                   // as it needs to be iterated across afterwards
-	BlockingMutex global_untyped_mutex;
 	BlockingMutex builtin_mutex;
 
-	// NOT recursive & only used at the end of `check_proc_body`
-	// and in `add_dependency`.
-	// This is a possible source of contention but probably not
-	// too much of a problem in practice
-	BlockingMutex deps_mutex;
-
 	BlockingMutex type_and_value_mutex;
 
 	RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
 
-	RecursiveMutex gen_procs_mutex;
-	RecursiveMutex gen_types_mutex;
-	PtrMap<Ast *, Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
-	PtrMap<Type *, Array<Entity *> > gen_types; 
+	RwMutex       gen_types_mutex;
+	PtrMap<Type *, GenTypesData > gen_types;
 
 	BlockingMutex type_info_mutex; // NOT recursive
 	Array<Type *> type_info_types;
@@ -353,37 +377,39 @@ struct CheckerInfo {
 	BlockingMutex foreign_mutex; // NOT recursive
 	StringMap<Entity *> foreigns;
 
-	// only used by 'odin query'
-	bool          allow_identifier_uses;
-	BlockingMutex identifier_uses_mutex;
-	Array<Ast *>  identifier_uses;
-
 	// NOTE(bill): These are actually MPSC queues
 	// TODO(bill): Convert them to be MPSC queues
-	MPMCQueue<Entity *> definition_queue;
-	MPMCQueue<Entity *> entity_queue;
-	MPMCQueue<Entity *> required_global_variable_queue;
-	MPMCQueue<Entity *> required_foreign_imports_through_force_queue;
+	MPSCQueue<Entity *> definition_queue;
+	MPSCQueue<Entity *> entity_queue;
+	MPSCQueue<Entity *> required_global_variable_queue;
+	MPSCQueue<Entity *> required_foreign_imports_through_force_queue;
 
-	MPMCQueue<Ast *> intrinsics_entry_point_usage;
+	MPSCQueue<Ast *> intrinsics_entry_point_usage;
 
 	BlockingMutex objc_types_mutex;
 	PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
 
 	BlockingMutex load_file_mutex;
 	StringMap<LoadFileCache *> load_file_cache;
+
+	BlockingMutex all_procedures_mutex;
+	Array<ProcInfo *> all_procedures;
 };
 
 struct CheckerContext {
+	// Order matters here
+	BlockingMutex  mutex;
 	Checker *      checker;
 	CheckerInfo *  info;
+
 	AstPackage *   pkg;
 	AstFile *      file;
 	Scope *        scope;
 	DeclInfo *     decl;
 
+	// Order doesn't matter after this
 	u32            state_flags;
-	bool           in_defer; // TODO(bill): Actually handle correctly
+	bool           in_defer;
 	Type *         type_hint;
 
 	String         proc_name;
@@ -394,9 +420,7 @@ struct CheckerContext {
 	ForeignContext foreign_context;
 
 	CheckerTypePath *type_path;
-	isize            type_level; // TODO(bill): Actually handle correctly
-	CheckerPolyPath *poly_path;
-	isize            poly_level; // TODO(bill): Actually handle correctly
+	isize            type_level;
 
 	UntypedExprInfoMap *untyped;
 
@@ -413,8 +437,6 @@ struct CheckerContext {
 	Scope *    polymorphic_scope;
 
 	Ast *assignment_lhs_hint;
-
-	ProcBodyQueue *procs_to_check_queue;
 };
 
 
@@ -424,13 +446,14 @@ struct Checker {
 
 	CheckerContext builtin_ctx;
 
-	MPMCQueue<Entity *> procs_with_deferred_to_check;
+	MPSCQueue<Entity *> procs_with_deferred_to_check;
+	Array<ProcInfo *> procs_to_check;
 
-	ProcBodyQueue procs_to_check_queue;
-	Semaphore procs_to_check_semaphore;
+	BlockingMutex nested_proc_lits_mutex;
+	Array<DeclInfo *> nested_proc_lits;
 
-	// TODO(bill): Technically MPSC queue
-	MPMCQueue<UntypedExprInfo> global_untyped_queue;
+
+	MPSCQueue<UntypedExprInfo> global_untyped_queue;
 };
 
 
@@ -441,59 +464,53 @@ gb_global AstPackage *config_pkg      = nullptr;
 
 
 // CheckerInfo API
-TypeAndValue type_and_value_of_expr (Ast *expr);
-Type *       type_of_expr           (Ast *expr);
-Entity *     implicit_entity_of_node(Ast *clause);
-DeclInfo *   decl_info_of_ident     (Ast *ident);
-DeclInfo *   decl_info_of_entity    (Entity * e);
-AstFile *    ast_file_of_filename   (CheckerInfo *i, String   filename);
+gb_internal TypeAndValue type_and_value_of_expr (Ast *expr);
+gb_internal Type *       type_of_expr           (Ast *expr);
+gb_internal Entity *     implicit_entity_of_node(Ast *clause);
+gb_internal DeclInfo *   decl_info_of_ident     (Ast *ident);
+gb_internal DeclInfo *   decl_info_of_entity    (Entity * e);
+gb_internal AstFile *    ast_file_of_filename   (CheckerInfo *i, String   filename);
 // IMPORTANT: Only to use once checking is done
-isize        type_info_index        (CheckerInfo *i, Type *type, bool error_on_failure);
+gb_internal isize        type_info_index        (CheckerInfo *i, Type *type, bool error_on_failure);
 
 // Will return nullptr if not found
-Entity *entity_of_node(Ast *expr);
-
-
-Entity *scope_lookup_current(Scope *s, String const &name);
-Entity *scope_lookup (Scope *s, String const &name);
-void    scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
-Entity *scope_insert (Scope *s, Entity *entity, bool use_mutex=true);
+gb_internal Entity *entity_of_node(Ast *expr);
 
 
-void      add_type_and_value      (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
-ExprInfo *check_get_expr_info     (CheckerContext *c, Ast *expr);
-void      add_untyped             (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue value);
-void      add_entity_use          (CheckerContext *c, Ast *identifier, Entity *entity);
-void      add_implicit_entity     (CheckerContext *c, Ast *node, Entity *e);
-void      add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true);
-void      add_type_info_type      (CheckerContext *c, Type *t);
+gb_internal Entity *scope_lookup_current(Scope *s, String const &name);
+gb_internal Entity *scope_lookup (Scope *s, String const &name);
+gb_internal void    scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
+gb_internal Entity *scope_insert (Scope *s, Entity *entity);
 
-void check_add_import_decl(CheckerContext *c, Ast *decl);
-void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
 
+gb_internal void      add_type_and_value      (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
+gb_internal ExprInfo *check_get_expr_info     (CheckerContext *c, Ast *expr);
+gb_internal void      add_untyped             (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue value);
+gb_internal void      add_entity_use          (CheckerContext *c, Ast *identifier, Entity *entity);
+gb_internal void      add_implicit_entity     (CheckerContext *c, Ast *node, Entity *e);
+gb_internal void      add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true);
+gb_internal void      add_type_info_type      (CheckerContext *c, Type *t);
 
-void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
-void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
-void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
+gb_internal void check_add_import_decl(CheckerContext *c, Ast *decl);
+gb_internal void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
 
-bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
-void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
-void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
-void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
 
-CheckerTypePath *new_checker_type_path();
-void destroy_checker_type_path(CheckerTypePath *tp);
+gb_internal void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
+gb_internal void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
+gb_internal void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
 
-void    check_type_path_push(CheckerContext *c, Entity *e);
-Entity *check_type_path_pop (CheckerContext *c);
+gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
+gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
+gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
+gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
 
-CheckerPolyPath *new_checker_poly_path();
-void destroy_checker_poly_path(CheckerPolyPath *);
+gb_internal CheckerTypePath *new_checker_type_path();
+gb_internal void destroy_checker_type_path(CheckerTypePath *tp);
 
-void  check_poly_path_push(CheckerContext *c, Type *t);
-Type *check_poly_path_pop (CheckerContext *c);
+gb_internal void    check_type_path_push(CheckerContext *c, Entity *e);
+gb_internal Entity *check_type_path_pop (CheckerContext *c);
 
-void init_core_context(Checker *c);
-void init_mem_allocator(Checker *c);
+gb_internal void init_core_context(Checker *c);
+gb_internal void init_mem_allocator(Checker *c);
 
-void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
+gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);

+ 73 - 49
src/common.cpp

@@ -29,36 +29,51 @@
 #include <string.h>
 #include <atomic> // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library)
 
-gbAllocator heap_allocator(void);
+gb_internal gbAllocator heap_allocator(void);
 
-#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
+#define for_array_off(index_, off_, array_) for (isize index_ = off_; index_ < (array_).count; index_++)
+#define for_array(index_, array_) for_array_off(index_, 0, array_)
 
-i32 next_pow2(i32 n);
-i64 next_pow2(i64 n);
-isize next_pow2_isize(isize n);
-void debugf(char const *fmt, ...);
+gb_internal i32 next_pow2(i32 n);
+gb_internal i64 next_pow2(i64 n);
+gb_internal isize next_pow2_isize(isize n);
+gb_internal void debugf(char const *fmt, ...);
 
 #if defined(GB_SYSTEM_WINDOWS) && defined(GB_ARCH_32_BIT)
 #error Odin on Windows requires a 64-bit build-system. The 'Developer Command Prompt' for VS still defaults to 32-bit shell. The 64-bit shell can be found under the name 'x64 Native Tools Command Prompt' for VS. For more information, please see https://odin-lang.org/docs/install/#for-windows
 #endif
 
-#include "threading.cpp"
+template <typename T>
+struct TypeIsPointer {
+	enum {value = false};
+};
+
+template <typename T>
+struct TypeIsPointer<T *> {
+	enum {value = true};
+};
+
 #include "unicode.cpp"
 #include "array.cpp"
-#include "queue.cpp"
+#include "threading.cpp"
 #include "common_memory.cpp"
+#include "queue.cpp"
 #include "string.cpp"
 #include "range_cache.cpp"
 
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(push)
+	#pragma warning(disable: 4505)
+#endif
 
-bool is_power_of_two(i64 x) {
+gb_internal gb_inline bool is_power_of_two(i64 x) {
 	if (x <= 0) {
 		return false;
 	}
 	return !(x & (x-1));
 }
 
-int isize_cmp(isize x, isize y) {
+gb_internal int isize_cmp(isize x, isize y) {
 	if (x < y) {
 		return -1;
 	} else if (x > y) {
@@ -66,7 +81,7 @@ int isize_cmp(isize x, isize y) {
 	}
 	return 0;
 }
-int u64_cmp(u64 x, u64 y) {
+gb_internal int u64_cmp(u64 x, u64 y) {
 	if (x < y) {
 		return -1;
 	} else if (x > y) {
@@ -74,7 +89,7 @@ int u64_cmp(u64 x, u64 y) {
 	}
 	return 0;
 }
-int i64_cmp(i64 x, i64 y) {
+gb_internal int i64_cmp(i64 x, i64 y) {
 	if (x < y) {
 		return -1;
 	} else if (x > y) {
@@ -82,7 +97,7 @@ int i64_cmp(i64 x, i64 y) {
 	}
 	return 0;
 }
-int i32_cmp(i32 x, i32 y) {
+gb_internal int i32_cmp(i32 x, i32 y) {
 	if (x < y) {
 		return -1;
 	} else if (x > y) {
@@ -91,7 +106,7 @@ int i32_cmp(i32 x, i32 y) {
 	return 0;
 }
 
-u32 fnv32a(void const *data, isize len) {
+gb_internal u32 fnv32a(void const *data, isize len) {
 	u8 const *bytes = cast(u8 const *)data;
 	u32 h = 0x811c9dc5;
 	
@@ -112,7 +127,7 @@ u32 fnv32a(void const *data, isize len) {
 	return h;
 }
 
-u64 fnv64a(void const *data, isize len) {
+gb_internal u64 fnv64a(void const *data, isize len) {
 	u8 const *bytes = cast(u8 const *)data;
 	u64 h = 0xcbf29ce484222325ull;
 	
@@ -133,7 +148,7 @@ u64 fnv64a(void const *data, isize len) {
 	return h;
 }
 
-u64 u64_digit_value(Rune r) {
+gb_internal u64 u64_digit_value(Rune r) {
 	switch (r) {
 	case '0': return 0;
 	case '1': return 1;
@@ -162,7 +177,7 @@ u64 u64_digit_value(Rune r) {
 }
 
 
-u64 u64_from_string(String string) {
+gb_internal u64 u64_from_string(String string) {
 	u64 base = 10;
 	bool has_prefix = false;
 	if (string.len > 2 && string[0] == '0') {
@@ -205,7 +220,7 @@ gb_global char const global_num_to_char_table[] =
 	"abcdefghijklmnopqrstuvwxyz"
 	"@$";
 
-String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
+gb_internal String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
 	char buf[32] = {0};
 	isize i = gb_size_of(buf);
 
@@ -220,7 +235,7 @@ String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
 	gb_memmove(out_buf, &buf[i], len);
 	return make_string(cast(u8 *)out_buf, len);
 }
-String i64_to_string(i64 a, char *out_buf, isize out_buf_len) {
+gb_internal String i64_to_string(i64 a, char *out_buf, isize out_buf_len) {
 	char buf[32] = {0};
 	isize i = gb_size_of(buf);
 	bool negative = false;
@@ -282,17 +297,17 @@ gb_global u64 const unsigned_integer_maxs[] = {
 };
 
 
-bool add_overflow_u64(u64 x, u64 y, u64 *result) {
+gb_internal bool add_overflow_u64(u64 x, u64 y, u64 *result) {
 	*result = x + y;
 	return *result < x || *result < y;
 }
 
-bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
+gb_internal bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
 	*result = x - y;
 	return *result > x;
 }
 
-void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
+gb_internal void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
 #if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT)
 	*lo = _umul128(x, y, hi);
 #else
@@ -342,7 +357,7 @@ struct StringIntern {
 PtrMap<uintptr, StringIntern *> string_intern_map = {}; // Key: u64
 gb_global Arena string_intern_arena = {};
 
-char const *string_intern(char const *text, isize len) {
+gb_internal char const *string_intern(char const *text, isize len) {
 	u64 hash = gb_fnv64a(text, len);
 	uintptr key = cast(uintptr)(hash ? hash : 1);
 	StringIntern **found = map_get(&string_intern_map, key);
@@ -363,18 +378,18 @@ char const *string_intern(char const *text, isize len) {
 	return new_intern->str;
 }
 
-char const *string_intern(String const &string) {
+gb_internal char const *string_intern(String const &string) {
 	return string_intern(cast(char const *)string.text, string.len);
 }
 
-void init_string_interner(void) {
-	map_init(&string_intern_map, heap_allocator());
+gb_internal void init_string_interner(void) {
+	map_init(&string_intern_map);
 }
 
 
 
 
-i32 next_pow2(i32 n) {
+gb_internal i32 next_pow2(i32 n) {
 	if (n <= 0) {
 		return 0;
 	}
@@ -387,7 +402,7 @@ i32 next_pow2(i32 n) {
 	n++;
 	return n;
 }
-i64 next_pow2(i64 n) {
+gb_internal i64 next_pow2(i64 n) {
 	if (n <= 0) {
 		return 0;
 	}
@@ -401,7 +416,7 @@ i64 next_pow2(i64 n) {
 	n++;
 	return n;
 }
-isize next_pow2_isize(isize n) {
+gb_internal isize next_pow2_isize(isize n) {
 	if (n <= 0) {
 		return 0;
 	}
@@ -417,7 +432,7 @@ isize next_pow2_isize(isize n) {
 	n++;
 	return n;
 }
-u32 next_pow2_u32(u32 n) {
+gb_internal u32 next_pow2_u32(u32 n) {
 	if (n == 0) {
 		return 0;
 	}
@@ -432,7 +447,7 @@ u32 next_pow2_u32(u32 n) {
 }
 
 
-i32 bit_set_count(u32 x) {
+gb_internal i32 bit_set_count(u32 x) {
 	x -= ((x >> 1) & 0x55555555);
 	x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
 	x = (((x >> 4) + x) & 0x0f0f0f0f);
@@ -442,13 +457,13 @@ i32 bit_set_count(u32 x) {
 	return cast(i32)(x & 0x0000003f);
 }
 
-i64 bit_set_count(u64 x) {
+gb_internal i64 bit_set_count(u64 x) {
 	u32 a = *(cast(u32 *)&x);
 	u32 b = *(cast(u32 *)&x + 1);
 	return bit_set_count(a) + bit_set_count(b);
 }
 
-u32 floor_log2(u32 x) {
+gb_internal u32 floor_log2(u32 x) {
 	x |= x >> 1;
 	x |= x >> 2;
 	x |= x >> 4;
@@ -457,7 +472,7 @@ u32 floor_log2(u32 x) {
 	return cast(u32)(bit_set_count(x) - 1);
 }
 
-u64 floor_log2(u64 x) {
+gb_internal u64 floor_log2(u64 x) {
 	x |= x >> 1;
 	x |= x >> 2;
 	x |= x >> 4;
@@ -468,7 +483,7 @@ u64 floor_log2(u64 x) {
 }
 
 
-u32 ceil_log2(u32 x) {
+gb_internal u32 ceil_log2(u32 x) {
 	i32 y = cast(i32)(x & (x-1));
 	y |= -y;
 	y >>= 32-1;
@@ -480,7 +495,7 @@ u32 ceil_log2(u32 x) {
 	return cast(u32)(bit_set_count(x) - 1 - y);
 }
 
-u64 ceil_log2(u64 x) {
+gb_internal u64 ceil_log2(u64 x) {
 	i64 y = cast(i64)(x & (x-1));
 	y |= -y;
 	y >>= 64-1;
@@ -493,7 +508,7 @@ u64 ceil_log2(u64 x) {
 	return cast(u64)(bit_set_count(x) - 1 - y);
 }
 
-u32 prev_pow2(u32 n) {
+gb_internal u32 prev_pow2(u32 n) {
 	if (n == 0) {
 		return 0;
 	}
@@ -504,7 +519,7 @@ u32 prev_pow2(u32 n) {
 	n |= n >> 16;
 	return n - (n >> 1);
 }
-i32 prev_pow2(i32 n) {
+gb_internal i32 prev_pow2(i32 n) {
 	if (n <= 0) {
 		return 0;
 	}
@@ -515,7 +530,7 @@ i32 prev_pow2(i32 n) {
 	n |= n >> 16;
 	return n - (n >> 1);
 }
-i64 prev_pow2(i64 n) {
+gb_internal i64 prev_pow2(i64 n) {
 	if (n <= 0) {
 		return 0;
 	}
@@ -528,7 +543,7 @@ i64 prev_pow2(i64 n) {
 	return n - (n >> 1);
 }
 
-u16 f32_to_f16(f32 value) {
+gb_internal u16 f32_to_f16(f32 value) {
 	union { u32 i; f32 f; } v;
 	i32 i, s, e, m;
 
@@ -579,7 +594,7 @@ u16 f32_to_f16(f32 value) {
 	}
 }
 
-f32 f16_to_f32(u16 value) {
+gb_internal f32 f16_to_f32(u16 value) {
 	typedef union { u32 u; f32 f; } fp32;
 	fp32 v;
 
@@ -595,7 +610,7 @@ f32 f16_to_f32(u16 value) {
 	return v.f;
 }
 
-f64 gb_sqrt(f64 x) {
+gb_internal gb_inline f64 gb_sqrt(f64 x) {
 	return sqrt(x);
 }
 
@@ -623,7 +638,7 @@ f64 gb_sqrt(f64 x) {
 
 #if defined(GB_SYSTEM_WINDOWS)
 
-wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
+gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
 	u32 i, j;
 
 	u32 len = cast(u32)string16_len(cmd_line);
@@ -706,11 +721,13 @@ enum LoadedFileError {
 	LoadedFile_COUNT,
 };
 
-LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_file, bool copy_file_contents) {
+gb_internal LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_file, bool copy_file_contents) {
 	LoadedFileError err = LoadedFile_None;
 	
 	if (!copy_file_contents) {
 	#if defined(GB_SYSTEM_WINDOWS)
+		TEMPORARY_ALLOCATOR_GUARD();
+
 		isize w_len = 0;
 		wchar_t *w_str = gb__alloc_utf8_to_ucs2(temporary_allocator(), fullpath, &w_len);
 		if (w_str == nullptr) {
@@ -811,7 +828,9 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
 
 #define USE_DAMERAU_LEVENSHTEIN 1
 
-isize levenstein_distance_case_insensitive(String const &a, String const &b) {
+gb_internal isize levenstein_distance_case_insensitive(String const &a, String const &b) {
+	TEMPORARY_ALLOCATOR_GUARD();
+
 	isize w = b.len+1;
 	isize h = a.len+1;
 	isize *matrix = gb_alloc_array(temporary_allocator(), isize, w*h);
@@ -870,16 +889,16 @@ struct DidYouMeanAnswers {
 
 enum {MAX_SMALLEST_DID_YOU_MEAN_DISTANCE = 3-USE_DAMERAU_LEVENSHTEIN};
 
-DidYouMeanAnswers did_you_mean_make(gbAllocator allocator, isize cap, String const &key) {
+gb_internal DidYouMeanAnswers did_you_mean_make(gbAllocator allocator, isize cap, String const &key) {
 	DidYouMeanAnswers d = {};
 	array_init(&d.distances, allocator, 0, cap);
 	d.key = key;
 	return d;
 }
-void did_you_mean_destroy(DidYouMeanAnswers *d) {
+gb_internal void did_you_mean_destroy(DidYouMeanAnswers *d) {
 	array_free(&d->distances);
 }
-void did_you_mean_append(DidYouMeanAnswers *d, String const &target) {
+gb_internal void did_you_mean_append(DidYouMeanAnswers *d, String const &target) {
 	if (target.len == 0 || target == "_") {
 		return;
 	}
@@ -888,7 +907,7 @@ void did_you_mean_append(DidYouMeanAnswers *d, String const &target) {
 	dat.distance = levenstein_distance_case_insensitive(d->key, target);
 	array_add(&d->distances, dat);
 }
-Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
+gb_internal Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
 	gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
 	isize count = 0;
 	for (isize i = 0; i < d->distances.count; i++) {
@@ -900,3 +919,8 @@ Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
 	}
 	return slice_array(d->distances, 0, count);
 }
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(pop)
+#endif

+ 149 - 76
src/common_memory.cpp

@@ -1,5 +1,5 @@
 
-gb_inline void zero_size(void *ptr, isize len) {
+gb_internal gb_inline void zero_size(void *ptr, isize len) {
 	memset(ptr, 0, len);
 }
 
@@ -7,43 +7,31 @@ gb_inline void zero_size(void *ptr, isize len) {
 
 
 template <typename U, typename V>
-gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
+gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
 
 template <typename U, typename V>
-gb_inline U const &bit_cast(V const &v) { return reinterpret_cast<U const &>(v); }
+gb_internal gb_inline U const &bit_cast(V const &v) { return reinterpret_cast<U const &>(v); }
 
 
-gb_inline i64 align_formula(i64 size, i64 align) {
-	if (align > 0) {
-		i64 result = size + align-1;
-		return result - result%align;
-	}
-	return size;
+gb_internal gb_inline i64 align_formula(i64 size, i64 align) {
+	i64 result = size + align-1;
+	return result - (i64)((u64)result%(u64)align);
 }
-gb_inline isize align_formula_isize(isize size, isize align) {
-	if (align > 0) {
-		isize result = size + align-1;
-		return result - result%align;
-	}
-	return size;
+gb_internal gb_inline isize align_formula_isize(isize size, isize align) {
+	isize result = size + align-1;
+	return result - (isize)((usize)result%(usize)align);
 }
-gb_inline void *align_formula_ptr(void *ptr, isize align) {
-	if (align > 0) {
-		uintptr result = (cast(uintptr)ptr) + align-1;
-		return (void *)(result - result%align);
-	}
-	return ptr;
+gb_internal gb_inline void *align_formula_ptr(void *ptr, isize align) {
+	uintptr result = (cast(uintptr)ptr) + align-1;
+	return (void *)(result - result%align);
 }
 
 
 gb_global BlockingMutex global_memory_block_mutex;
-gb_global BlockingMutex global_memory_allocator_mutex;
 
-void platform_virtual_memory_init(void);
+gb_internal void platform_virtual_memory_init(void);
 
-void virtual_memory_init(void) {
-	mutex_init(&global_memory_block_mutex);
-	mutex_init(&global_memory_allocator_mutex);
+gb_internal void virtual_memory_init(void) {
 	platform_virtual_memory_init();
 }
 
@@ -57,22 +45,23 @@ struct MemoryBlock {
 };
 
 struct Arena {
-	MemoryBlock *curr_block;
-	isize        minimum_block_size;
-	bool         ignore_mutex;
+	MemoryBlock * curr_block;
+	isize         minimum_block_size;
+	BlockingMutex mutex;
+	isize         temp_count;
 };
 
 enum { DEFAULT_MINIMUM_BLOCK_SIZE = 8ll*1024ll*1024ll };
 
 gb_global isize DEFAULT_PAGE_SIZE = 4096;
 
-MemoryBlock *virtual_memory_alloc(isize size);
-void virtual_memory_dealloc(MemoryBlock *block);
-void *arena_alloc(Arena *arena, isize min_size, isize alignment);
-void arena_free_all(Arena *arena);
+gb_internal MemoryBlock *virtual_memory_alloc(isize size);
+gb_internal void virtual_memory_dealloc(MemoryBlock *block);
+gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment);
+gb_internal void arena_free_all(Arena *arena);
 
 
-isize arena_align_forward_offset(Arena *arena, isize alignment) {
+gb_internal isize arena_align_forward_offset(Arena *arena, isize alignment) {
 	isize alignment_offset = 0;
 	isize ptr = cast(isize)(arena->curr_block->base + arena->curr_block->used);
 	isize mask = alignment-1;
@@ -82,13 +71,10 @@ isize arena_align_forward_offset(Arena *arena, isize alignment) {
 	return alignment_offset;
 }
 
-void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
+gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
 	GB_ASSERT(gb_is_power_of_two(alignment));
 	
-	BlockingMutex *mutex = &global_memory_allocator_mutex;
-	if (!arena->ignore_mutex) {
-		mutex_lock(mutex);
-	}
+	mutex_lock(&arena->mutex);
 	
 	isize size = 0;
 	if (arena->curr_block != nullptr) {
@@ -115,15 +101,13 @@ void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
 	curr_block->used += size;
 	GB_ASSERT(curr_block->used <= curr_block->size);
 	
-	if (!arena->ignore_mutex) {
-		mutex_unlock(mutex);
-	}
+	mutex_unlock(&arena->mutex);
 	
 	// NOTE(bill): memory will be zeroed by default due to virtual memory 
 	return ptr;	
 }
 
-void arena_free_all(Arena *arena) {
+gb_internal void arena_free_all(Arena *arena) {
 	while (arena->curr_block != nullptr) {
 		MemoryBlock *free_block = arena->curr_block;
 		arena->curr_block = free_block->prev;
@@ -142,12 +126,12 @@ struct PlatformMemoryBlock {
 gb_global std::atomic<isize> global_platform_memory_total_usage;
 gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
 
-PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
-void platform_virtual_memory_free(PlatformMemoryBlock *block);
-void platform_virtual_memory_protect(void *memory, isize size);
+gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
+gb_internal void platform_virtual_memory_free(PlatformMemoryBlock *block);
+gb_internal void platform_virtual_memory_protect(void *memory, isize size);
 
 #if defined(GB_SYSTEM_WINDOWS)
-	void platform_virtual_memory_init(void) {
+	gb_internal void platform_virtual_memory_init(void) {
 		global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel;	
 		global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel;
 		
@@ -157,7 +141,7 @@ void platform_virtual_memory_protect(void *memory, isize size);
 		GB_ASSERT(gb_is_power_of_two(DEFAULT_PAGE_SIZE));
 	}
 
-	PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
+	gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
 		PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
 		if (pmblock == nullptr) {
 			gb_printf_err("Out of Virtual memory, oh no...\n");
@@ -165,20 +149,20 @@ void platform_virtual_memory_protect(void *memory, isize size);
 			gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
 			GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
 		}
-		global_platform_memory_total_usage += total_size;
+		global_platform_memory_total_usage.fetch_add(total_size);
 		return pmblock;
 	}
-	void platform_virtual_memory_free(PlatformMemoryBlock *block) {
-		global_platform_memory_total_usage -= block->total_size;
+	gb_internal void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+		global_platform_memory_total_usage.fetch_sub(block->total_size);
 		GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
 	}
-	void platform_virtual_memory_protect(void *memory, isize size) {
+	gb_internal void platform_virtual_memory_protect(void *memory, isize size) {
 		DWORD old_protect = 0;
 		BOOL is_protected = VirtualProtect(memory, size, PAGE_NOACCESS, &old_protect);
 		GB_ASSERT(is_protected);
 	}
 #else
-	void platform_virtual_memory_init(void) {
+	gb_internal void platform_virtual_memory_init(void) {
 		global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel;	
 		global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel;
 		
@@ -186,7 +170,7 @@ void platform_virtual_memory_protect(void *memory, isize size);
 		GB_ASSERT(gb_is_power_of_two(DEFAULT_PAGE_SIZE));
 	}
 	
-	PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
+	gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
 		PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 		if (pmblock == nullptr) {
 			gb_printf_err("Out of Virtual memory, oh no...\n");
@@ -197,18 +181,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
 		global_platform_memory_total_usage += total_size;
 		return pmblock;
 	}
-	void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+	gb_internal void platform_virtual_memory_free(PlatformMemoryBlock *block) {
 		isize size = block->total_size;
 		global_platform_memory_total_usage -= size;
 		munmap(block, size);
 	}
-	void platform_virtual_memory_protect(void *memory, isize size) {
+	gb_internal void platform_virtual_memory_protect(void *memory, isize size) {
 		int err = mprotect(memory, size, PROT_NONE);
 		GB_ASSERT(err == 0);
 	}
 #endif
 
-MemoryBlock *virtual_memory_alloc(isize size) {
+gb_internal MemoryBlock *virtual_memory_alloc(isize size) {
 	isize const page_size = DEFAULT_PAGE_SIZE; 
 	
 	isize total_size     = size + gb_size_of(PlatformMemoryBlock);
@@ -250,7 +234,7 @@ MemoryBlock *virtual_memory_alloc(isize size) {
 	return &pmblock->block;
 }
 
-void virtual_memory_dealloc(MemoryBlock *block_to_free) {
+gb_internal void virtual_memory_dealloc(MemoryBlock *block_to_free) {
 	PlatformMemoryBlock *block = cast(PlatformMemoryBlock *)block_to_free;
 	if (block != nullptr) {
 		mutex_lock(&global_memory_block_mutex);
@@ -262,12 +246,87 @@ void virtual_memory_dealloc(MemoryBlock *block_to_free) {
 	}
 }
 
+struct ArenaTemp {
+	Arena *      arena;
+	MemoryBlock *block;
+	isize        used;
+};
+
+ArenaTemp arena_temp_begin(Arena *arena) {
+	GB_ASSERT(arena);
+	MUTEX_GUARD(&arena->mutex);
 
+	ArenaTemp temp = {};
+	temp.arena = arena;
+	temp.block = arena->curr_block;
+	if (arena->curr_block != nullptr) {
+		temp.used = arena->curr_block->used;
+	}
+	arena->temp_count += 1;
+	return temp;
+}
+
+void arena_temp_end(ArenaTemp const &temp) {
+	GB_ASSERT(temp.arena);
+	Arena *arena = temp.arena;
+	MUTEX_GUARD(&arena->mutex);
+
+	if (temp.block) {
+		bool memory_block_found = false;
+		for (MemoryBlock *block = arena->curr_block; block != nullptr; block = block->prev) {
+			if (block == temp.block) {
+				memory_block_found = true;
+				break;
+			}
+		}
+		GB_ASSERT_MSG(memory_block_found, "memory block stored within ArenaTemp not owned by Arena");
 
+		while (arena->curr_block != temp.block) {
+			MemoryBlock *free_block = arena->curr_block;
+			if (free_block != nullptr) {
+				arena->curr_block = free_block->prev;
+				virtual_memory_dealloc(free_block);
+			}
+		}
 
-GB_ALLOCATOR_PROC(arena_allocator_proc);
+		MemoryBlock *block = arena->curr_block;
+		if (block) {
+			GB_ASSERT_MSG(block->used >= temp.used, "out of order use of arena_temp_end");
+			isize amount_to_zero = gb_min(block->used - temp.used, block->size - block->used);
+			gb_zero_size(block->base + temp.used, amount_to_zero);
+			block->used = temp.used;
+		}
+	}
 
-gbAllocator arena_allocator(Arena *arena) {
+	GB_ASSERT_MSG(arena->temp_count > 0, "double-use of arena_temp_end");
+	arena->temp_count -= 1;
+}
+
+void arena_temp_ignore(ArenaTemp const &temp) {
+	GB_ASSERT(temp.arena);
+	Arena *arena = temp.arena;
+	MUTEX_GUARD(&arena->mutex);
+
+	GB_ASSERT_MSG(arena->temp_count > 0, "double-use of arena_temp_end");
+	arena->temp_count -= 1;
+}
+
+
+
+struct ArenaTempGuard {
+	ArenaTempGuard(Arena *arena) {
+		this->temp = arena_temp_begin(arena);
+	}
+	~ArenaTempGuard() {
+		arena_temp_end(this->temp);
+	}
+	ArenaTemp temp;
+};
+
+
+gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc);
+
+gb_internal gbAllocator arena_allocator(Arena *arena) {
 	gbAllocator a;
 	a.proc = arena_allocator_proc;
 	a.data = arena;
@@ -275,7 +334,7 @@ gbAllocator arena_allocator(Arena *arena) {
 }
 
 
-GB_ALLOCATOR_PROC(arena_allocator_proc) {
+gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
 	void *ptr = nullptr;
 	Arena *arena = cast(Arena *)allocator_data;
 	GB_ASSERT_NOT_NULL(arena);
@@ -306,31 +365,44 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
 }
 
 
-gb_global gb_thread_local Arena permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE, true};
-gbAllocator permanent_allocator() {
+gb_global gb_thread_local Arena permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+gb_internal gbAllocator permanent_allocator() {
 	return arena_allocator(&permanent_arena);
 }
 
-gbAllocator temporary_allocator() {
-	return permanent_allocator();
+gb_global gb_thread_local Arena temporary_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+gb_internal gbAllocator temporary_allocator() {
+	return arena_allocator(&temporary_arena);
 }
 
 
+#define TEMP_ARENA_GUARD(arena) ArenaTempGuard GB_DEFER_3(_arena_guard_){arena}
 
 
+// #define TEMPORARY_ALLOCATOR_GUARD()
+#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(&temporary_arena)
+#define PERMANENT_ALLOCATOR_GUARD()
 
 
-GB_ALLOCATOR_PROC(heap_allocator_proc);
 
-gbAllocator heap_allocator(void) {
-	gbAllocator a;
-	a.proc = heap_allocator_proc;
-	a.data = nullptr;
-	return a;
+gb_internal bool IS_ODIN_DEBUG(void);
+
+gb_internal GB_ALLOCATOR_PROC(heap_allocator_proc);
+
+
+gb_global gb_thread_local Arena heap_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+gb_internal gbAllocator heap_allocator(void) {
+	if (IS_ODIN_DEBUG()) {
+		gbAllocator a;
+		a.proc = heap_allocator_proc;
+		a.data = nullptr;
+		return a;
+	}
+	return arena_allocator(&heap_arena);
 }
 
 
-GB_ALLOCATOR_PROC(heap_allocator_proc) {
+gb_internal GB_ALLOCATOR_PROC(heap_allocator_proc) {
 	void *ptr = nullptr;
 	gb_unused(allocator_data);
 	gb_unused(old_size);
@@ -460,21 +532,22 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
 
 
 template <typename T>
-void resize_array_raw(T **array, gbAllocator const &a, isize old_count, isize new_count) {
+gb_internal isize resize_array_raw(T **array, gbAllocator const &a, isize old_count, isize new_count, isize custom_alignment=1) {
 	GB_ASSERT(new_count >= 0);
 	if (new_count == 0) {
 		gb_free(a, *array);
 		*array = nullptr;
-		return;
+		return 0;
 	}
 	if (new_count < old_count) {
-		return;
+		return old_count;
 	}
 	isize old_size = old_count * gb_size_of(T);
 	isize new_size = new_count * gb_size_of(T);
-	isize alignment = gb_align_of(T);
+	isize alignment = gb_max(gb_align_of(T), custom_alignment);
 	auto new_data = cast(T *)gb_resize_align(a, *array, old_size, new_size, alignment);
 	GB_ASSERT(new_data != nullptr);
 	*array = new_data;
+	return new_count;
 }
 

+ 24 - 38
src/docs.cpp

@@ -28,7 +28,7 @@ gb_global char const *print_entity_names[Entity_Count] = {
 };
 
 
-GB_COMPARE_PROC(cmp_entities_for_printing) {
+gb_internal GB_COMPARE_PROC(cmp_entities_for_printing) {
 	GB_ASSERT(a != nullptr);
 	GB_ASSERT(b != nullptr);
 	Entity *x = *cast(Entity **)a;
@@ -49,14 +49,13 @@ GB_COMPARE_PROC(cmp_entities_for_printing) {
 	int ox = print_entity_kind_ordering[x->kind];
 	int oy = print_entity_kind_ordering[y->kind];
 	res = ox - oy;
-	if (res != 0) {
-		return res;
+	if (res == 0) {
+		res = string_compare(x->token.string, y->token.string);
 	}
-	res = string_compare(x->token.string, y->token.string);
 	return res;
 }
 
-GB_COMPARE_PROC(cmp_ast_package_by_name) {
+gb_internal GB_COMPARE_PROC(cmp_ast_package_by_name) {
 	GB_ASSERT(a != nullptr);
 	GB_ASSERT(b != nullptr);
 	AstPackage *x = *cast(AstPackage **)a;
@@ -67,7 +66,7 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) {
 #include "docs_format.cpp"
 #include "docs_writer.cpp"
 
-void print_doc_line(i32 indent, String const &data) {
+gb_internal void print_doc_line(i32 indent, String const &data) {
 	while (indent --> 0) {
 		gb_printf("\t");
 	}
@@ -75,7 +74,7 @@ void print_doc_line(i32 indent, String const &data) {
 	gb_printf("\n");
 }
 
-void print_doc_line(i32 indent, char const *fmt, ...) {
+gb_internal void print_doc_line(i32 indent, char const *fmt, ...) {
 	while (indent --> 0) {
 		gb_printf("\t");
 	}
@@ -85,16 +84,7 @@ void print_doc_line(i32 indent, char const *fmt, ...) {
 	va_end(va);
 	gb_printf("\n");
 }
-void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
-	while (indent --> 0) {
-		gb_printf("\t");
-	}
-	va_list va;
-	va_start(va, fmt);
-	gb_printf_va(fmt, va);
-	va_end(va);
-}
-void print_doc_line_no_newline(i32 indent, String const &data) {
+gb_internal void print_doc_line_no_newline(i32 indent, String const &data) {
 	while (indent --> 0) {
 		gb_printf("\t");
 	}
@@ -102,7 +92,7 @@ void print_doc_line_no_newline(i32 indent, String const &data) {
 }
 
 
-bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
+gb_internal bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
 	if (g == nullptr) {
 		return false;
 	}
@@ -191,7 +181,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
 
 
 
-void print_doc_expr(Ast *expr) {
+gb_internal void print_doc_expr(Ast *expr) {
 	gbString s = nullptr;
 	if (build_context.cmd_doc_flags & CmdDocFlag_Short) {
 		s = expr_to_string_shorthand(expr);
@@ -202,8 +192,7 @@ void print_doc_expr(Ast *expr) {
 	gb_string_free(s);
 }
 
-
-void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
+gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
 	if (pkg == nullptr) {
 		return;
 	}
@@ -220,10 +209,10 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
 	}
 
 	if (pkg->scope != nullptr) {
-		auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
+		auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.count);
 		defer (array_free(&entities));
-		for_array(i, pkg->scope->elements.entries) {
-			Entity *e = pkg->scope->elements.entries[i].value;
+		for (auto const &entry : pkg->scope->elements) {
+			Entity *e = entry.value;
 			switch (e->kind) {
 			case Entity_Invalid:
 			case Entity_Builtin:
@@ -240,23 +229,20 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
 				// Fine
 				break;
 			}
-			array_add(&entities, e);
-		}
-		gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
-		bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
-
-		EntityKind curr_entity_kind = Entity_Invalid;
-		for_array(i, entities) {
-			Entity *e = entities[i];
 			if (e->pkg != pkg) {
 				continue;
 			}
 			if (!is_entity_exported(e)) {
 				continue;
 			}
+			array_add(&entities, e);
+		}
+		gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
 
+		bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
 
+		EntityKind curr_entity_kind = Entity_Invalid;
+		for (Entity *e : entities) {
 			if (curr_entity_kind != e->kind) {
 				if (curr_entity_kind != Entity_Invalid) {
 					print_doc_line(0, "");
@@ -297,7 +283,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
 				print_doc_expr(init_expr);
 			}
 
-			gb_printf(";\n");
+			gb_printf("\n");
 
 			if (show_docs) {
 				print_doc_comment_group_string(3, docs);
@@ -320,7 +306,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
 
 }
 
-void generate_documentation(Checker *c) {
+gb_internal void generate_documentation(Checker *c) {
 	CheckerInfo *info = &c->info;
 
 	if (build_context.cmd_doc_flags & CmdDocFlag_DocFormat) {
@@ -358,9 +344,9 @@ void generate_documentation(Checker *c) {
 
 		odin_doc_write(info, output_file_path);
 	} else {
-		auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.entries.count);
-		for_array(i, info->packages.entries) {
-			AstPackage *pkg = info->packages.entries[i].value;
+		auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.count);
+		for (auto const &entry : info->packages) {
+			AstPackage *pkg = entry.value;
 			if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
 				array_add(&pkgs, pkg);
 			} else {

+ 2 - 2
src/docs_format.cpp

@@ -27,14 +27,14 @@ struct OdinDocHeaderBase {
 };
 
 template <typename T>
-Slice<T> from_array(OdinDocHeaderBase *base, OdinDocArray<T> const &a) {
+gb_internal Slice<T> from_array(OdinDocHeaderBase *base, OdinDocArray<T> const &a) {
 	Slice<T> s = {};
 	s.data  = cast(T *)(cast(uintptr)base + cast(uintptr)a.offset);
 	s.count = cast(isize)a.length;
 	return s;
 }
 
-String from_string(OdinDocHeaderBase *base, OdinDocString const &s) {
+gb_internal String from_string(OdinDocHeaderBase *base, OdinDocString const &s) {
 	String str = {};
 	str.text = cast(u8 *)(cast(uintptr)base + cast(uintptr)s.offset);
 	str.len  = cast(isize)s.length;

+ 58 - 62
src/docs_writer.cpp

@@ -11,7 +11,7 @@ enum OdinDocWriterState {
 	OdinDocWriterState_Writing,
 };
 
-char const* OdinDocWriterState_strings[] {
+gb_global char const* OdinDocWriterState_strings[] {
 	"preparing",
 	"writing  ",
 };
@@ -40,26 +40,25 @@ struct OdinDocWriter {
 	OdinDocWriterItemTracker<u8> blob;
 };
 
-OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e);
-OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type);
+gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e);
+gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type);
 
 template <typename T>
-void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker<T> *t, isize size) {
+gb_internal void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker<T> *t, isize size) {
 	t->len = size;
 	t->cap = size;
 }
 
 
-void odin_doc_writer_prepare(OdinDocWriter *w) {
+gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
 	w->state = OdinDocWriterState_Preparing;
 
-	gbAllocator a = heap_allocator();
-	string_map_init(&w->string_cache, a);
+	string_map_init(&w->string_cache);
 
-	map_init(&w->file_cache, a);
-	map_init(&w->pkg_cache, a);
-	map_init(&w->entity_cache, a);
-	map_init(&w->type_cache, a);
+	map_init(&w->file_cache);
+	map_init(&w->pkg_cache);
+	map_init(&w->entity_cache);
+	map_init(&w->type_cache);
 
 	odin_doc_writer_item_tracker_init(&w->files,    1);
 	odin_doc_writer_item_tracker_init(&w->pkgs,     1);
@@ -70,7 +69,7 @@ void odin_doc_writer_prepare(OdinDocWriter *w) {
 }
 
 
-void odin_doc_writer_destroy(OdinDocWriter *w) {
+gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
 	gb_free(heap_allocator(), w->data);
 
 	string_map_destroy(&w->string_cache);
@@ -83,7 +82,7 @@ void odin_doc_writer_destroy(OdinDocWriter *w) {
 
 
 template <typename T>
-void odin_doc_writer_tracker_size(isize *offset, OdinDocWriterItemTracker<T> *t, isize alignment=1) {
+gb_internal void odin_doc_writer_tracker_size(isize *offset, OdinDocWriterItemTracker<T> *t, isize alignment=1) {
 	isize size = t->cap*gb_size_of(T);
 	isize align = gb_max(gb_align_of(T), alignment);
 	*offset = align_formula_isize(*offset, align);
@@ -91,7 +90,7 @@ void odin_doc_writer_tracker_size(isize *offset, OdinDocWriterItemTracker<T> *t,
 	*offset += size;
 }
 
-isize odin_doc_writer_calc_total_size(OdinDocWriter *w) {
+gb_internal isize odin_doc_writer_calc_total_size(OdinDocWriter *w) {
 	isize total_size = gb_size_of(OdinDocHeader);
 	odin_doc_writer_tracker_size(&total_size, &w->files);
 	odin_doc_writer_tracker_size(&total_size, &w->pkgs);
@@ -102,7 +101,7 @@ isize odin_doc_writer_calc_total_size(OdinDocWriter *w) {
 	return total_size;
 }
 
-void odin_doc_writer_start_writing(OdinDocWriter *w) {
+gb_internal void odin_doc_writer_start_writing(OdinDocWriter *w) {
 	w->state = OdinDocWriterState_Writing;
 
 	string_map_clear(&w->string_cache);
@@ -118,7 +117,7 @@ void odin_doc_writer_start_writing(OdinDocWriter *w) {
 	w->header = cast(OdinDocHeader *)w->data;
 }
 
-u32 hash_data_after_header(OdinDocHeaderBase *base, void *data, isize data_len) {
+gb_internal u32 hash_data_after_header(OdinDocHeaderBase *base, void *data, isize data_len) {
 	u8 *start = cast(u8 *)data;
 	u8 *end = start + base->total_size;
 	start += base->header_size;
@@ -132,13 +131,13 @@ u32 hash_data_after_header(OdinDocHeaderBase *base, void *data, isize data_len)
 
 
 template <typename T>
-void odin_doc_writer_assign_tracker(OdinDocArray<T> *array, OdinDocWriterItemTracker<T> const &t) {
+gb_internal void odin_doc_writer_assign_tracker(OdinDocArray<T> *array, OdinDocWriterItemTracker<T> const &t) {
 	array->offset = cast(u32)t.offset;
 	array->length = cast(u32)t.len;
 }
 
 
-void odin_doc_writer_end_writing(OdinDocWriter *w) {
+gb_internal void odin_doc_writer_end_writing(OdinDocWriter *w) {
 	OdinDocHeader *h = w->header;
 
 	gb_memmove(h->base.magic, OdinDocHeader_MagicString, gb_strlen(OdinDocHeader_MagicString));
@@ -156,7 +155,7 @@ void odin_doc_writer_end_writing(OdinDocWriter *w) {
 }
 
 template <typename T>
-u32 odin_doc_write_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, T const *item, T **dst=nullptr) {
+gb_internal u32 odin_doc_write_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, T const *item, T **dst=nullptr) {
 	if (w->state == OdinDocWriterState_Preparing) {
 		t->cap += 1;
 		if (dst) *dst = nullptr;
@@ -175,7 +174,7 @@ u32 odin_doc_write_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, T cons
 }
 
 template <typename T>
-T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index) {
+gb_internal T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index) {
 	if (w->state != OdinDocWriterState_Writing) {
 		return nullptr;
 	}
@@ -184,7 +183,7 @@ T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index
 	return cast(T *)data;
 }
 
-OdinDocString odin_doc_write_string_without_cache(OdinDocWriter *w, String const &str) {
+gb_internal OdinDocString odin_doc_write_string_without_cache(OdinDocWriter *w, String const &str) {
 	OdinDocString res = {};
 
 	if (w->state == OdinDocWriterState_Preparing) {
@@ -204,7 +203,7 @@ OdinDocString odin_doc_write_string_without_cache(OdinDocWriter *w, String const
 	return res;
 }
 
-OdinDocString odin_doc_write_string(OdinDocWriter *w, String const &str) {
+gb_internal OdinDocString odin_doc_write_string(OdinDocWriter *w, String const &str) {
 	OdinDocString *c = string_map_get(&w->string_cache, str);
 	if (c != nullptr) {
 		if (w->state == OdinDocWriterState_Writing) {
@@ -223,7 +222,7 @@ OdinDocString odin_doc_write_string(OdinDocWriter *w, String const &str) {
 
 
 template <typename T>
-OdinDocArray<T> odin_write_slice(OdinDocWriter *w, T *data, isize len) {
+gb_internal OdinDocArray<T> odin_write_slice(OdinDocWriter *w, T *data, isize len) {
 	GB_ASSERT(gb_align_of(T) <= 4);
 	if (len <= 0) {
 		return {0, 0};
@@ -249,12 +248,12 @@ OdinDocArray<T> odin_write_slice(OdinDocWriter *w, T *data, isize len) {
 
 
 template <typename T>
-OdinDocArray<T> odin_write_item_as_slice(OdinDocWriter *w, T data) {
+gb_internal OdinDocArray<T> odin_write_item_as_slice(OdinDocWriter *w, T data) {
 	return odin_write_slice(w, &data, 1);
 }
 
 
-OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
+gb_internal OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
 	OdinDocFileIndex file_index = 0;
 	if (pos.file_id != 0) {
 		AstFile *file = global_files[pos.file_id];
@@ -273,7 +272,7 @@ OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
 	return doc_pos;
 }
 
-bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
+gb_internal bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
 	if (g == nullptr) {
 		return false;
 	}
@@ -361,7 +360,7 @@ bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
 	return false;
 }
 
-OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) {
+gb_internal OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) {
 	if (pkg == nullptr) {
 		return {};
 	}
@@ -378,7 +377,7 @@ OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) {
 	return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
 }
 
-OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) {
+gb_internal OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) {
 	if (g == nullptr) {
 		return {};
 	}
@@ -389,7 +388,7 @@ OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) {
 	return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
 }
 
-OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) {
+gb_internal OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) {
 	if (expr == nullptr) {
 		return {};
 	}
@@ -402,7 +401,7 @@ OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) {
 	return odin_doc_write_string(w, make_string(cast(u8 *)s, gb_string_length(s)));
 }
 
-OdinDocArray<OdinDocAttribute> odin_doc_attributes(OdinDocWriter *w, Array<Ast *> const &attributes) {
+gb_internal OdinDocArray<OdinDocAttribute> odin_doc_attributes(OdinDocWriter *w, Array<Ast *> const &attributes) {
 	isize count = 0;
 	for_array(i, attributes) {
 		Ast *attr = attributes[i];
@@ -448,7 +447,7 @@ OdinDocArray<OdinDocAttribute> odin_doc_attributes(OdinDocWriter *w, Array<Ast *
 	return odin_write_slice(w, attribs.data, attribs.count);
 }
 
-OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w, Slice<Ast *> const &where_clauses) {
+gb_internal OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w, Slice<Ast *> const &where_clauses) {
 	if (where_clauses.count == 0) {
 		return {};
 	}
@@ -462,17 +461,17 @@ OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w, Slice<Ast *
 	return odin_write_slice(w, clauses.data, clauses.count);
 }
 
-OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type) {
+gb_internal OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type) {
 	OdinDocTypeIndex index = odin_doc_type(w, type);
 	return odin_write_item_as_slice(w, index);
 }
 
-OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) {
+gb_internal OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) {
 	OdinDocEntityIndex index = odin_doc_add_entity(w, e);
 	return odin_write_item_as_slice(w, index);
 }
 
-OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
+gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 	if (type == nullptr) {
 		return 0;
 	}
@@ -480,11 +479,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 	if (found) {
 		return *found;
 	}
-	for_array(i, w->type_cache.entries) {
+	for (auto const &entry : w->type_cache) {
 		// NOTE(bill): THIS IS SLOW
-		Type *other = w->type_cache.entries[i].key;
+		Type *other = entry.key;
 		if (are_types_identical_unique_tuples(type, other)) {
-			OdinDocTypeIndex index = w->type_cache.entries[i].value;
+			OdinDocTypeIndex index = entry.value;
 			map_set(&w->type_cache, type, index);
 			return index;
 		}
@@ -750,7 +749,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 	}
 	return type_index;
 }
-OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
+gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 	if (e == nullptr) {
 		return 0;
 	}
@@ -860,7 +859,6 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 	if (e->flags & EntityFlag_Param) {
 		if (e->flags & EntityFlag_Using)      { flags |= OdinDocEntityFlag_Param_Using;    }
 		if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const;    }
-		if (e->flags & EntityFlag_AutoCast)   { flags |= OdinDocEntityFlag_Param_AutoCast; }
 		if (e->flags & EntityFlag_Ellipsis)   { flags |= OdinDocEntityFlag_Param_Ellipsis; }
 		if (e->flags & EntityFlag_NoAlias)    { flags |= OdinDocEntityFlag_Param_NoAlias;  }
 		if (e->flags & EntityFlag_AnyInt)     { flags |= OdinDocEntityFlag_Param_AnyInt;   }
@@ -911,26 +909,24 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 	return doc_entity_index;
 }
 
-void odin_doc_update_entities(OdinDocWriter *w) {
+gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
 	{
 		// NOTE(bill): Double pass, just in case entities are created on odin_doc_type
-		auto entities = array_make<Entity *>(heap_allocator(), w->entity_cache.entries.count);
+		auto entities = array_make<Entity *>(heap_allocator(), 0, w->entity_cache.count);
 		defer (array_free(&entities));
 
-		for_array(i, w->entity_cache.entries) {
-			Entity *e = w->entity_cache.entries[i].key;
-			entities[i] = e;
+		for (auto const &entry : w->entity_cache) {
+			array_add(&entities, entry.key);
 		}
-		for_array(i, entities) {
-			Entity *e = entities[i];
+		for (Entity *e : entities) {
 			OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
 			gb_unused(type_index);
 		}
 	}
 
-	for_array(i, w->entity_cache.entries) {
-		Entity *e = w->entity_cache.entries[i].key;
-		OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
+	for (auto const &entry : w->entity_cache) {
+		Entity *e = entry.key;
+		OdinDocEntityIndex entity_index = entry.value;
 		OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
 
 		OdinDocEntityIndex foreign_library = 0;
@@ -948,8 +944,8 @@ void odin_doc_update_entities(OdinDocWriter *w) {
 				auto pges = array_make<OdinDocEntityIndex>(heap_allocator(), 0, e->ProcGroup.entities.count);
 				defer (array_free(&pges));
 
-				for_array(j, e->ProcGroup.entities) {
-					OdinDocEntityIndex index = odin_doc_add_entity(w, e->ProcGroup.entities[j]);
+				for (Entity *entity : e->ProcGroup.entities) {
+					OdinDocEntityIndex index = odin_doc_add_entity(w, entity);
 					array_add(&pges, index);
 				}
 				grouped_entities = odin_write_slice(w, pges.data, pges.count);
@@ -968,7 +964,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
 
 
 
-OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
+gb_internal OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
 	if (pkg->scope == nullptr) {
 		return {};
 	}
@@ -976,12 +972,12 @@ OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPa
 		return {};
 	}
 
-	auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
+	auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.count);
 	defer (array_free(&entries));
 
-	for_array(i, pkg->scope->elements.entries) {
-		String name = pkg->scope->elements.entries[i].key.string;
-		Entity *e = pkg->scope->elements.entries[i].value;
+	for (auto const &element : pkg->scope->elements) {
+		String name = element.key;
+		Entity *e = element.value;
 		switch (e->kind) {
 		case Entity_Invalid:
 		case Entity_Nil:
@@ -1018,11 +1014,11 @@ OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPa
 }
 
 
-void odin_doc_write_docs(OdinDocWriter *w) {
-	auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.entries.count);
+gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
+	auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.count);
 	defer (array_free(&pkgs));
-	for_array(i, w->info->packages.entries) {
-		AstPackage *pkg = w->info->packages.entries[i].value;
+	for (auto const &entry : w->info->packages) {
+		AstPackage *pkg = entry.value;
 		if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
 			array_add(&pkgs, pkg);
 		} else {
@@ -1093,7 +1089,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
 }
 
 
-void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
+gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
 	gbFile f = {};
 	gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
 	if (err != gbFileError_None) {
@@ -1108,7 +1104,7 @@ void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
 	}
 }
 
-void odin_doc_write(CheckerInfo *info, char const *filename) {
+gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
 	OdinDocWriter w_ = {};
 	OdinDocWriter *w = &w_;
 	defer (odin_doc_writer_destroy(w));

+ 30 - 36
src/entity.cpp

@@ -26,7 +26,7 @@ enum EntityKind {
 	Entity_Count,
 };
 
-String const entity_strings[] = {
+gb_global String const entity_strings[] = {
 #define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
 	ENTITY_KINDS
 #undef ENTITY_KIND
@@ -61,7 +61,7 @@ enum EntityFlag : u64 {
 	EntityFlag_ProcBodyChecked = 1ull<<21,
 
 	EntityFlag_CVarArg       = 1ull<<22,
-	EntityFlag_AutoCast      = 1ull<<23,
+
 	EntityFlag_AnyInt        = 1ull<<24,
 
 	EntityFlag_Disabled      = 1ull<<25,
@@ -116,7 +116,7 @@ struct ParameterValue {
 	};
 };
 
-bool has_parameter_value(ParameterValue const &param_value) {
+gb_internal gb_inline bool has_parameter_value(ParameterValue const &param_value) {
 	if (param_value.kind != ParameterValue_Invalid) {
 		return true;
 	}
@@ -130,7 +130,7 @@ enum EntityConstantFlags : u32 {
 	EntityConstantFlag_ImplicitEnumValue = 1<<0,
 };
 
-enum ProcedureOptimizationMode : u32 {
+enum ProcedureOptimizationMode : u8 {
 	ProcedureOptimizationMode_Default,
 	ProcedureOptimizationMode_None,
 	ProcedureOptimizationMode_Minimal,
@@ -151,10 +151,9 @@ struct TypeNameObjCMetadata {
 	Array<TypeNameObjCMetadataEntry> value_entries;
 };
 
-TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
+gb_internal TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
 	TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
 	md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
-	mutex_init(md->mutex);
 	array_init(&md->type_entries,  heap_allocator());
 	array_init(&md->value_entries, heap_allocator());
 	return md;
@@ -234,6 +233,9 @@ struct Entity {
 			String  link_name;
 			String  link_prefix;
 			DeferredProcedure deferred_procedure;
+
+			struct GenProcsData *gen_procs;
+			BlockingMutex gen_procs_mutex;
 			ProcedureOptimizationMode optimization_mode;
 			bool    is_foreign                 : 1;
 			bool    is_export                  : 1;
@@ -266,7 +268,7 @@ struct Entity {
 	};
 };
 
-bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = false) {
+gb_internal bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = false) {
 	switch (kind) {
 	case Entity_Builtin:
 		return allow_builtin;
@@ -278,7 +280,7 @@ bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = false) {
 	return true;
 }
 
-bool is_entity_exported(Entity *e, bool allow_builtin = false) {
+gb_internal bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 	// TODO(bill): Determine the actual exportation rules for imports of entities
 	GB_ASSERT(e != nullptr);
 	if (!is_entity_kind_exported(e->kind, allow_builtin)) {
@@ -300,7 +302,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 	return true;
 }
 
-bool entity_has_deferred_procedure(Entity *e) {
+gb_internal bool entity_has_deferred_procedure(Entity *e) {
 	GB_ASSERT(e != nullptr);
 	if (e->kind == Entity_Procedure) {
 		return e->Procedure.deferred_procedure.entity != nullptr;
@@ -311,7 +313,7 @@ bool entity_has_deferred_procedure(Entity *e) {
 
 gb_global std::atomic<u64> global_entity_id;
 
-Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
+gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
 	gbAllocator a = permanent_allocator();
 	Entity *entity = gb_alloc_item(a, Entity);
 	entity->kind   = kind;
@@ -323,13 +325,13 @@ Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) {
 	return entity;
 }
 
-Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, EntityState state = EntityState_Unresolved) {
+gb_internal Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, EntityState state = EntityState_Unresolved) {
 	Entity *entity = alloc_entity(Entity_Variable, scope, token, type);
 	entity->state = state;
 	return entity;
 }
 
-Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast *using_expr) {
+gb_internal Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast *using_expr) {
 	GB_ASSERT(parent != nullptr);
 	token.pos = parent->token.pos;
 	Entity *entity = alloc_entity(Entity_Variable, parent->scope, token, type);
@@ -343,19 +345,19 @@ Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast
 }
 
 
-Entity *alloc_entity_constant(Scope *scope, Token token, Type *type, ExactValue value) {
+gb_internal Entity *alloc_entity_constant(Scope *scope, Token token, Type *type, ExactValue value) {
 	Entity *entity = alloc_entity(Entity_Constant, scope, token, type);
 	entity->Constant.value = value;
 	return entity;
 }
 
-Entity *alloc_entity_type_name(Scope *scope, Token token, Type *type, EntityState state = EntityState_Unresolved) {
+gb_internal Entity *alloc_entity_type_name(Scope *scope, Token token, Type *type, EntityState state = EntityState_Unresolved) {
 	Entity *entity = alloc_entity(Entity_TypeName, scope, token, type);
 	entity->state = state;
 	return entity;
 }
 
-Entity *alloc_entity_param(Scope *scope, Token token, Type *type, bool is_using, bool is_value) {
+gb_internal Entity *alloc_entity_param(Scope *scope, Token token, Type *type, bool is_using, bool is_value) {
 	Entity *entity = alloc_entity_variable(scope, token, type);
 	entity->flags |= EntityFlag_Used;
 	entity->flags |= EntityFlag_Param;
@@ -366,7 +368,7 @@ Entity *alloc_entity_param(Scope *scope, Token token, Type *type, bool is_using,
 }
 
 
-Entity *alloc_entity_const_param(Scope *scope, Token token, Type *type, ExactValue value, bool poly_const) {
+gb_internal Entity *alloc_entity_const_param(Scope *scope, Token token, Type *type, ExactValue value, bool poly_const) {
 	Entity *entity = alloc_entity_constant(scope, token, type, value);
 	entity->flags |= EntityFlag_Used;
 	if (poly_const) entity->flags |= EntityFlag_PolyConst;
@@ -375,7 +377,7 @@ Entity *alloc_entity_const_param(Scope *scope, Token token, Type *type, ExactVal
 }
 
 
-Entity *alloc_entity_field(Scope *scope, Token token, Type *type, bool is_using, i32 field_index, EntityState state = EntityState_Unresolved) {
+gb_internal Entity *alloc_entity_field(Scope *scope, Token token, Type *type, bool is_using, i32 field_index, EntityState state = EntityState_Unresolved) {
 	Entity *entity = alloc_entity_variable(scope, token, type);
 	entity->Variable.field_index = field_index;
 	if (is_using) entity->flags |= EntityFlag_Using;
@@ -384,7 +386,7 @@ Entity *alloc_entity_field(Scope *scope, Token token, Type *type, bool is_using,
 	return entity;
 }
 
-Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *type, i32 field_index) {
+gb_internal Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *type, i32 field_index) {
 	Entity *entity = alloc_entity_variable(scope, token, type);
 	entity->Variable.field_index = field_index;
 	entity->flags |= EntityFlag_Field;
@@ -393,26 +395,18 @@ Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *type, i32 field
 	return entity;
 }
 
-Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags) {
+gb_internal Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags) {
 	Entity *entity = alloc_entity(Entity_Procedure, scope, token, signature_type);
 	entity->Procedure.tags = tags;
 	return entity;
 }
 
-Entity *alloc_entity_proc_group(Scope *scope, Token token, Type *type) {
+gb_internal Entity *alloc_entity_proc_group(Scope *scope, Token token, Type *type) {
 	Entity *entity = alloc_entity(Entity_ProcGroup, scope, token, type);
 	return entity;
 }
 
-
-Entity *alloc_entity_builtin(Scope *scope, Token token, Type *type, i32 id) {
-	Entity *entity = alloc_entity(Entity_Builtin, scope, token, type);
-	entity->Builtin.id = id;
-	entity->state = EntityState_Resolved;
-	return entity;
-}
-
-Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
+gb_internal Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
                                  String path, String name, Scope *import_scope) {
 	Entity *entity = alloc_entity(Entity_ImportName, scope, token, type);
 	entity->ImportName.path = path;
@@ -422,7 +416,7 @@ Entity *alloc_entity_import_name(Scope *scope, Token token, Type *type,
 	return entity;
 }
 
-Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
+gb_internal Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
                                   Slice<String> paths, String name) {
 	Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
 	entity->LibraryName.paths = paths;
@@ -435,12 +429,12 @@ Entity *alloc_entity_library_name(Scope *scope, Token token, Type *type,
 
 
 
-Entity *alloc_entity_nil(String name, Type *type) {
+gb_internal Entity *alloc_entity_nil(String name, Type *type) {
 	Entity *entity = alloc_entity(Entity_Nil, nullptr, make_token_ident(name), type);
 	return entity;
 }
 
-Entity *alloc_entity_label(Scope *scope, Token token, Type *type, Ast *node, Ast *parent) {
+gb_internal Entity *alloc_entity_label(Scope *scope, Token token, Type *type, Ast *node, Ast *parent) {
 	Entity *entity = alloc_entity(Entity_Label, scope, token, type);
 	entity->Label.node = node;
 	entity->Label.parent = parent;
@@ -448,15 +442,15 @@ Entity *alloc_entity_label(Scope *scope, Token token, Type *type, Ast *node, Ast
 	return entity;
 }
 
-Entity *alloc_entity_dummy_variable(Scope *scope, Token token) {
+gb_internal Entity *alloc_entity_dummy_variable(Scope *scope, Token token) {
 	token.string = str_lit("_");
 	return alloc_entity_variable(scope, token, nullptr);
 }
 
 
-Entity *entity_from_expr(Ast *expr);
+gb_internal Entity *entity_from_expr(Ast *expr);
 
-Entity *strip_entity_wrapping(Entity *e) {
+gb_internal Entity *strip_entity_wrapping(Entity *e) {
 	if (e == nullptr) {
 		return nullptr;
 	}
@@ -469,7 +463,7 @@ Entity *strip_entity_wrapping(Entity *e) {
 	return e;
 }
 
-Entity *strip_entity_wrapping(Ast *expr) {
+gb_internal Entity *strip_entity_wrapping(Ast *expr) {
 	Entity *e = entity_from_expr(expr);
 	return strip_entity_wrapping(e);
 }

+ 31 - 35
src/error.cpp

@@ -17,15 +17,11 @@ gb_global ErrorCollector global_error_collector;
 #define MAX_ERROR_COLLECTOR_COUNT (36)
 
 
-bool any_errors(void) {
+gb_internal bool any_errors(void) {
 	return global_error_collector.count.load() != 0;
 }
 
-void init_global_error_collector(void) {
-	mutex_init(&global_error_collector.mutex);
-	mutex_init(&global_error_collector.block_mutex);
-	mutex_init(&global_error_collector.error_out_mutex);
-	mutex_init(&global_error_collector.string_mutex);
+gb_internal void init_global_error_collector(void) {
 	array_init(&global_error_collector.errors, heap_allocator());
 	array_init(&global_error_collector.error_buffer, heap_allocator());
 	array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
@@ -35,9 +31,9 @@ void init_global_error_collector(void) {
 
 // temporary
 // defined in build_settings.cpp
-char *token_pos_to_string(TokenPos const &pos);
+gb_internal char *token_pos_to_string(TokenPos const &pos);
 
-bool set_file_path_string(i32 index, String const &path) {
+gb_internal bool set_file_path_string(i32 index, String const &path) {
 	bool ok = false;
 	GB_ASSERT(index >= 0);
 	mutex_lock(&global_error_collector.string_mutex);
@@ -55,7 +51,7 @@ bool set_file_path_string(i32 index, String const &path) {
 	return ok;
 }
 
-bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
+gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
 	bool ok = false;
 	GB_ASSERT(index >= 0);
 	mutex_lock(&global_error_collector.string_mutex);
@@ -73,7 +69,7 @@ bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
 	return ok;
 }
 
-String get_file_path_string(i32 index) {
+gb_internal String get_file_path_string(i32 index) {
 	GB_ASSERT(index >= 0);
 	mutex_lock(&global_error_collector.string_mutex);
 
@@ -86,7 +82,7 @@ String get_file_path_string(i32 index) {
 	return path;
 }
 
-AstFile *thread_safe_get_ast_file_from_id(i32 index) {
+gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
 	GB_ASSERT(index >= 0);
 	mutex_lock(&global_error_collector.string_mutex);
 
@@ -101,12 +97,12 @@ AstFile *thread_safe_get_ast_file_from_id(i32 index) {
 
 
 
-void begin_error_block(void) {
+gb_internal void begin_error_block(void) {
 	mutex_lock(&global_error_collector.block_mutex);
 	global_error_collector.in_block.store(true);
 }
 
-void end_error_block(void) {
+gb_internal void end_error_block(void) {
 	if (global_error_collector.error_buffer.count > 0) {
 		isize n = global_error_collector.error_buffer.count;
 		u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
@@ -127,7 +123,7 @@ void end_error_block(void) {
 #define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
 typedef ERROR_OUT_PROC(ErrorOutProc);
 
-ERROR_OUT_PROC(default_error_out_va) {
+gb_internal ERROR_OUT_PROC(default_error_out_va) {
 	gbFile *f = gb_file_get_standard(gbFileStandard_Error);
 
 	char buf[4096] = {};
@@ -154,15 +150,15 @@ ERROR_OUT_PROC(default_error_out_va) {
 }
 
 
-ErrorOutProc *error_out_va = default_error_out_va;
+gb_global ErrorOutProc *error_out_va = default_error_out_va;
 
 // NOTE: defined in build_settings.cpp
-bool global_warnings_as_errors(void);
-bool global_ignore_warnings(void);
-bool show_error_line(void);
-gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
+gb_internal bool global_warnings_as_errors(void);
+gb_internal bool global_ignore_warnings(void);
+gb_internal bool show_error_line(void);
+gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
 
-void error_out(char const *fmt, ...) {
+gb_internal void error_out(char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_out_va(fmt, va);
@@ -170,7 +166,7 @@ void error_out(char const *fmt, ...) {
 }
 
 
-bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
 	if (!show_error_line()) {
 		return false;
 	}
@@ -237,7 +233,7 @@ bool show_error_on_line(TokenPos const &pos, TokenPos end) {
 	return false;
 }
 
-void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	global_error_collector.count.fetch_add(1);
 
 	mutex_lock(&global_error_collector.mutex);
@@ -257,7 +253,7 @@ void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	}
 }
 
-void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	if (global_warnings_as_errors()) {
 		error_va(pos, end, fmt, va);
 		return;
@@ -280,11 +276,11 @@ void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va)
 }
 
 
-void error_line_va(char const *fmt, va_list va) {
+gb_internal void error_line_va(char const *fmt, va_list va) {
 	error_out_va(fmt, va);
 }
 
-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) {
 	mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -303,7 +299,7 @@ void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
 }
 
 
-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) {
 	mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -323,7 +319,7 @@ void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list
 	}
 }
 
-void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
 	if (global_warnings_as_errors()) {
 		syntax_error_va(pos, end, fmt, va);
 		return;
@@ -347,21 +343,21 @@ void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_li
 
 
 
-void warning(Token const &token, char const *fmt, ...) {
+gb_internal void warning(Token const &token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	warning_va(token.pos, {}, fmt, va);
 	va_end(va);
 }
 
-void error(Token const &token, char const *fmt, ...) {
+gb_internal void error(Token const &token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_va(token.pos, {}, fmt, va);
 	va_end(va);
 }
 
-void error(TokenPos pos, char const *fmt, ...) {
+gb_internal void error(TokenPos pos, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	Token token = {};
@@ -370,7 +366,7 @@ void error(TokenPos pos, char const *fmt, ...) {
 	va_end(va);
 }
 
-void error_line(char const *fmt, ...) {
+gb_internal void error_line(char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_line_va(fmt, va);
@@ -378,21 +374,21 @@ void error_line(char const *fmt, ...) {
 }
 
 
-void syntax_error(Token const &token, char const *fmt, ...) {
+gb_internal void syntax_error(Token const &token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_error_va(token.pos, {}, fmt, va);
 	va_end(va);
 }
 
-void syntax_error(TokenPos pos, char const *fmt, ...) {
+gb_internal void syntax_error(TokenPos pos, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_error_va(pos, {}, fmt, va);
 	va_end(va);
 }
 
-void syntax_warning(Token const &token, char const *fmt, ...) {
+gb_internal void syntax_warning(Token const &token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_warning_va(token.pos, {}, fmt, va);
@@ -400,7 +396,7 @@ void syntax_warning(Token const &token, char const *fmt, ...) {
 }
 
 
-void compiler_error(char const *fmt, ...) {
+gb_internal void compiler_error(char const *fmt, ...) {
 	va_list va;
 
 	va_start(va, fmt);

+ 85 - 95
src/exact_value.cpp

@@ -6,7 +6,7 @@ struct Ast;
 struct HashKey;
 struct Type;
 struct Entity;
-bool are_types_identical(Type *x, Type *y);
+gb_internal bool are_types_identical(Type *x, Type *y);
 
 struct Complex128 {
 	f64 real, imag;
@@ -15,16 +15,6 @@ struct Quaternion256 {
 	f64 imag, jmag, kmag, real;
 };
 
-Quaternion256 quaternion256_inverse(Quaternion256 x) {
-	f64 invmag2 = 1.0 / (x.real*x.real + x.imag*x.imag + x.jmag*x.jmag + x.kmag*x.kmag);
-	x.real = +x.real * invmag2;
-	x.imag = -x.imag * invmag2;
-	x.jmag = -x.jmag * invmag2;
-	x.kmag = -x.kmag * invmag2;
-	return x;
-}
-
-
 enum ExactValueKind {
 	ExactValue_Invalid    = 0,
 
@@ -60,7 +50,7 @@ struct ExactValue {
 
 gb_global ExactValue const empty_exact_value = {};
 
-uintptr hash_exact_value(ExactValue v) {
+gb_internal uintptr hash_exact_value(ExactValue v) {
 	mutex_lock(&hash_exact_value_mutex);
 	defer (mutex_unlock(&hash_exact_value_mutex));
 	
@@ -70,7 +60,7 @@ uintptr hash_exact_value(ExactValue v) {
 	case ExactValue_Bool:
 		return gb_fnv32a(&v.value_bool, gb_size_of(v.value_bool));
 	case ExactValue_String:
-		return ptr_map_hash_key(string_intern(v.value_string));
+		return gb_fnv32a(v.value_string.text, v.value_string.len);
 	case ExactValue_Integer:
 		{
 			u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
@@ -97,44 +87,44 @@ uintptr hash_exact_value(ExactValue v) {
 }
 
 
-ExactValue exact_value_compound(Ast *node) {
+gb_internal ExactValue exact_value_compound(Ast *node) {
 	ExactValue result = {ExactValue_Compound};
 	result.value_compound = node;
 	return result;
 }
 
-ExactValue exact_value_bool(bool b) {
+gb_internal ExactValue exact_value_bool(bool b) {
 	ExactValue result = {ExactValue_Bool};
 	result.value_bool = (b != 0);
 	return result;
 }
 
-ExactValue exact_value_string(String string) {
+gb_internal ExactValue exact_value_string(String string) {
 	// TODO(bill): Allow for numbers with underscores in them
 	ExactValue result = {ExactValue_String};
 	result.value_string = string;
 	return result;
 }
 
-ExactValue exact_value_i64(i64 i) {
+gb_internal ExactValue exact_value_i64(i64 i) {
 	ExactValue result = {ExactValue_Integer};
 	big_int_from_i64(&result.value_integer, i);
 	return result;
 }
 
-ExactValue exact_value_u64(u64 i) {
+gb_internal ExactValue exact_value_u64(u64 i) {
 	ExactValue result = {ExactValue_Integer};
 	big_int_from_u64(&result.value_integer, i);
 	return result;
 }
 
-ExactValue exact_value_float(f64 f) {
+gb_internal ExactValue exact_value_float(f64 f) {
 	ExactValue result = {ExactValue_Float};
 	result.value_float = f;
 	return result;
 }
 
-ExactValue exact_value_complex(f64 real, f64 imag) {
+gb_internal ExactValue exact_value_complex(f64 real, f64 imag) {
 	ExactValue result = {ExactValue_Complex};
 	result.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
 	result.value_complex->real = real;
@@ -142,7 +132,7 @@ ExactValue exact_value_complex(f64 real, f64 imag) {
 	return result;
 }
 
-ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
+gb_internal ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
 	ExactValue result = {ExactValue_Quaternion};
 	result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256);
 	result.value_quaternion->real = real;
@@ -152,27 +142,27 @@ ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
 	return result;
 }
 
-ExactValue exact_value_pointer(i64 ptr) {
+gb_internal ExactValue exact_value_pointer(i64 ptr) {
 	ExactValue result = {ExactValue_Pointer};
 	result.value_pointer = ptr;
 	return result;
 }
 
-ExactValue exact_value_procedure(Ast *node) {
+gb_internal ExactValue exact_value_procedure(Ast *node) {
 	ExactValue result = {ExactValue_Procedure};
 	result.value_procedure = node;
 	return result;
 }
 
 
-ExactValue exact_value_typeid(Type *type) {
+gb_internal ExactValue exact_value_typeid(Type *type) {
 	ExactValue result = {ExactValue_Typeid};
 	result.value_typeid = type;
 	return result;
 }
 
 
-ExactValue exact_value_integer_from_string(String const &string) {
+gb_internal ExactValue exact_value_integer_from_string(String const &string) {
 	ExactValue result = {ExactValue_Integer};
 	bool success;
 	big_int_from_string(&result.value_integer, string, &success);
@@ -184,7 +174,7 @@ ExactValue exact_value_integer_from_string(String const &string) {
 
 
 
-f64 float_from_string(String string) {
+gb_internal f64 float_from_string(String string) {
 	isize i = 0;
 	u8 *str = string.text;
 	isize len = string.len;
@@ -262,7 +252,7 @@ f64 float_from_string(String string) {
 	return sign * (frac ? (value / scale) : (value * scale));
 }
 
-ExactValue exact_value_float_from_string(String string) {
+gb_internal ExactValue exact_value_float_from_string(String string) {
 	if (string.len > 2 && string[0] == '0' && string[1] == 'h') {
 
 		isize digit_count = 0;
@@ -298,7 +288,7 @@ ExactValue exact_value_float_from_string(String string) {
 }
 
 
-ExactValue exact_value_from_basic_literal(TokenKind kind, String const &string) {
+gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String const &string) {
 	switch (kind) {
 	case Token_String:  return exact_value_string(string);
 	case Token_Integer: return exact_value_integer_from_string(string);
@@ -330,7 +320,7 @@ ExactValue exact_value_from_basic_literal(TokenKind kind, String const &string)
 	return result;
 }
 
-ExactValue exact_value_to_integer(ExactValue v) {
+gb_internal ExactValue exact_value_to_integer(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Bool: {
 		i64 i = 0;
@@ -357,7 +347,7 @@ ExactValue exact_value_to_integer(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_to_float(ExactValue v) {
+gb_internal ExactValue exact_value_to_float(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 		return exact_value_float(big_int_to_f64(&v.value_integer));
@@ -368,7 +358,7 @@ ExactValue exact_value_to_float(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_to_complex(ExactValue v) {
+gb_internal ExactValue exact_value_to_complex(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 		return exact_value_complex(big_int_to_f64(&v.value_integer), 0);
@@ -383,7 +373,7 @@ ExactValue exact_value_to_complex(ExactValue v) {
 	v.value_complex = gb_alloc_item(permanent_allocator(), Complex128);
 	return r;
 }
-ExactValue exact_value_to_quaternion(ExactValue v) {
+gb_internal ExactValue exact_value_to_quaternion(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 		return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0);
@@ -399,7 +389,7 @@ ExactValue exact_value_to_quaternion(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_real(ExactValue v) {
+gb_internal ExactValue exact_value_real(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 	case ExactValue_Float:
@@ -413,7 +403,7 @@ ExactValue exact_value_real(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_imag(ExactValue v) {
+gb_internal ExactValue exact_value_imag(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 	case ExactValue_Float:
@@ -427,7 +417,7 @@ ExactValue exact_value_imag(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_jmag(ExactValue v) {
+gb_internal ExactValue exact_value_jmag(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 	case ExactValue_Float:
@@ -440,7 +430,7 @@ ExactValue exact_value_jmag(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_kmag(ExactValue v) {
+gb_internal ExactValue exact_value_kmag(ExactValue v) {
 	switch (v.kind) {
 	case ExactValue_Integer:
 	case ExactValue_Float:
@@ -453,60 +443,60 @@ ExactValue exact_value_kmag(ExactValue v) {
 	return r;
 }
 
-ExactValue exact_value_make_imag(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Integer:
-		return exact_value_complex(0, exact_value_to_float(v).value_float);
-	case ExactValue_Float:
-		return exact_value_complex(0, v.value_float);
-	default:
-		GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'");
-	}
-	ExactValue r = {ExactValue_Invalid};
-	return r;
-}
-
-ExactValue exact_value_make_jmag(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Integer:
-		return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0);
-	case ExactValue_Float:
-		return exact_value_quaternion(0, 0, v.value_float, 0);
-	default:
-		GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'");
-	}
-	ExactValue r = {ExactValue_Invalid};
-	return r;
-}
-
-ExactValue exact_value_make_kmag(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Integer:
-		return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float);
-	case ExactValue_Float:
-		return exact_value_quaternion(0, 0, 0, v.value_float);
-	default:
-		GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'");
-	}
-	ExactValue r = {ExactValue_Invalid};
-	return r;
-}
-
-i64 exact_value_to_i64(ExactValue v) {
+// gb_internal ExactValue exact_value_make_imag(ExactValue v) {
+// 	switch (v.kind) {
+// 	case ExactValue_Integer:
+// 		return exact_value_complex(0, exact_value_to_float(v).value_float);
+// 	case ExactValue_Float:
+// 		return exact_value_complex(0, v.value_float);
+// 	default:
+// 		GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'");
+// 	}
+// 	ExactValue r = {ExactValue_Invalid};
+// 	return r;
+// }
+
+// gb_internal ExactValue exact_value_make_jmag(ExactValue v) {
+// 	switch (v.kind) {
+// 	case ExactValue_Integer:
+// 		return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0);
+// 	case ExactValue_Float:
+// 		return exact_value_quaternion(0, 0, v.value_float, 0);
+// 	default:
+// 		GB_PANIC("Expected an integer or float type for 'exact_value_make_jmag'");
+// 	}
+// 	ExactValue r = {ExactValue_Invalid};
+// 	return r;
+// }
+
+// gb_internal ExactValue exact_value_make_kmag(ExactValue v) {
+// 	switch (v.kind) {
+// 	case ExactValue_Integer:
+// 		return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float);
+// 	case ExactValue_Float:
+// 		return exact_value_quaternion(0, 0, 0, v.value_float);
+// 	default:
+// 		GB_PANIC("Expected an integer or float type for 'exact_value_make_kmag'");
+// 	}
+// 	ExactValue r = {ExactValue_Invalid};
+// 	return r;
+// }
+
+gb_internal i64 exact_value_to_i64(ExactValue v) {
 	v = exact_value_to_integer(v);
 	if (v.kind == ExactValue_Integer) {
 		return big_int_to_i64(&v.value_integer);
 	}
 	return 0;
 }
-u64 exact_value_to_u64(ExactValue v) {
+gb_internal u64 exact_value_to_u64(ExactValue v) {
 	v = exact_value_to_integer(v);
 	if (v.kind == ExactValue_Integer) {
 		return big_int_to_u64(&v.value_integer);
 	}
 	return 0;
 }
-f64 exact_value_to_f64(ExactValue v) {
+gb_internal f64 exact_value_to_f64(ExactValue v) {
 	v = exact_value_to_float(v);
 	if (v.kind == ExactValue_Float) {
 		return v.value_float;
@@ -519,7 +509,7 @@ f64 exact_value_to_f64(ExactValue v) {
 
 
 
-ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, bool is_unsigned) {
+gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, bool is_unsigned) {
 	switch (op) {
 	case Token_Add:	{
 		switch (v.kind) {
@@ -596,7 +586,7 @@ failure:
 }
 
 // NOTE(bill): Make sure things are evaluated in correct order
-i32 exact_value_order(ExactValue const &v) {
+gb_internal i32 exact_value_order(ExactValue const &v) {
 	switch (v.kind) {
 	case ExactValue_Invalid:
 	case ExactValue_Compound:
@@ -623,7 +613,7 @@ i32 exact_value_order(ExactValue const &v) {
 	}
 }
 
-void match_exact_values(ExactValue *x, ExactValue *y) {
+gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
 	if (exact_value_order(*y) < exact_value_order(*x)) {
 		match_exact_values(y, x);
 		return;
@@ -687,7 +677,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 }
 
 // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
-ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
+gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
 	match_exact_values(&x, &y);
 
 	switch (x.kind) {
@@ -846,32 +836,32 @@ error:; // NOTE(bill): MSVC accepts this??? apparently you cannot declare variab
 	return empty_exact_value;
 }
 
-gb_inline ExactValue exact_value_add(ExactValue const &x, ExactValue const &y) {
+gb_internal gb_inline ExactValue exact_value_add(ExactValue const &x, ExactValue const &y) {
 	return exact_binary_operator_value(Token_Add, x, y);
 }
-gb_inline ExactValue exact_value_sub(ExactValue const &x, ExactValue const &y) {
+gb_internal gb_inline ExactValue exact_value_sub(ExactValue const &x, ExactValue const &y) {
 	return exact_binary_operator_value(Token_Sub, x, y);
 }
-gb_inline ExactValue exact_value_mul(ExactValue const &x, ExactValue const &y) {
+gb_internal gb_inline ExactValue exact_value_mul(ExactValue const &x, ExactValue const &y) {
 	return exact_binary_operator_value(Token_Mul, x, y);
 }
-gb_inline ExactValue exact_value_quo(ExactValue const &x, ExactValue const &y) {
+gb_internal gb_inline ExactValue exact_value_quo(ExactValue const &x, ExactValue const &y) {
 	return exact_binary_operator_value(Token_Quo, x, y);
 }
-gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactValue const &y) {
+gb_internal gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactValue const &y) {
 	return exact_binary_operator_value(op, x, y);
 }
 
-gb_inline ExactValue exact_value_increment_one(ExactValue const &x) {
+gb_internal gb_inline ExactValue exact_value_increment_one(ExactValue const &x) {
 	return exact_binary_operator_value(Token_Add, x, exact_value_i64(1));
 }
 
 
-i32 cmp_f64(f64 a, f64 b) {
+gb_internal gb_inline i32 cmp_f64(f64 a, f64 b) {
 	return (a > b) - (a < b);
 }
 
-bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
+gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 	match_exact_values(&x, &y);
 
 	switch (x.kind) {
@@ -969,12 +959,12 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 	return false;
 }
 
-Entity *strip_entity_wrapping(Ast *expr);
-Entity *strip_entity_wrapping(Entity *e);
+gb_internal Entity *strip_entity_wrapping(Ast *expr);
+gb_internal Entity *strip_entity_wrapping(Entity *e);
 
-gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
+gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
 
-gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
+gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) {
 	switch (v.kind) {
 	case ExactValue_Invalid:
 		return str;
@@ -1017,6 +1007,6 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st
 	return str;
 };
 
-gbString exact_value_to_string(ExactValue const &v, isize string_limit=36) {
+gb_internal gbString exact_value_to_string(ExactValue const &v, isize string_limit=36) {
 	return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit);
 }

+ 0 - 2
src/gb/gb.h

@@ -465,8 +465,6 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
 #if !defined(gb_thread_local)
 	#if defined(_MSC_VER) && _MSC_VER >= 1300
 		#define gb_thread_local __declspec(thread)
-	#elif defined(__GNUC__)
-		#define gb_thread_local __thread
 	#else
 		#define gb_thread_local thread_local
 	#endif

+ 220 - 107
src/llvm_abi.cpp

@@ -1,3 +1,5 @@
+#define ALLOW_SPLIT_MULTI_RETURNS true
+
 enum lbArgKind {
 	lbArg_Direct,
 	lbArg_Indirect,
@@ -16,21 +18,21 @@ struct lbArgType {
 };
 
 
-i64 lb_sizeof(LLVMTypeRef type);
-i64 lb_alignof(LLVMTypeRef type);
+gb_internal i64 lb_sizeof(LLVMTypeRef type);
+gb_internal i64 lb_alignof(LLVMTypeRef type);
 
-lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
+gb_internal lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
 	return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, nullptr, 0, false};
 }
-lbArgType lb_arg_type_direct(LLVMTypeRef type) {
+gb_internal lbArgType lb_arg_type_direct(LLVMTypeRef type) {
 	return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
 }
 
-lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
+gb_internal lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
 	return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr, 0, false};
 }
 
-lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) {
+gb_internal lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) {
 	i64 alignment = lb_alignof(type);
 	alignment = gb_max(alignment, 8);
 
@@ -39,7 +41,7 @@ lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) {
 	return lbArgType{lbArg_Indirect, type, nullptr, nullptr, byval_attr, align_attr, alignment, true};
 }
 
-lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
+gb_internal lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
 	return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr, 0, false};
 }
 
@@ -48,21 +50,29 @@ struct lbFunctionType {
 	ProcCallingConvention calling_convention;
 	Array<lbArgType> args;
 	lbArgType        ret;
+
+	LLVMTypeRef      multiple_return_original_type; // nullptr if not used
+	isize            original_arg_count;
 };
 
-i64 llvm_align_formula(i64 off, i64 a) {
+gb_internal gbAllocator lb_function_type_args_allocator(void) {
+	return heap_allocator();
+}
+
+
+gb_internal gb_inline i64 llvm_align_formula(i64 off, i64 a) {
 	return (off + a - 1) / a * a;
 }
 
 
-bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) {
+gb_internal bool lb_is_type_kind(LLVMTypeRef type, LLVMTypeKind kind) {
 	if (type == nullptr) {
 		return false;
 	}
 	return LLVMGetTypeKind(type) == kind;
 }
 
-LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
+gb_internal LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
 	unsigned arg_count = cast(unsigned)ft->args.count;
 	unsigned offset = 0;
 
@@ -100,7 +110,9 @@ LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
 			}
 			args[arg_index++] = arg_type;
 		} else if (arg->kind == lbArg_Indirect) {
-			GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+			if (ft->multiple_return_original_type == nullptr || i < ft->original_arg_count) {
+				GB_ASSERT(!lb_is_type_kind(arg->type, LLVMPointerTypeKind));
+			}
 			args[arg_index++] = LLVMPointerType(arg->type, 0);
 		} else if (arg->kind == lbArg_Ignore) {
 			// ignore
@@ -118,7 +130,7 @@ LLVMTypeRef lb_function_type_to_llvm_raw(lbFunctionType *ft, bool is_var_arg) {
 // }
 
 
-void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) {
+gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCallingConvention calling_convention) {
 	if (ft == nullptr) {
 		return;
 	}
@@ -147,6 +159,13 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
 			LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
 		}
 
+		if (ft->multiple_return_original_type) {
+			if (ft->original_arg_count <= i) {
+				LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
+				LLVMAddAttributeAtIndex(fn, arg_index+1, nonnull_attr);
+			}
+		}
+
 		arg_index++;
 	}
 
@@ -182,7 +201,7 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
 }
 
 
-i64 lb_sizeof(LLVMTypeRef type) {
+gb_internal i64 lb_sizeof(LLVMTypeRef type) {
 	LLVMTypeKind kind = LLVMGetTypeKind(type);
 	switch (kind) {
 	case LLVMVoidTypeKind:
@@ -248,7 +267,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
 	return 0;
 }
 
-i64 lb_alignof(LLVMTypeRef type) {
+gb_internal i64 lb_alignof(LLVMTypeRef type) {
 	LLVMTypeKind kind = LLVMGetTypeKind(type);
 	switch (kind) {
 	case LLVMVoidTypeKind:
@@ -307,25 +326,63 @@ i64 lb_alignof(LLVMTypeRef type) {
 }
 
 
-#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention)
+#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention)
 typedef LB_ABI_INFO(lbAbiInfoType);
 
+#define LB_ABI_COMPUTE_RETURN_TYPE(name) lbArgType name(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple)
+typedef LB_ABI_COMPUTE_RETURN_TYPE(lbAbiComputeReturnType);
+
+
+gb_internal lbArgType lb_abi_modify_return_is_tuple(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, lbAbiComputeReturnType *compute_return_type) {
+	GB_ASSERT(return_type != nullptr);
+	GB_ASSERT(compute_return_type != nullptr);
+
+	lbArgType return_arg = {};
+	if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+		unsigned field_count = LLVMCountStructElementTypes(return_type);
+		if (field_count > 1) {
+			ft->original_arg_count = ft->args.count;
+			ft->multiple_return_original_type = return_type;
+
+			for (unsigned i = 0; i < field_count-1; i++) {
+				LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i);
+				LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0);
+				lbArgType ret_partial = lb_arg_type_direct(field_pointer_type);
+				array_add(&ft->args, ret_partial);
+			}
+
+			// override the return type for the last field
+			LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
+			return_arg = compute_return_type(ft, c, new_return_type, true, false);
+		}
+	}
+	return return_arg;
+}
+
+#define LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO() do {                                                                  \
+	if (return_is_tuple) {                                                                                      \
+		lbArgType new_return_type = lb_abi_modify_return_is_tuple(ft, c, return_type, compute_return_type); \
+		if (new_return_type.type != nullptr) {                                                              \
+			return new_return_type;                                                                     \
+		}                                                                                                   \
+	}                                                                                                           \
+} while (0)
 
 // NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
 namespace lbAbi386 {
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
-		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
 		ft->calling_convention = calling_convention;
 		return ft;
 	}
 
-	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
 		if (!is_return && lb_sizeof(type) > 8) {
 			return lb_arg_type_indirect(type, nullptr);
 		}
@@ -352,8 +409,8 @@ namespace lbAbi386 {
 		return lb_arg_type_direct(type, nullptr, nullptr, attr);
 	}
 
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
-		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 
 		for (unsigned i = 0; i < arg_count; i++) {
 			LLVMTypeRef t = arg_types[i];
@@ -372,7 +429,7 @@ namespace lbAbi386 {
 		return args;
 	}
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
 		if (!return_is_defined) {
 			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
 		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
@@ -383,6 +440,9 @@ namespace lbAbi386 {
 			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
 			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
 			}
+
+			LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
 			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
 			return lb_arg_type_indirect(return_type, attr);
 		}
@@ -391,20 +451,20 @@ namespace lbAbi386 {
 };
 
 namespace lbAbiAmd64Win64 {
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 
-
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
-		ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+		ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
 		ft->calling_convention = calling_convention;
 		return ft;
 	}
 
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
-		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 
 		for (unsigned i = 0; i < arg_count; i++) {
 			LLVMTypeRef t = arg_types[i];
@@ -428,6 +488,26 @@ namespace lbAbiAmd64Win64 {
 		}
 		return args;
 	}
+
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+			i64 sz = lb_sizeof(return_type);
+			switch (sz) {
+			case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c,  8), nullptr, nullptr);
+			case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+			}
+
+			LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
+			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+			return lb_arg_type_indirect(return_type, attr);
+		}
+		return lbAbi386::non_struct(c, return_type, true);
+	}
 };
 
 // NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
@@ -450,7 +530,7 @@ namespace lbAbiAmd64SysV {
 		RegClass_Memory,
 	};
 
-	bool is_sse(RegClass reg_class) {
+	gb_internal bool is_sse(RegClass reg_class) {
 		switch (reg_class) {
 		case RegClass_SSEFs:
 		case RegClass_SSEFv:
@@ -466,7 +546,7 @@ namespace lbAbiAmd64SysV {
 		return false;
 	}
 
-	void all_mem(Array<RegClass> *cs) {
+	gb_internal void all_mem(Array<RegClass> *cs) {
 		for_array(i, *cs) {
 			(*cs)[i] = RegClass_Memory;
 		}
@@ -478,19 +558,19 @@ namespace lbAbiAmd64SysV {
 		Amd64TypeAttribute_StructRect,
 	};
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
-	void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
-	void fixup(LLVMTypeRef t, Array<RegClass> *cls);
-	lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
-	Array<RegClass> classify(LLVMTypeRef t);
-	LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
+	gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
+	gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls);
+	gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
+	gb_internal Array<RegClass> classify(LLVMTypeRef t);
+	gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
 
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->calling_convention = calling_convention;
 
-		ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+		ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 		for (unsigned i = 0; i < arg_count; i++) {
 			ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
 		}
@@ -504,7 +584,7 @@ namespace lbAbiAmd64SysV {
 		return ft;
 	}
 
-	bool is_mem_cls(Array<RegClass> const &cls, Amd64TypeAttributeKind attribute_kind) {
+	gb_internal bool is_mem_cls(Array<RegClass> const &cls, Amd64TypeAttributeKind attribute_kind) {
 		if (attribute_kind == Amd64TypeAttribute_ByVal) {
 			if (cls.count == 0) {
 				return false;
@@ -520,7 +600,7 @@ namespace lbAbiAmd64SysV {
 		return false;
 	}
 
-	bool is_register(LLVMTypeRef type) {
+	gb_internal bool is_register(LLVMTypeRef type) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		i64 sz = lb_sizeof(type);
 		if (sz == 0) {
@@ -537,7 +617,7 @@ namespace lbAbiAmd64SysV {
 		return false;
 	}
 
-	bool is_llvm_type_slice_like(LLVMTypeRef type) {
+	gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
 		if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
 			return false;
 		}
@@ -553,7 +633,7 @@ namespace lbAbiAmd64SysV {
 
 	}
 
-	lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
+	gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
 		if (is_register(type)) {
 			LLVMAttributeRef attribute = nullptr;
 			if (type == LLVMInt1TypeInContext(c)) {
@@ -588,7 +668,7 @@ namespace lbAbiAmd64SysV {
 		}
 	}
 
-	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
 		LLVMAttributeRef attr = nullptr;
 		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
 		if (type == i1) {
@@ -597,7 +677,7 @@ namespace lbAbiAmd64SysV {
 		return lb_arg_type_direct(type, nullptr, nullptr, attr);
 	}
 
-	Array<RegClass> classify(LLVMTypeRef t) {
+	gb_internal Array<RegClass> classify(LLVMTypeRef t) {
 		i64 sz = lb_sizeof(t);
 		i64 words = (sz + 7)/8;
 		auto reg_classes = array_make<RegClass>(heap_allocator(), cast(isize)words);
@@ -610,7 +690,7 @@ namespace lbAbiAmd64SysV {
 		return reg_classes;
 	}
 
-	void unify(Array<RegClass> *cls, i64 i, RegClass const newv) {
+	gb_internal void unify(Array<RegClass> *cls, i64 i, RegClass const newv) {
 		RegClass const oldv = (*cls)[cast(isize)i];
 		if (oldv == newv) {
 			return;
@@ -646,7 +726,7 @@ namespace lbAbiAmd64SysV {
 		(*cls)[cast(isize)i] = to_write;
 	}
 
-	void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
+	gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
 		i64 i = 0;
 		i64 e = cls->count;
 		if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) ||
@@ -693,7 +773,7 @@ namespace lbAbiAmd64SysV {
 		}
 	}
 
-	unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
+	gb_internal unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
 		unsigned len = 1;
 		for (isize i = offset; i < reg_classes.count; i++) {
 			if (reg_classes[i] != RegClass_SSEUp) {
@@ -705,7 +785,7 @@ namespace lbAbiAmd64SysV {
 	}
 
 
-	LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
+	gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
 		auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
 		for (isize i = 0; i < reg_classes.count; /**/) {
 			RegClass reg_class = reg_classes[i];
@@ -774,7 +854,7 @@ namespace lbAbiAmd64SysV {
 		return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
 	}
 
-	void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
+	gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
 		i64 t_align = lb_alignof(t);
 		i64 t_size  = lb_sizeof(t);
 
@@ -875,7 +955,7 @@ namespace lbAbiAmd64SysV {
 		}
 	}
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
 		if (!return_is_defined) {
 			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
 		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
@@ -886,6 +966,9 @@ namespace lbAbiAmd64SysV {
 			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
 			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
 			}
+
+			LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
 			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
 			return lb_arg_type_indirect(return_type, attr);
 		} else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
@@ -897,20 +980,20 @@ namespace lbAbiAmd64SysV {
 
 
 namespace lbAbiArm64 {
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
-	bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
+	gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
 
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
-		ft->ret = compute_return_type(c, return_type, return_is_defined);
-		ft -> args = compute_arg_types(c, arg_types, arg_count);
+		ft->args = compute_arg_types(c, arg_types, arg_count);
+		ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
 		ft->calling_convention = calling_convention;
 		return ft;
 	}
 
-	bool is_register(LLVMTypeRef type) {
+	gb_internal bool is_register(LLVMTypeRef type) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		switch (kind) {
 		case LLVMIntegerTypeKind:
@@ -923,7 +1006,7 @@ namespace lbAbiArm64 {
 		return false;
 	}
 
-	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
 		LLVMAttributeRef attr = nullptr;
 		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
 		if (type == i1) {
@@ -932,7 +1015,7 @@ namespace lbAbiArm64 {
 		return lb_arg_type_direct(type, nullptr, nullptr, attr);
 	}
 
-	bool is_homogenous_array(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+	gb_internal bool is_homogenous_array(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
 		GB_ASSERT(lb_is_type_kind(type, LLVMArrayTypeKind));
 		unsigned len = LLVMGetArrayLength(type);
 		if (len == 0) {
@@ -949,7 +1032,7 @@ namespace lbAbiArm64 {
 		}
 		return false;
 	}
-	bool is_homogenous_struct(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+	gb_internal bool is_homogenous_struct(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
 		GB_ASSERT(lb_is_type_kind(type, LLVMStructTypeKind));
 		unsigned elem_count = LLVMCountStructElementTypes(type);
 		if (elem_count == 0) {
@@ -992,7 +1075,7 @@ namespace lbAbiArm64 {
 	}
 
 
-	bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
+	gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		switch (kind) {
 		case LLVMFloatTypeKind:
@@ -1008,31 +1091,33 @@ namespace lbAbiArm64 {
 		return false;
 	}
 
-	unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef base_type, unsigned member_count) {
+	gb_internal unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef base_type, unsigned member_count) {
 		return (member_count <= 4);
 	}
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
 		LLVMTypeRef homo_base_type = nullptr;
 		unsigned homo_member_count = 0;
 
 		if (!return_is_defined) {
 			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
-		} else if (is_register(type)) {
-			return non_struct(c, type);
-		} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
+		} else if (is_register(return_type)) {
+			return non_struct(c, return_type);
+		} else if (is_homogenous_aggregate(c, return_type, &homo_base_type, &homo_member_count)) {
 			if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
-				return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+				return lb_arg_type_direct(return_type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
 			} else {
 				//TODO(Platin): do i need to create stuff that can handle the diffrent return type?
 				//              else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
 
+				LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
 				//LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
-				LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
-				return lb_arg_type_indirect(type, attr);
+				LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+				return lb_arg_type_indirect(return_type, attr);
 			}
 		} else {
-			i64 size = lb_sizeof(type);
+			i64 size = lb_sizeof(return_type);
 			if (size <= 16) {
 				LLVMTypeRef cast_type = nullptr;
 				if (size <= 1) {
@@ -1047,16 +1132,18 @@ namespace lbAbiArm64 {
 					unsigned count = cast(unsigned)((size+7)/8);
 					cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
 				}
-				return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+				return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
 			} else {
-				LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
-				return lb_arg_type_indirect(type, attr);
+				LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
+				LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+				return lb_arg_type_indirect(return_type, attr);
 			}
 		}
 	}
     
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
-		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 
 		for (unsigned i = 0; i < arg_count; i++) {
 			LLVMTypeRef type = arg_types[i];
@@ -1101,21 +1188,21 @@ namespace lbAbiWasm {
 		            The approach taken optimizes for passing things in multiple
 		            registers/arguments if possible rather than by pointer.
 	*/
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 
 	enum {MAX_DIRECT_STRUCT_SIZE = 32};
 
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
-		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
 		ft->calling_convention = calling_convention;
 		return ft;
 	}
 
-	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
 		if (!is_return && type == LLVMIntTypeInContext(c, 128)) {
 			LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
 			return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
@@ -1133,7 +1220,7 @@ namespace lbAbiWasm {
 		return lb_arg_type_direct(type, nullptr, nullptr, attr);
 	}
 	
-	bool is_basic_register_type(LLVMTypeRef type) {
+	gb_internal bool is_basic_register_type(LLVMTypeRef type) {
 		switch (LLVMGetTypeKind(type)) {
 		case LLVMHalfTypeKind:
 		case LLVMFloatTypeKind:
@@ -1146,7 +1233,7 @@ namespace lbAbiWasm {
 		return false;
 	}
 
-	bool type_can_be_direct(LLVMTypeRef type) {
+	gb_internal bool type_can_be_direct(LLVMTypeRef type) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		i64 sz = lb_sizeof(type);
 		if (sz == 0) {
@@ -1172,7 +1259,7 @@ namespace lbAbiWasm {
 		return false;
 	}
 
-	lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) {
+	gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind);
 		
@@ -1187,8 +1274,8 @@ namespace lbAbiWasm {
 	}
 	
 
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
-		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 
 		for (unsigned i = 0; i < arg_count; i++) {
 			LLVMTypeRef t = arg_types[i];
@@ -1202,7 +1289,7 @@ namespace lbAbiWasm {
 		return args;
 	}
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
 		if (!return_is_defined) {
 			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
 		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
@@ -1217,6 +1304,9 @@ namespace lbAbiWasm {
 			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
 			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
 			}
+
+			LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
 			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
 			return lb_arg_type_indirect(return_type, attr);
 		}
@@ -1225,10 +1315,10 @@ namespace lbAbiWasm {
 }
 
 namespace lbAbiArm32 {
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
+	gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
 
-	LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
@@ -1237,7 +1327,7 @@ namespace lbAbiArm32 {
 		return ft;
 	}
 
-	bool is_register(LLVMTypeRef type, bool is_return) {
+	gb_internal bool is_register(LLVMTypeRef type, bool is_return) {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		switch (kind) {
 		case LLVMHalfTypeKind:
@@ -1256,7 +1346,7 @@ namespace lbAbiArm32 {
 		return false;
 	}
 
-	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
 		LLVMAttributeRef attr = nullptr;
 		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
 		if (type == i1) {
@@ -1265,8 +1355,8 @@ namespace lbAbiArm32 {
 		return lb_arg_type_direct(type, nullptr, nullptr, attr);
 	}
 
-	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
-		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 
 		for (unsigned i = 0; i < arg_count; i++) {
 			LLVMTypeRef t = arg_types[i];
@@ -1290,7 +1380,7 @@ namespace lbAbiArm32 {
 		return args;
 	}
 
-	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+	gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
 		if (!return_is_defined) {
 			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
 		} else if (!is_register(return_type, true)) {
@@ -1307,14 +1397,14 @@ namespace lbAbiArm32 {
 };
 
 
-LB_ABI_INFO(lb_get_abi_info) {
+gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
 	switch (calling_convention) {
 	case ProcCC_None:
 	case ProcCC_InlineAsm:
 		{
 			lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 			ft->ctx = c;
-			ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
+			ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
 			for (unsigned i = 0; i < arg_count; i++) {
 				ft->args[i] = lb_arg_type_direct(arg_types[i]);
 			}
@@ -1328,32 +1418,55 @@ LB_ABI_INFO(lb_get_abi_info) {
 		}
 	case ProcCC_Win64:
 		GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
-		return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	case ProcCC_SysV:
 		GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
-		return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	}
 
 	switch (build_context.metrics.arch) {
 	case TargetArch_amd64:
-		if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
-			return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		if (build_context.metrics.os == TargetOs_windows) {
+			return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
+		} else if (build_context.metrics.abi == TargetABI_Win64) {
+			return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 		} else if (build_context.metrics.abi == TargetABI_SysV) {
-			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 		} else {
-			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 		}
 	case TargetArch_i386:
-		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	case TargetArch_arm32:
-		return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	case TargetArch_arm64:
-		return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	case TargetArch_wasm32:
 	case TargetArch_wasm64:
-		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention);
 	}
 
 	GB_PANIC("Unsupported ABI");
 	return {};
 }
+
+
+gb_internal LB_ABI_INFO(lb_get_abi_info) {
+	lbFunctionType *ft = lb_get_abi_info_internal(
+		c,
+		arg_types, arg_count,
+		return_type, return_is_defined,
+		ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
+		calling_convention);
+
+
+	// NOTE(bill): this is handled here rather than when developing the type in `lb_type_internal_for_procedures_raw`
+	// This is to make it consistent when and how it is handled
+	if (calling_convention == ProcCC_Odin) {
+		// append the `context` pointer
+		lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
+		array_add(&ft->args, context_param);
+	}
+
+	return ft;
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 622 - 340
src/llvm_backend.cpp


+ 169 - 150
src/llvm_backend.hpp

@@ -117,20 +117,34 @@ struct lbIncompleteDebugType {
 
 typedef Slice<i32> lbStructFieldRemapping;
 
+enum lbFunctionPassManagerKind {
+	lbFunctionPassManager_default,
+	lbFunctionPassManager_default_without_memcpy,
+	lbFunctionPassManager_minimal,
+	lbFunctionPassManager_size,
+	lbFunctionPassManager_speed,
+
+	lbFunctionPassManager_COUNT
+};
+
 struct lbModule {
 	LLVMModuleRef mod;
 	LLVMContextRef ctx;
 
 	struct lbGenerator *gen;
+	LLVMTargetMachineRef target_machine;
 
 	CheckerInfo *info;
-	AstPackage *pkg; // associated
+	AstPackage *pkg; // possibly associated
+	AstFile *file;   // possibly associated
 
 	PtrMap<Type *, LLVMTypeRef> types;
 	PtrMap<Type *, LLVMTypeRef> func_raw_types;
 	PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *
 	i32 internal_type_level;
 
+	RwMutex values_mutex;
+
 	PtrMap<Entity *, lbValue> values;           
 	PtrMap<Entity *, lbAddr>  soa_values;       
 	StringMap<lbValue>  members;
@@ -150,11 +164,14 @@ struct lbModule {
 	u32 nested_type_name_guid;
 
 	Array<lbProcedure *> procedures_to_generate;
+	Array<Entity *> global_procedures_and_types_to_create;
 
 	lbProcedure *curr_procedure;
 
 	LLVMDIBuilderRef debug_builder;
 	LLVMMetadataRef debug_compile_unit;
+
+	RecursiveMutex debug_values_mutex;
 	PtrMap<void *, LLVMMetadataRef> debug_values; 
 
 	Array<lbIncompleteDebugType> debug_incomplete_types;
@@ -164,6 +181,8 @@ struct lbModule {
 
 	PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
 	PtrMap<Type *, lbAddr> map_info_map;      // address of runtime.Map_Cell_Info
+
+	LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
 };
 
 struct lbGenerator {
@@ -173,10 +192,11 @@ struct lbGenerator {
 	Array<String> output_temp_paths;
 	String   output_base;
 	String   output_name;
-	PtrMap<AstPackage *, lbModule *> modules; 
+	PtrMap<void *, lbModule *> modules; // key is `AstPackage *` (`void *` is used for future use)
 	PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; 
 	lbModule default_module;
 
+	BlockingMutex anonymous_proc_lits_mutex;
 	PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; 
 
 	BlockingMutex foreign_mutex;
@@ -185,6 +205,10 @@ struct lbGenerator {
 
 	std::atomic<u32> global_array_index;
 	std::atomic<u32> global_generated_index;
+
+	lbProcedure *startup_type_info;
+	lbProcedure *startup_runtime;
+	lbProcedure *objc_names;
 };
 
 
@@ -254,17 +278,15 @@ struct lbTargetList {
 };
 
 
+struct lbTupleFix {
+	Slice<lbValue> values;
+};
+
 enum lbProcedureFlag : u32 {
 	lbProcedureFlag_WithoutMemcpyPass = 1<<0,
 	lbProcedureFlag_DebugAllocaCopy = 1<<1,
 };
 
-struct lbCopyElisionHint {
-	lbValue ptr;
-	Ast *   ast;
-	bool    used;
-};
-
 struct lbProcedure {
 	u32 flags;
 	u16 state_flags;
@@ -302,6 +324,7 @@ struct lbProcedure {
 	lbBlock *        curr_block;
 	lbTargetList *   target_list;
 	PtrMap<Entity *, lbValue> direct_parameters;
+	bool             in_multi_assignment;
 
 	Ast *curr_stmt;
 
@@ -310,10 +333,9 @@ struct lbProcedure {
 
 	LLVMMetadataRef debug_info;
 
-	lbCopyElisionHint copy_elision_hint;
-
 	PtrMap<Ast *, lbValue> selector_values;
 	PtrMap<Ast *, lbAddr>  selector_addr;
+	PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
 };
 
 
@@ -324,200 +346,197 @@ struct lbProcedure {
 #define LLVMBuildPtrDiff2(Builder__, Ty__, LHS__, RHS__, Name__) LLVMBuildPtrDiff(Builder__, LHS__, RHS__, Name__)
 #endif
 
-bool lb_init_generator(lbGenerator *gen, Checker *c);
-
-String lb_mangle_name(lbModule *m, Entity *e);
-String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
+gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
 
-LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
-LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
-void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
-void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
-lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
-void lb_end_procedure(lbProcedure *p);
+gb_internal String lb_mangle_name(lbModule *m, Entity *e);
+gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
 
+gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
+gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
+gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
+gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
+gb_internal lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
+gb_internal void lb_end_procedure(lbProcedure *p);
 
-LLVMTypeRef lb_type(lbModule *m, Type *type);
-LLVMTypeRef llvm_get_element_type(LLVMTypeRef type);
 
-lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
+gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type);
+gb_internal LLVMTypeRef llvm_get_element_type(LLVMTypeRef type);
 
-lbValue lb_const_nil(lbModule *m, Type *type);
-lbValue lb_const_undef(lbModule *m, Type *type);
-lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true);
-lbValue lb_const_bool(lbModule *m, Type *type, bool value);
-lbValue lb_const_int(lbModule *m, Type *type, u64 value);
+gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
 
+gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
+gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true);
+gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
+gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
 
-lbAddr lb_addr(lbValue addr);
-Type *lb_addr_type(lbAddr const &addr);
-LLVMTypeRef llvm_addr_type(lbModule *module, lbValue addr_val);
-void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value);
-lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr);
-lbValue lb_emit_load(lbProcedure *p, lbValue v);
-void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value);
 
+gb_internal lbAddr lb_addr(lbValue addr);
+gb_internal Type *lb_addr_type(lbAddr const &addr);
+gb_internal LLVMTypeRef llvm_addr_type(lbModule *module, lbValue addr_val);
+gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value);
+gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr);
+gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue v);
+gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value);
 
-void    lb_build_stmt(lbProcedure *p, Ast *stmt);
-lbValue lb_build_expr(lbProcedure *p, Ast *expr);
-lbAddr  lb_build_addr(lbProcedure *p, Ast *expr);
-void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts);
 
-lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index);
-lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index);
-lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index);
-lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index);
-lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index);
-lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index);
-lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index);
-lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
-lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
+gb_internal void    lb_build_stmt(lbProcedure *p, Ast *stmt);
+gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr);
+gb_internal lbAddr  lb_build_addr(lbProcedure *p, Ast *expr);
+gb_internal void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts);
 
-lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column);
-lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column);
-lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column);
+gb_internal lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index);
+gb_internal lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index);
+gb_internal lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index);
+gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index);
+gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index);
+gb_internal lbValue lb_emit_tuple_ev(lbProcedure *p, lbValue value, i32 index);
+gb_internal lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index);
+gb_internal lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index);
+gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
+gb_internal lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
 
+gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column);
+gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column);
+gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column);
 
-lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
-void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
-lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
-lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
-lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false);
-lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
-lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x);
 
-void lb_emit_jump(lbProcedure *p, lbBlock *target_block);
-void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *false_block);
-void lb_start_block(lbProcedure *p, lbBlock *b);
+gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
+gb_internal lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
+gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
+gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
+gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none);
+gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
+gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x);
 
-lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
+gb_internal void lb_emit_jump(lbProcedure *p, lbBlock *target_block);
+gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *false_block);
+gb_internal void lb_start_block(lbProcedure *p, lbBlock *b);
 
+gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
 
-lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
-lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
-lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
 
+gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
+gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
+gb_internal lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
 
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
-lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
 
-void lb_add_foreign_library_path(lbModule *m, Entity *e);
+gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
+gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, bool force_no_init=false);
 
-lbValue lb_typeid(lbModule *m, Type *type);
+gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e);
 
-lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
-lbValue lb_address_from_load(lbProcedure *p, lbValue value);
-void    lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt);
-lbAddr lb_add_local_generated(lbProcedure *p, Type *type, bool zero_init);
+gb_internal lbValue lb_typeid(lbModule *m, Type *type);
 
-lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args);
+gb_internal lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
+gb_internal lbValue lb_address_from_load(lbProcedure *p, lbValue value);
+gb_internal void    lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt);
+gb_internal lbAddr lb_add_local_generated(lbProcedure *p, Type *type, bool zero_init);
 
+gb_internal lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args);
 
-lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index);
-lbValue lb_string_elem(lbProcedure *p, lbValue string);
-lbValue lb_string_len(lbProcedure *p, lbValue string);
-lbValue lb_cstring_len(lbProcedure *p, lbValue value);
-lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr);
-lbValue lb_slice_elem(lbProcedure *p, lbValue slice);
-lbValue lb_slice_len(lbProcedure *p, lbValue slice);
-lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
-lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
-lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
-lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
-lbValue lb_map_len(lbProcedure *p, lbValue value);
-lbValue lb_map_cap(lbProcedure *p, lbValue value);
-lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
-void lb_emit_increment(lbProcedure *p, lbValue addr);
-lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y);
 
-lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t);
+gb_internal lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index);
+gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string);
+gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string);
+gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value);
+gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr);
+gb_internal lbValue lb_slice_elem(lbProcedure *p, lbValue slice);
+gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice);
+gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
+gb_internal lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
+gb_internal lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
+gb_internal lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
+gb_internal lbValue lb_map_len(lbProcedure *p, lbValue value);
+gb_internal lbValue lb_map_cap(lbProcedure *p, lbValue value);
+gb_internal lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
+gb_internal void lb_emit_increment(lbProcedure *p, lbValue addr);
+gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y);
 
-void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len);
+gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t);
 
-lbValue lb_type_info(lbModule *m, Type *type);
+gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len);
 
-lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
-lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
+gb_internal lbValue lb_type_info(lbModule *m, Type *type);
 
-bool lb_is_const(lbValue value);
-bool lb_is_const_or_global(lbValue value);
-bool lb_is_const_nil(lbValue value);
-String lb_get_const_string(lbModule *m, lbValue value);
+gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
+gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
 
-lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
-lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
-lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
-lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
-lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
+gb_internal bool lb_is_const(lbValue value);
+gb_internal bool lb_is_const_or_global(lbValue value);
+gb_internal bool lb_is_const_nil(lbValue value);
+gb_internal String lb_get_const_string(lbModule *m, lbValue value);
 
-lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
-void    lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
-lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
+gb_internal lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
+gb_internal lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
+gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
+gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
+gb_internal lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
 
-lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
-lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
+gb_internal lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
+gb_internal void    lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
+gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
 
-void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
-lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
-lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
+gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
+gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
 
-lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
+gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
+gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
 
-lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
-lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
-lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
 
-LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
+gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
+gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
+gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
 
-lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type);
-lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type);
-lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type);
-lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type);
-lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type);
+gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
 
-lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);
+gb_internal lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type);
+gb_internal lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type);
+gb_internal lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type);
+gb_internal lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type);
+gb_internal lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type);
 
-void lb_mem_zero_addr(lbProcedure *p, LLVMValueRef ptr, Type *type);
+gb_internal lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);
 
-void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e);
-lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type);
-lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
+gb_internal void lb_mem_zero_addr(lbProcedure *p, LLVMValueRef ptr, Type *type);
 
-LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
-LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
-void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
+gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e);
+gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type);
+gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
 
-lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
-bool lb_is_expr_untyped_const(Ast *expr);
+gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
+gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
+gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
 
-LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name = "");
+gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
+gb_internal bool lb_is_expr_untyped_const(Ast *expr);
 
-void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment);
+gb_internal LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name = "");
 
-void lb_emit_init_context(lbProcedure *p, lbAddr addr);
+gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment);
 
-lbCopyElisionHint lb_set_copy_elision_hint(lbProcedure *p, lbAddr const &addr, Ast *ast);
-void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint);
-lbValue lb_consume_copy_elision_hint(lbProcedure *p);
+gb_internal void lb_emit_init_context(lbProcedure *p, lbAddr addr);
 
 
-lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t);
-LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
+gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t);
+gb_internal LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
 
-LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
+gb_internal LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
 
-LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count);
-void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
-void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
-LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
+gb_internal LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count);
+gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
+gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
+gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
 
-i64 lb_max_zero_init_size(void) {
+gb_internal gb_inline i64 lb_max_zero_init_size(void) {
 	return cast(i64)(4*build_context.word_size);
 }
 
-LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
-LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
+gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
+gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
 
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
@@ -637,7 +656,7 @@ enum : LLVMAttributeIndex {
 };
 
 
-char const *llvm_linkage_strings[] = {
+gb_global char const *llvm_linkage_strings[] = {
 	"external linkage",
 	"available externally linkage",
 	"link once any linkage",

+ 30 - 30
src/llvm_backend_const.cpp

@@ -1,4 +1,4 @@
-bool lb_is_const(lbValue value) {
+gb_internal bool lb_is_const(lbValue value) {
 	LLVMValueRef v = value.value;
 	if (is_type_untyped_nil(value.type) || is_type_untyped_undef(value.type)) {
 		// TODO(bill): Is this correct behaviour?
@@ -10,7 +10,7 @@ bool lb_is_const(lbValue value) {
 	return false;
 }
 
-bool lb_is_const_or_global(lbValue value) {
+gb_internal bool lb_is_const_or_global(lbValue value) {
 	if (lb_is_const(value)) {
 		return true;
 	}
@@ -29,7 +29,7 @@ bool lb_is_const_or_global(lbValue value) {
 }
 
 
-bool lb_is_elem_const(Ast *elem, Type *elem_type) {
+gb_internal bool lb_is_elem_const(Ast *elem, Type *elem_type) {
 	if (!elem_type_can_be_constant(elem_type)) {
 		return false;
 	}
@@ -42,7 +42,7 @@ bool lb_is_elem_const(Ast *elem, Type *elem_type) {
 }
 
 
-bool lb_is_const_nil(lbValue value) {
+gb_internal bool lb_is_const_nil(lbValue value) {
 	LLVMValueRef v = value.value;
 	if (LLVMIsConstant(v)) {
 		if (LLVMIsAConstantAggregateZero(v)) {
@@ -55,7 +55,7 @@ bool lb_is_const_nil(lbValue value) {
 }
 
 
-bool lb_is_expr_constant_zero(Ast *expr) {
+gb_internal bool lb_is_expr_constant_zero(Ast *expr) {
 	GB_ASSERT(expr != nullptr);
 	auto v = exact_value_to_integer(expr->tav.value);
 	if (v.kind == ExactValue_Integer) {
@@ -64,7 +64,7 @@ bool lb_is_expr_constant_zero(Ast *expr) {
 	return false;
 }
 
-String lb_get_const_string(lbModule *m, lbValue value) {
+gb_internal String lb_get_const_string(lbModule *m, lbValue value) {
 	GB_ASSERT(lb_is_const(value));
 	GB_ASSERT(LLVMIsConstant(value.value));
 
@@ -92,7 +92,7 @@ String lb_get_const_string(lbModule *m, lbValue value) {
 }
 
 
-LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
+gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
 	LLVMTypeRef src = LLVMTypeOf(val);
 	if (src == dst) {
 		return val;
@@ -116,7 +116,7 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
 }
 
 
-lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
+gb_internal lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
 	GB_ASSERT(is_type_internally_pointer_like(value.type));
 	GB_ASSERT(is_type_internally_pointer_like(t));
 	GB_ASSERT(lb_is_const(value));
@@ -127,7 +127,7 @@ lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
 	return res;
 }
 
-LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_) {
+gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_) {
 	LLVMTypeRef struct_type = lb_type(m, t);
 	GB_ASSERT(LLVMGetTypeKind(struct_type) == LLVMStructTypeKind);
 	
@@ -157,7 +157,7 @@ LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values,
 	return llvm_const_named_struct_internal(struct_type, values_with_padding, values_with_padding_count);
 }
 
-LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
+gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
 	unsigned value_count = cast(unsigned)value_count_;
 	unsigned elem_count = LLVMCountStructElementTypes(t);
 	GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
@@ -168,7 +168,7 @@ LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *value
 	return LLVMConstNamedStruct(t, values, value_count);
 }
 
-LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
+gb_internal LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
 	unsigned value_count = cast(unsigned)value_count_;
 	for (unsigned i = 0; i < value_count; i++) {
 		values[i] = llvm_const_cast(values[i], elem_type);
@@ -176,7 +176,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
 	return LLVMConstArray(elem_type, values, value_count);
 }
 
-LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
+gb_internal LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
 	GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
 	GB_ASSERT(are_types_identical(len.type, t_int));
 	LLVMValueRef vals[2] = {
@@ -187,38 +187,38 @@ LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
 }
 
 
-lbValue lb_const_nil(lbModule *m, Type *type) {
+gb_internal lbValue lb_const_nil(lbModule *m, Type *type) {
 	LLVMValueRef v = LLVMConstNull(lb_type(m, type));
 	return lbValue{v, type};
 }
 
-lbValue lb_const_undef(lbModule *m, Type *type) {
+gb_internal lbValue lb_const_undef(lbModule *m, Type *type) {
 	LLVMValueRef v = LLVMGetUndef(lb_type(m, type));
 	return lbValue{v, type};
 }
 
 
 
-lbValue lb_const_int(lbModule *m, Type *type, u64 value) {
+gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value) {
 	lbValue res = {};
 	res.value = LLVMConstInt(lb_type(m, type), cast(unsigned long long)value, !is_type_unsigned(type));
 	res.type = type;
 	return res;
 }
 
-lbValue lb_const_string(lbModule *m, String const &value) {
+gb_internal lbValue lb_const_string(lbModule *m, String const &value) {
 	return lb_const_value(m, t_string, exact_value_string(value));
 }
 
 
-lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
+gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
 	lbValue res = {};
 	res.value = LLVMConstInt(lb_type(m, type), value, false);
 	res.type = type;
 	return res;
 }
 
-LLVMValueRef lb_const_f16(lbModule *m, f32 f, Type *type=t_f16) {
+gb_internal LLVMValueRef lb_const_f16(lbModule *m, f32 f, Type *type=t_f16) {
 	GB_ASSERT(type_size_of(type) == 2);
 
 	u16 u = f32_to_f16(f);
@@ -229,7 +229,7 @@ LLVMValueRef lb_const_f16(lbModule *m, f32 f, Type *type=t_f16) {
 	return LLVMConstBitCast(i, lb_type(m, type));
 }
 
-LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
+gb_internal LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
 	GB_ASSERT(type_size_of(type) == 4);
 	u32 u = bit_cast<u32>(f);
 	if (is_type_different_to_arch_endianness(type)) {
@@ -241,7 +241,7 @@ LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
 
 
 
-bool lb_is_expr_untyped_const(Ast *expr) {
+gb_internal bool lb_is_expr_untyped_const(Ast *expr) {
 	auto const &tv = type_and_value_of_expr(expr);
 	if (is_type_untyped(tv.type)) {
 		return tv.value.kind != ExactValue_Invalid;
@@ -250,13 +250,13 @@ bool lb_is_expr_untyped_const(Ast *expr) {
 }
 
 
-lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
+gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
 	GB_ASSERT(is_type_typed(t));
 	auto const &tv = type_and_value_of_expr(expr);
 	return lb_const_value(m, t, tv.value);
 }
 
-lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos) {
 	lbModule *m = p->module;
 
 	LLVMValueRef fields[4] = {};
@@ -271,7 +271,7 @@ lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedu
 	return res;
 }
 
-lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
+gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
 	String proc_name = {};
 	if (p->entity) {
 		proc_name = p->entity->token.string;
@@ -284,7 +284,7 @@ lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
 }
 
 
-lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
 	lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
 	lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
 	lb_make_global_private_const(addr);
@@ -292,24 +292,24 @@ lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const
 }
 
 
-lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
+gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
 	lbValue loc = lb_emit_source_code_location_const(p, node);
 	lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
 	lb_make_global_private_const(addr);
 	return addr.addr;
 }
 
-lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
 	return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
 }
 
-lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
+gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
 	return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, node));
 }
 
 
 
-LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
+gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
 	bool is_local = allow_local && m->curr_procedure != nullptr;
 	bool is_const = true;
 	if (is_local) {
@@ -341,7 +341,7 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_
 	return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count);
 }
 
-LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
+gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
 	if (big_int_is_zero(a)) {
 		return LLVMConstNull(lb_type(m, original_type));
 	}
@@ -387,7 +387,7 @@ LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *
 }
 
 
-lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
 	LLVMContextRef ctx = m->ctx;
 
 	type = default_type(type);

+ 33 - 34
src/llvm_backend_debug.cpp

@@ -1,27 +1,25 @@
-LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
+gb_internal LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
 	if (key == nullptr) {
 		return nullptr;
 	}
+	mutex_lock(&m->debug_values_mutex);
 	auto found = map_get(&m->debug_values, key);
+	mutex_unlock(&m->debug_values_mutex);
 	if (found) {
 		return *found;
 	}
 	return nullptr;
 }
-void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef value) {
+gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef value) {
 	if (key != nullptr) {
+		mutex_lock(&m->debug_values_mutex);
 		map_set(&m->debug_values, key, value);
+		mutex_unlock(&m->debug_values_mutex);
 	}
 }
 
-LLVMMetadataRef lb_get_llvm_file_metadata_from_node(lbModule *m, Ast *node) {
-	if (node == nullptr) {
-		return nullptr;
-	}
-	return lb_get_llvm_metadata(m, node->file());
-}
 
-LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
+gb_internal LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
 	GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name));
 
 	for (isize i = p->scope_stack.count-1; i >= 0; i--) {
@@ -34,21 +32,21 @@ LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
 	return p->debug_info;
 }
 
-LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos) {
+gb_internal LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos) {
 	LLVMMetadataRef scope = lb_get_current_debug_scope(p);
 	GB_ASSERT_MSG(scope != nullptr, "%.*s", LIT(p->name));
 	return LLVMDIBuilderCreateDebugLocation(p->module->ctx, cast(unsigned)pos.line, cast(unsigned)pos.column, scope, nullptr);
 }
-LLVMMetadataRef lb_debug_location_from_ast(lbProcedure *p, Ast *node) {
+gb_internal LLVMMetadataRef lb_debug_location_from_ast(lbProcedure *p, Ast *node) {
 	GB_ASSERT(node != nullptr);
 	return lb_debug_location_from_token_pos(p, ast_token(node).pos);
 }
-LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast *node) {
+gb_internal LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast *node) {
 	GB_ASSERT(node != nullptr);
 	return lb_debug_location_from_token_pos(p, ast_end_token(node).pos);
 }
 
-LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
+gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
 	i64 size = type_size_of(type); // Check size
 	gb_unused(size);
 
@@ -93,7 +91,7 @@ LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
 	return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters, parameter_count, flags);
 }
 
-LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
+gb_internal LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
 	unsigned field_line = 1;
 	LLVMDIFlags field_flags = LLVMDIFlagZero;
 
@@ -107,7 +105,7 @@ LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *typ
 		field_flags, lb_debug_type(m, type)
 	);
 }
-LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_in_bits, u32 align_in_bits, LLVMMetadataRef *elements, unsigned element_count) {
+gb_internal LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_in_bits, u32 align_in_bits, LLVMMetadataRef *elements, unsigned element_count) {
 	AstPackage *pkg = m->info->runtime_package;
 	GB_ASSERT(pkg->files.count != 0);
 	LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]);
@@ -117,7 +115,7 @@ LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_
 }
 
 
-LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
+gb_internal LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
 	LLVMMetadataRef basic_type = LLVMDIBuilderCreateBasicType(m->debug_builder, cast(char const *)name.text, name.len, size_in_bits, encoding, flags);
 #if 1
 	LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, basic_type, cast(char const *)name.text, name.len, nullptr, 0, nullptr, cast(u32)size_in_bits);
@@ -127,7 +125,7 @@ LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 si
 #endif
 }
 
-LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
+gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 	i64 size = type_size_of(type); // Check size
 	gb_unused(size);
 
@@ -474,7 +472,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 	return nullptr;
 }
 
-LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) {
+gb_internal LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) {
 	LLVMMetadataRef found = nullptr;
 	for (;;) {
 		if (scope == nullptr) {
@@ -496,13 +494,15 @@ LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) {
 	}
 }
 
-LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
+gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
 	GB_ASSERT(type != nullptr);
 	LLVMMetadataRef found = lb_get_llvm_metadata(m, type);
 	if (found != nullptr) {
 		return found;
 	}
 
+	MUTEX_GUARD(&m->debug_values_mutex);
+
 	if (type->kind == Type_Named) {
 		LLVMMetadataRef file = nullptr;
 		unsigned line = 0;
@@ -615,11 +615,13 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
 	return dt;
 }
 
-void lb_debug_complete_types(lbModule *m) {
+gb_internal void lb_debug_complete_types(lbModule *m) {
 	/* unsigned const word_size = cast(unsigned)build_context.word_size; */
 	unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
 
 	for_array(debug_incomplete_type_index, m->debug_incomplete_types) {
+		TEMPORARY_ALLOCATOR_GUARD();
+
 		auto const &idt = m->debug_incomplete_types[debug_incomplete_type_index];
 		GB_ASSERT(idt.type != nullptr);
 		GB_ASSERT(idt.metadata != nullptr);
@@ -962,7 +964,7 @@ void lb_debug_complete_types(lbModule *m) {
 
 
 
-void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
+gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
 	if (p->debug_info == nullptr) {
 		return;
 	}
@@ -1024,7 +1026,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
 	LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
 }
 
-void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block, lbArgKind arg_kind) {
+gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block, lbArgKind arg_kind) {
 	if (p->debug_info == nullptr) {
 		return;
 	}
@@ -1097,7 +1099,7 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
 }
 
 
-void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
+gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
 	if (!p->debug_info || !p->body) {
 		return;
 	}
@@ -1125,17 +1127,17 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
 }
 
 
-String debug_info_mangle_constant_name(Entity *e, bool *did_allocate_) {
+gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
 	String name = e->token.string;
 	if (e->pkg && e->pkg->name.len > 0) {
 		// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
-		name = concatenate3_strings(heap_allocator(), e->pkg->name, str_lit("::"), name);
+		name = concatenate3_strings(allocator, e->pkg->name, str_lit("::"), name);
 		if (did_allocate_) *did_allocate_ = true;
 	}
 	return name;
 }
 
-void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
+gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
 	LLVMMetadataRef scope = nullptr;
 	LLVMMetadataRef file = nullptr;
 	unsigned line = 0;
@@ -1151,14 +1153,11 @@ void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMe
 		expr, decl, 8/*AlignInBits*/);
 }
 
-void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
+gb_internal void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
 	LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
 
-	bool did_allocate = false;
-	String name = debug_info_mangle_constant_name(e, &did_allocate);
-	defer (if (did_allocate) {
-		gb_free(heap_allocator(), name.text);
-	});
+	TEMPORARY_ALLOCATOR_GUARD();
+	String name = debug_info_mangle_constant_name(e, temporary_allocator(), nullptr);
 
 	add_debug_info_global_variable_expr(m, name, dtype, expr);
 	if ((e->pkg && e->pkg->kind == Package_Init) ||
@@ -1167,7 +1166,7 @@ void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLV
 	}
 }
 
-void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
+gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
 	if (e == nullptr || e->kind != Entity_Constant) {
 		return;
 	}
@@ -1176,7 +1175,7 @@ void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e)
 	}
 	lbModule *m = &gen->default_module;
 	if (USE_SEPARATE_MODULES) {
-		m = lb_pkg_module(gen, e->pkg);
+		m = lb_module_of_entity(gen, e);
 	}
 
 	if (is_type_integer(e->type)) {

+ 62 - 60
src/llvm_backend_expr.cpp

@@ -1,6 +1,6 @@
-lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
+gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
 
-lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
+gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
 	lbModule *m = p->module;
 
 	lbBlock *rhs  = lb_create_block(p, "logical.cmp.rhs");
@@ -61,8 +61,7 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast
 	GB_ASSERT(incoming_values.count > 0);
 
 	LLVMTypeRef phi_type = nullptr;
-	for_array(i, incoming_values) {
-		LLVMValueRef incoming_value = incoming_values[i];
+	for (LLVMValueRef incoming_value : incoming_values) {
 		if (!LLVMIsConstant(incoming_value)) {
 			phi_type = LLVMTypeOf(incoming_value);
 			break;
@@ -113,7 +112,7 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast
 }
 
 
-lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) {
 	switch (op) {
 	case Token_Add:
 		return x;
@@ -134,7 +133,7 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
 		Type *elem_type = base_array_type(type);
 
 		// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
-		lbAddr res_addr = lb_add_local(p, type, nullptr, false, 0, true);
+		lbAddr res_addr = lb_add_local(p, type, nullptr, false, true);
 		lbValue res = lb_addr_get_ptr(p, res_addr);
 
 		bool inline_array_arith = lb_can_try_to_inline_array_arith(type);
@@ -283,7 +282,7 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
 	return res;
 }
 
-bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, lbValue *res_) {
+gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, lbValue *res_) {
 	GB_ASSERT(is_type_array_like(type));
 	Type *elem_type = base_array_type(type);
 
@@ -418,7 +417,7 @@ bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbVal
 }
 
 
-lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
+gb_internal lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
 	GB_ASSERT(is_type_array_like(lhs.type) || is_type_array_like(rhs.type));
 
 	lhs = lb_emit_conv(p, lhs, type);
@@ -490,7 +489,7 @@ lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue r
 	}
 }
 
-bool lb_is_matrix_simdable(Type *t) {
+gb_internal bool lb_is_matrix_simdable(Type *t) {
 	Type *mt = base_type(t);
 	GB_ASSERT(mt->kind == Type_Matrix);
 	
@@ -510,6 +509,11 @@ bool lb_is_matrix_simdable(Type *t) {
 	case TargetArch_arm64:
 		break;
 	}
+
+	if (type_align_of(t) < 16) {
+		// it's not aligned well enough to use the vector instructions
+		return false;
+	}
 	
 	if (elem->kind == Type_Basic) {
 		switch (elem->Basic.kind) {
@@ -534,7 +538,7 @@ bool lb_is_matrix_simdable(Type *t) {
 }
 
 
-LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
+gb_internal LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
 	Type *mt = base_type(matrix.type);
 	GB_ASSERT(mt->kind == Type_Matrix);
 	LLVMTypeRef elem_type = lb_type(p->module, mt->Matrix.elem);
@@ -554,7 +558,7 @@ LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
 #endif
 }
 
-LLVMValueRef lb_matrix_trimmed_vector_mask(lbProcedure *p, Type *mt) {
+gb_internal LLVMValueRef lb_matrix_trimmed_vector_mask(lbProcedure *p, Type *mt) {
 	mt = base_type(mt);
 	GB_ASSERT(mt->kind == Type_Matrix);
 
@@ -574,7 +578,7 @@ LLVMValueRef lb_matrix_trimmed_vector_mask(lbProcedure *p, Type *mt) {
 	return mask;
 }
 
-LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
+gb_internal LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
 	LLVMValueRef vector = lb_matrix_to_vector(p, m);
 
 	Type *mt = base_type(m.type);
@@ -592,7 +596,7 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
 }
 
 
-lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
+gb_internal lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
 	if (is_type_array(m.type)) {
 		i32 rank = type_math_rank(m.type);
 		if (rank == 2) {
@@ -669,7 +673,7 @@ lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
 	return lb_addr_load(p, res);
 }
 
-lbValue lb_matrix_cast_vector_to_type(lbProcedure *p, LLVMValueRef vector, Type *type) {
+gb_internal lbValue lb_matrix_cast_vector_to_type(lbProcedure *p, LLVMValueRef vector, Type *type) {
 	lbAddr res = lb_add_local_generated(p, type, true);
 	LLVMValueRef res_ptr = res.addr.value;
 	unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(LLVMTypeOf(vector)));
@@ -681,7 +685,7 @@ lbValue lb_matrix_cast_vector_to_type(lbProcedure *p, LLVMValueRef vector, Type
 	return lb_addr_load(p, res);
 }
 
-lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type) {
+gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type) {
 	if (is_type_array(m.type)) {
 		// no-op
 		m.type = type;
@@ -710,7 +714,7 @@ lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type) {
 }
 
 
-lbValue lb_emit_outer_product(lbProcedure *p, lbValue a, lbValue b, Type *type) {
+gb_internal lbValue lb_emit_outer_product(lbProcedure *p, lbValue a, lbValue b, Type *type) {
 	Type *mt = base_type(type);
 	Type *at = base_type(a.type);
 	Type *bt = base_type(b.type);
@@ -741,7 +745,7 @@ lbValue lb_emit_outer_product(lbProcedure *p, lbValue a, lbValue b, Type *type)
 
 }
 
-lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
+gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
 	// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
 
 	Type *xt = base_type(lhs.type);
@@ -828,7 +832,7 @@ lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type)
 	}
 }
 
-lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
+gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
 	// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
 
 	Type *mt = base_type(lhs.type);
@@ -897,7 +901,7 @@ lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type
 	return lb_addr_load(p, res);
 }
 
-lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
+gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type) {
 	// TODO(bill): Handle edge case for f16 types on x86(-64) platforms
 
 	Type *mt = base_type(rhs.type);
@@ -984,7 +988,7 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
 
 
 
-lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
+gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
 	GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
 
 	if (op == Token_Mul && !component_wise) {
@@ -1056,7 +1060,7 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue
 
 
 
-lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
+gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
 	if (is_type_array_like(lhs.type) || is_type_array_like(rhs.type)) {
 		return lb_emit_arith_array(p, op, lhs, rhs, type);
 	} else if (is_type_matrix(lhs.type) || is_type_matrix(rhs.type)) {
@@ -1164,6 +1168,9 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
 		}
 	}
 
+	lhs = lb_emit_conv(p, lhs, type);
+	rhs = lb_emit_conv(p, rhs, type);
+
 	if (is_type_integer(type) && is_type_different_to_arch_endianness(type)) {
 		switch (op) {
 		case Token_AndNot:
@@ -1192,10 +1199,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
 		return lb_emit_byte_swap(p, res, type);
 	}
 
-handle_op:
-	lhs = lb_emit_conv(p, lhs, type);
-	rhs = lb_emit_conv(p, rhs, type);
-
+handle_op:;
 	lbValue res = {};
 	res.type = type;
 
@@ -1325,7 +1329,7 @@ handle_op:
 	return {};
 }
 
-lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
 	ast_node(be, BinaryExpr, expr);
 
 	TypeAndValue tv = type_and_value_of_expr(expr);
@@ -1472,7 +1476,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
 	return {};
 }
 
-lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
+gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	lbModule *m = p->module;
 	t = reduce_tuple_to_single_type(t);
 
@@ -1554,13 +1558,13 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	if (is_type_boolean(src) && dst == t_llvm_bool) {
 		lbValue res = {};
 		res.value = LLVMBuildTrunc(p->builder, value.value, lb_type(m, dst), "");
-		res.type = dst;
+		res.type = t;
 		return res;
 	}
 	if (src == t_llvm_bool && is_type_boolean(dst)) {
 		lbValue res = {};
 		res.value = LLVMBuildZExt(p->builder, value.value, lb_type(m, dst), "");
-		res.type = dst;
+		res.type = t;
 		return res;
 	}
 
@@ -1921,8 +1925,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	}
 
 	if (is_type_union(dst)) {
-		for_array(i, dst->Union.variants) {
-			Type *vt = dst->Union.variants[i];
+		for (Type *vt : dst->Union.variants) {
 			if (are_types_identical(vt, src_type)) {
 				lbAddr parent = lb_add_local_generated(p, t, true);
 				lb_emit_store_union_variant(p, parent.addr, value, vt);
@@ -2159,7 +2162,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		// bit_set <-> integer
 		if (is_type_integer(src) && is_type_bit_set(dst)) {
 			lbValue res = lb_emit_conv(p, value, bit_set_to_int(dst));
-			res.type = dst;
+			res.type = t;
 			return res;
 		}
 		if (is_type_bit_set(src) && is_type_integer(dst)) {
@@ -2202,7 +2205,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	return {};
 }
 
-lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) {
+gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) {
 	GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
 	lbValue left_ptr  = lb_address_from_load_or_generate_local(p, left);
 	lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
@@ -2230,7 +2233,7 @@ lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbVa
 
 
 
-lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
+gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
 	Type *a = core_type(left.type);
 	Type *b = core_type(right.type);
 
@@ -2642,7 +2645,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 
 
 
-lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
+gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 	lbValue res = {};
 	res.type = t_llvm_bool;
 	Type *t = x.type;
@@ -2803,7 +2806,7 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 	return {};
 }
 
-lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
+gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
 	lbAddr v = lb_add_local_generated(p, type, false);
 	lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
 	lbValue idx = lb_emit_struct_ep(p, v.addr, 1);
@@ -2813,7 +2816,7 @@ lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbV
 	return lb_addr_load(p, v);
 }
 
-lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 	ast_node(ue, UnaryExpr, expr);
 	auto tv = type_and_value_of_expr(expr);
 
@@ -3023,8 +3026,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 	return lb_build_addr_ptr(p, ue->expr);
 }
 
-lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr);
-lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr);
+gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	u16 prev_state_flags = p->state_flags;
 	defer (p->state_flags = prev_state_flags);
 
@@ -3080,7 +3083,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 	return res;
 }
 
-lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
 	lbModule *m = p->module;
 
 	expr = unparen_expr(expr);
@@ -3099,6 +3102,9 @@ lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
 
 		// NOTE(bill): Short on constant values
 		return lb_const_value(p->module, type, tv.value);
+	} else if (tv.mode == Addressing_Type) {
+		// NOTE(bill, 2023-01-16): is this correct? I hope so at least
+		return lb_typeid(m, tv.type);
 	}
 
 	switch (expr->kind) {
@@ -3355,10 +3361,10 @@ lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
 	return {};
 }
 
-lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e) {
+gb_internal lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e) {
 	return map_must_get(&p->module->soa_values, e);
 }
-lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
+gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
 	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
 	String name = e->token.string;
 	Entity *parent = e->using_parent;
@@ -3393,7 +3399,7 @@ lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
 
 
 
-lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
+gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
 	GB_ASSERT(e != nullptr);
 	if (e->kind == Entity_Constant) {
 		Type *t = default_type(type_of_expr(expr));
@@ -3427,7 +3433,7 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
 	return lb_addr(v);
 }
 
-lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
+gb_internal lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
 	isize index_count = ce->args.count-1;
 	lbAddr addr = lb_build_addr(p, ce->args[0]);
 	if (index_count == 0) {
@@ -3463,8 +3469,8 @@ lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue
 }
 
 
-lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr);
-lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
+gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr);
+gb_internal lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 	expr = unparen_expr(expr);
 
 	// IMPORTANT NOTE(bill):
@@ -3489,7 +3495,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 	return addr;
 }
 
-void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *> const &elems, Array<lbCompoundLitElemTempData> *temp_data, Type *compound_type) {
+gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *> const &elems, Array<lbCompoundLitElemTempData> *temp_data, Type *compound_type) {
 	Type *bt = base_type(compound_type);
 	Type *et = nullptr;
 	switch (bt->kind) {
@@ -3595,9 +3601,8 @@ void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *> const &ele
 		}
 	}
 }
-void lb_build_addr_compound_lit_assign_array(lbProcedure *p, Array<lbCompoundLitElemTempData> const &temp_data) {
-	for_array(i, temp_data) {
-		auto td = temp_data[i];
+gb_internal void lb_build_addr_compound_lit_assign_array(lbProcedure *p, Array<lbCompoundLitElemTempData> const &temp_data) {
+	for (auto const &td : temp_data) {
 		if (td.value.value != nullptr) {
 			if (td.elem_length > 0) {
 				auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32);
@@ -3614,7 +3619,7 @@ void lb_build_addr_compound_lit_assign_array(lbProcedure *p, Array<lbCompoundLit
 	}
 }
 
-lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
+gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
 	ast_node(ie, IndexExpr, expr);
 
 	Type *t = base_type(type_of_expr(ie->expr));
@@ -3833,7 +3838,7 @@ lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
 }
 
 
-lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
+gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
 	ast_node(se, SliceExpr, expr);
 
 	lbValue low  = lb_const_int(p->module, t_int, 0);
@@ -4031,7 +4036,7 @@ lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
 }
 
 
-lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
+gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 	ast_node(cl, CompoundLit, expr);
 
 	Type *type = type_of_expr(expr);
@@ -4129,8 +4134,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 		lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
 		gb_unused(err);
 
-		for_array(field_index, cl->elems) {
-			Ast *elem = cl->elems[field_index];
+		for (Ast *elem : cl->elems) {
 			ast_node(fv, FieldValue, elem);
 
 			lbValue key   = lb_build_expr(p, fv->field);
@@ -4304,8 +4308,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 			lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
 
 			lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
-			for_array(i, cl->elems) {
-				Ast *elem = cl->elems[i];
+			for (Ast *elem : cl->elems) {
 				GB_ASSERT(elem->kind != Ast_FieldValue);
 
 				if (lb_is_elem_const(elem, et)) {
@@ -4359,8 +4362,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 
 			// TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
 			// might be a better option
-			for_array(i, temp_data) {
-				auto td = temp_data[i];
+			for (auto const &td : temp_data) {
 				if (td.value.value != nullptr) {
 					if (td.elem_length > 0) {
 						for (i64 k = 0; k < td.elem_length; k++) {
@@ -4383,7 +4385,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 }
 
 
-lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
+gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
 	switch (expr->kind) {
 	case_ast_node(i, Implicit, expr);
 		lbAddr v = {};

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 187 - 215
src/llvm_backend_general.cpp


+ 29 - 26
src/llvm_backend_opt.cpp

@@ -32,21 +32,21 @@
 **************************************************************************/
 
 
-void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
-void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
-void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
-void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level);
-
-LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) {
-	lbModule *m = cast(lbModule *)user_data;
-	if (m == nullptr) {
-		return false;
-	}
-	if (value == nullptr) {
-		return false;
-	}
-	return LLVMIsAAllocaInst(value) != nullptr;
-}
+gb_internal void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
+gb_internal void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
+gb_internal void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
+gb_internal void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level);
+
+// gb_internal LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) {
+// 	lbModule *m = cast(lbModule *)user_data;
+// 	if (m == nullptr) {
+// 		return false;
+// 	}
+// 	if (value == nullptr) {
+// 		return false;
+// 	}
+// 	return LLVMIsAAllocaInst(value) != nullptr;
+// }
 
 
 #if LLVM_VERSION_MAJOR < 12
@@ -55,7 +55,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
 #define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) 
 #endif
 
-void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
+gb_internal void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
 	if (false && optimization_level == 0 && build_context.ODIN_DEBUG) {
 		LLVMAddMergedLoadStoreMotionPass(fpm);
 	} else {
@@ -68,7 +68,7 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimiz
 	}
 }
 
-void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
+gb_internal void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
 	// NOTE(bill): Treat -opt:3 as if it was -opt:2
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
@@ -102,7 +102,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
 #endif
 }
 
-void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
+gb_internal void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
 	// NOTE(bill): Treat -opt:3 as if it was -opt:2
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
@@ -141,7 +141,7 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
 #endif
 }
 
-void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level) {
+gb_internal void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level) {
 	LLVMAddCFGSimplificationPass(mpm);
 
 	LLVMAddJumpThreadingPass(mpm);
@@ -177,7 +177,7 @@ void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimizati
 }
 
 
-void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level) {
+gb_internal void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level) {
 
 	// NOTE(bill): Treat -opt:3 as if it was -opt:2
 	// TODO(bill): Determine which opt definitions should exist in the first place
@@ -266,7 +266,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 	optimization of Odin programs	
 **************************************************************************/
 
-void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
+gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
 	isize removal_count = 0;
 	isize pass_count = 0;
 	isize const max_pass_count = 10;
@@ -358,7 +358,10 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
 }
 
 
-void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
+gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
+	if (p == nullptr) {
+		return;
+	}
 	LLVMRunFunctionPassManager(fpm, p->value);
 	// NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM
 	// which means we cannot rely upon it
@@ -367,7 +370,7 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
 	lb_run_remove_dead_instruction_pass(p);
 }
 
-void llvm_delete_function(LLVMValueRef func) {
+gb_internal void llvm_delete_function(LLVMValueRef func) {
 	// for (LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(func); block != nullptr; /**/) {
 	// 	LLVMBasicBlockRef curr_block = block;
 	// 	block = LLVMGetNextBasicBlock(block);
@@ -382,7 +385,7 @@ void llvm_delete_function(LLVMValueRef func) {
 	LLVMDeleteFunction(func);
 }
 
-void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
+gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
 	LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used");
 
 	LLVMValueRef *constants;
@@ -419,7 +422,7 @@ void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
 	LLVMSetInitializer(global, initializer);
 }
 
-void lb_run_remove_unused_function_pass(lbModule *m) {
+gb_internal void lb_run_remove_unused_function_pass(lbModule *m) {
 	isize removal_count = 0;
 	isize pass_count = 0;
 	isize const max_pass_count = 10;
@@ -470,7 +473,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
 }
 
 
-void lb_run_remove_unused_globals_pass(lbModule *m) {
+gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) {
 	isize removal_count = 0;
 	isize pass_count = 0;
 	isize const max_pass_count = 10;

+ 258 - 132
src/llvm_backend_proc.cpp

@@ -1,6 +1,4 @@
-
-LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count)
-{
+gb_internal LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count) {
 	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
 	GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
 	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, type_count);
@@ -8,7 +6,7 @@ LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* a
 	return LLVMBuildCall2(p->builder, call_type, ip, args, arg_count, "");
 }
 
-void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
+gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
 	dst = lb_emit_conv(p, dst, t_rawptr);
 	src = lb_emit_conv(p, src, t_rawptr);
 	len = lb_emit_conv(p, len, t_int);
@@ -37,7 +35,7 @@ void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue l
 
 
 
-void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
+gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
 	dst = lb_emit_conv(p, dst, t_rawptr);
 	src = lb_emit_conv(p, src, t_rawptr);
 	len = lb_emit_conv(p, len, t_int);
@@ -66,17 +64,19 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
 }
 
 
-lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
+gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
 	GB_ASSERT(entity != nullptr);
 	GB_ASSERT(entity->kind == Entity_Procedure);
 	if (!entity->Procedure.is_foreign) {
-		GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
+		if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+			GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
+		}
 	}
 
 	String link_name = {};
 
 	if (ignore_body) {
-		lbModule *other_module = lb_pkg_module(m->gen, entity->pkg);
+		lbModule *other_module = lb_module_of_entity(m->gen, entity);
 		link_name = lb_get_entity_name(other_module, entity);
 	} else {
 		link_name = lb_get_entity_name(m, entity);
@@ -121,8 +121,9 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 	p->branch_blocks.allocator = a;
 	p->context_stack.allocator = a;
 	p->scope_stack.allocator   = a;
-	map_init(&p->selector_values,  a, 0);
-	map_init(&p->selector_addr,    a, 0);
+	map_init(&p->selector_values,  0);
+	map_init(&p->selector_addr,    0);
+	map_init(&p->tuple_fix_map,    0);
 
 	if (p->is_foreign) {
 		lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
@@ -191,11 +192,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 		lb_add_attribute_to_proc(m, p->value, "cold");
 	}
 
-	lbValue proc_value = {p->value, p->type};
-	lb_add_entity(m, entity,  proc_value);
-	lb_add_member(m, p->name, proc_value);
-	lb_add_procedure_value(m, p);
-
 	if (p->is_export) {
 		LLVMSetLinkage(p->value, LLVMDLLExportLinkage);
 		LLVMSetDLLStorageClass(p->value, LLVMDLLExportStorageClass);
@@ -203,7 +199,9 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 
 		lb_set_wasm_export_attributes(p->value, p->name);
 	} else if (!p->is_foreign) {
-		if (!USE_SEPARATE_MODULES) {
+		if (USE_SEPARATE_MODULES) {
+			LLVMSetLinkage(p->value, LLVMExternalLinkage);
+		} else {
 			LLVMSetLinkage(p->value, LLVMInternalLinkage);
 
 			// NOTE(bill): if a procedure is defined in package runtime and uses a custom link name,
@@ -317,10 +315,15 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 		}
 	}
 
+	lbValue proc_value = {p->value, p->type};
+	lb_add_entity(m, entity,  proc_value);
+	lb_add_member(m, p->name, proc_value);
+	lb_add_procedure_value(m, p);
+
 	return p;
 }
 
-lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type) {
+gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type) {
 	{
 		lbValue *found = string_map_get(&m->members, link_name);
 		GB_ASSERT_MSG(found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name));
@@ -346,6 +349,7 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
 	p->blocks.allocator        = a;
 	p->branch_blocks.allocator = a;
 	p->context_stack.allocator = a;
+	map_init(&p->tuple_fix_map, 0);
 
 
 	char *c_link_name = alloc_cstring(permanent_allocator(), p->name);
@@ -383,50 +387,50 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
 }
 
 
-lbValue lb_value_param(lbProcedure *p, Entity *e, Type *abi_type, i32 index, lbParamPasskind *kind_) {
-	lbParamPasskind kind = lbParamPass_Value;
-
-	if (e != nullptr && !are_types_identical(abi_type, e->type)) {
-		if (is_type_pointer(abi_type)) {
-			GB_ASSERT(e->kind == Entity_Variable);
-			Type *av = core_type(type_deref(abi_type));
-			if (are_types_identical(av, core_type(e->type))) {
-				kind = lbParamPass_Pointer;
-				if (e->flags&EntityFlag_Value) {
-					kind = lbParamPass_ConstRef;
-				}
-			} else {
-				kind = lbParamPass_BitCast;
-			}
-		} else if (is_type_integer(abi_type)) {
-			kind = lbParamPass_Integer;
-		} else if (abi_type == t_llvm_bool) {
-			kind = lbParamPass_Value;
-		} else if (is_type_boolean(abi_type)) {
-			kind = lbParamPass_Integer;
-		} else if (is_type_simd_vector(abi_type)) {
-			kind = lbParamPass_BitCast;
-		} else if (is_type_float(abi_type)) {
-			kind = lbParamPass_BitCast;
-		} else if (is_type_tuple(abi_type)) {
-			kind = lbParamPass_Tuple;
-		} else if (is_type_proc(abi_type)) {
-			kind = lbParamPass_Value;
-		} else {
-			GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type));
-		}
-	}
-
-	if (kind_) *kind_ = kind;
-	lbValue res = {};
-	res.value = LLVMGetParam(p->value, cast(unsigned)index);
-	res.type = abi_type;
-	return res;
-}
-
-
-
-void lb_start_block(lbProcedure *p, lbBlock *b) {
+// gb_internal lbValue lb_value_param(lbProcedure *p, Entity *e, Type *abi_type, i32 index, lbParamPasskind *kind_) {
+// 	lbParamPasskind kind = lbParamPass_Value;
+
+// 	if (e != nullptr && !are_types_identical(abi_type, e->type)) {
+// 		if (is_type_pointer(abi_type)) {
+// 			GB_ASSERT(e->kind == Entity_Variable);
+// 			Type *av = core_type(type_deref(abi_type));
+// 			if (are_types_identical(av, core_type(e->type))) {
+// 				kind = lbParamPass_Pointer;
+// 				if (e->flags&EntityFlag_Value) {
+// 					kind = lbParamPass_ConstRef;
+// 				}
+// 			} else {
+// 				kind = lbParamPass_BitCast;
+// 			}
+// 		} else if (is_type_integer(abi_type)) {
+// 			kind = lbParamPass_Integer;
+// 		} else if (abi_type == t_llvm_bool) {
+// 			kind = lbParamPass_Value;
+// 		} else if (is_type_boolean(abi_type)) {
+// 			kind = lbParamPass_Integer;
+// 		} else if (is_type_simd_vector(abi_type)) {
+// 			kind = lbParamPass_BitCast;
+// 		} else if (is_type_float(abi_type)) {
+// 			kind = lbParamPass_BitCast;
+// 		} else if (is_type_tuple(abi_type)) {
+// 			kind = lbParamPass_Tuple;
+// 		} else if (is_type_proc(abi_type)) {
+// 			kind = lbParamPass_Value;
+// 		} else {
+// 			GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type));
+// 		}
+// 	}
+
+// 	if (kind_) *kind_ = kind;
+// 	lbValue res = {};
+// 	res.value = LLVMGetParam(p->value, cast(unsigned)index);
+// 	res.type = abi_type;
+// 	return res;
+// }
+
+
+
+gb_internal void lb_start_block(lbProcedure *p, lbBlock *b) {
 	GB_ASSERT(b != nullptr);
 	if (!b->appended) {
 		b->appended = true;
@@ -436,7 +440,7 @@ void lb_start_block(lbProcedure *p, lbBlock *b) {
 	p->curr_block = b;
 }
 
-void lb_set_debug_position_to_procedure_begin(lbProcedure *p) {
+gb_internal void lb_set_debug_position_to_procedure_begin(lbProcedure *p) {
 	if (p->debug_info == nullptr) {
 		return;
 	}
@@ -453,7 +457,7 @@ void lb_set_debug_position_to_procedure_begin(lbProcedure *p) {
 	}
 }
 
-void lb_set_debug_position_to_procedure_end(lbProcedure *p) {
+gb_internal void lb_set_debug_position_to_procedure_end(lbProcedure *p) {
 	if (p->debug_info == nullptr) {
 		return;
 	}
@@ -470,7 +474,7 @@ void lb_set_debug_position_to_procedure_end(lbProcedure *p) {
 	}
 }
 
-void lb_begin_procedure_body(lbProcedure *p) {
+gb_internal void lb_begin_procedure_body(lbProcedure *p) {
 	DeclInfo *decl = decl_info_of_entity(p->entity);
 	if (decl != nullptr) {
 		for_array(i, decl->labels) {
@@ -486,7 +490,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
 	p->entry_block = lb_create_block(p, "entry", true);
 	lb_start_block(p, p->entry_block);
 
-	map_init(&p->direct_parameters, heap_allocator());
+	map_init(&p->direct_parameters);
 
 	GB_ASSERT(p->type != nullptr);
 
@@ -501,8 +505,23 @@ void lb_begin_procedure_body(lbProcedure *p) {
 			// NOTE(bill): this must be parameter 0
 
 			String name = str_lit("agg.result");
+			if (ft->multiple_return_original_type &&
+			    p->type->Proc.has_named_results) {
+				auto const &variables = p->type->Proc.results->Tuple.variables;
+				Entity *e = variables[variables.count-1];
+				if (!is_blank_ident(e->token)) {
+					name = e->token.string;
+				}
+			}
 
-			Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
+			Type *return_ptr_type = reduce_tuple_to_single_type(p->type->Proc.results);
+			bool split_returns = ft->multiple_return_original_type != nullptr;
+			if (split_returns) {
+				GB_ASSERT(is_type_tuple(return_ptr_type));
+				auto const &variables = return_ptr_type->Tuple.variables;
+				return_ptr_type = variables[variables.count-1]->type;
+			}
+			Type *ptr_type = alloc_type_pointer(return_ptr_type);
 			Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
 			e->flags |= EntityFlag_NoAlias;
 
@@ -580,14 +599,70 @@ void lb_begin_procedure_body(lbProcedure *p) {
 				if (e->token.string != "") {
 					GB_ASSERT(!is_blank_ident(e->token));
 
-					// NOTE(bill): Don't even bother trying to optimize this with the return ptr value
-					// This will violate the defer rules if you do:
-					//         foo :: proc() -> (x, y: T) {
-					//                 defer x = ... // defer is executed after the `defer`
-					//                 return // the values returned should be zeroed
-					//         }
-					// NOTE(bill): REALLY, don't even bother.
-					lbAddr res = lb_add_local(p, e->type, e);
+					lbAddr res = {};
+					if (p->entity && p->entity->decl_info &&
+					    p->entity->decl_info->defer_use_checked &&
+					    p->entity->decl_info->defer_used == 0) {
+
+						// NOTE(bill): this is a bodge to get around the issue of the problem BELOW
+						// We check to see if we ever use a defer statement ever within a procedure and if it
+						// if it never happens, see if you can possibly do take the return value pointer
+						//
+						// NOTE(bill): this could be buggy in that I have missed a case where `defer` was used
+						//
+						// TODO(bill): This could be optimized to check to see where a `defer` only uses
+						// the variable in question
+
+						bool has_return_ptr = p->return_ptr.addr.value != nullptr;
+						lbValue ptr = {};
+
+						if (ft->multiple_return_original_type != nullptr) {
+							isize the_offset = -1;
+							if (i+1 < results->variables.count) {
+								the_offset = cast(isize)param_offset + ft->original_arg_count + i;
+							} else if (has_return_ptr) {
+								GB_ASSERT(i+1 == results->variables.count);
+								the_offset = 0;
+							}
+							if (the_offset >= 0) {
+								lbValue ptr = {};
+								ptr.value = LLVMGetParam(p->value, cast(unsigned)the_offset);
+								ptr.type = alloc_type_pointer(e->type);
+
+
+							}
+						} else if (has_return_ptr) {
+							lbValue ptr = p->return_ptr.addr;
+
+							if (results->variables.count > 1) {
+								ptr = lb_emit_tuple_ep(p, ptr, cast(i32)i);
+							}
+							GB_ASSERT(is_type_pointer(ptr.type));
+							GB_ASSERT(are_types_identical(type_deref(ptr.type), e->type));
+						}
+
+						if (ptr.value != nullptr) {
+							lb_add_entity(p->module, e, ptr);
+							lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
+							// NOTE(bill): no need to zero on the callee side as it is zeroed on the caller side
+
+							res = lb_addr(ptr);
+						}
+					}
+
+					if (res.addr.type == nullptr) {
+						// NOTE(bill): Don't even bother trying to optimize this with the return ptr value
+						// This will violate the defer rules if you do:
+						//         foo :: proc() -> (x, y: T) {
+						//                 defer x = ... // defer is executed after the `defer`
+						//                 return // the values returned should be zeroed
+						//         }
+						// NOTE(bill): REALLY, don't even bother.
+						//
+						// IMPORTANT NOTE(bill): REALLY, don't even bother!!!!!!
+						res = lb_add_local(p, e->type, e);
+					}
+
 					if (e->Variable.param_value.kind != ParameterValue_Invalid) {
 						lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
 						lb_addr_store(p, res, c);
@@ -614,7 +689,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
 	lb_start_block(p, p->entry_block);
 }
 
-void lb_end_procedure_body(lbProcedure *p) {
+gb_internal void lb_end_procedure_body(lbProcedure *p) {
 	lb_set_debug_position_to_procedure_begin(p);
 
 	LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
@@ -648,11 +723,11 @@ void lb_end_procedure_body(lbProcedure *p) {
 	p->curr_block = nullptr;
 	p->state_flags = 0;
 }
-void lb_end_procedure(lbProcedure *p) {
+gb_internal void lb_end_procedure(lbProcedure *p) {
 	LLVMDisposeBuilder(p->builder);
 }
 
-void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
+gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
 	GB_ASSERT(pd->body != nullptr);
 	lbModule *m = p->module;
 	auto *min_dep_set = &m->info->minimum_dependency_set;
@@ -694,21 +769,14 @@ void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
 
 
 
-Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
+gb_internal Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
 	Array<lbValue> array = {};
 	Type *t = base_type(value.type);
 	if (t == nullptr) {
 		// Do nothing
 	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->kind == Type_Tuple);
-		auto *rt = &t->Tuple;
-		if (rt->variables.count > 0) {
-			array = array_make<lbValue>(permanent_allocator(), rt->variables.count);
-			for_array(i, rt->variables) {
-				lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i);
-				array[i] = elem;
-			}
-		}
+		array = array_make<lbValue>(permanent_allocator(), 0, t->Tuple.variables.count);
+		lb_append_tuple_values(p, &array, value);
 	} else {
 		array = array_make<lbValue>(permanent_allocator(), 1);
 		array[0] = value;
@@ -718,7 +786,7 @@ Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
 
 
 
-lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) {
+gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) {
 	GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value)));
 
 	unsigned arg_count = cast(unsigned)processed_args.count;
@@ -734,6 +802,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 	if (return_ptr.value != nullptr) {
 		args[arg_index++] = return_ptr.value;
 	}
+
 	for_array(i, processed_args) {
 		lbValue arg = processed_args[i];
 		if (is_type_proc(arg.type)) {
@@ -741,16 +810,23 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 		}
 		args[arg_index++] = arg.value;
 	}
+
 	if (context_ptr.addr.value != nullptr) {
 		LLVMValueRef cp = context_ptr.addr.value;
 		cp = LLVMBuildPointerCast(p->builder, cp, lb_type(p->module, t_rawptr), "");
 		args[arg_index++] = cp;
 	}
+
+	GB_ASSERT(arg_index == arg_count);
+
 	LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder);
 	GB_ASSERT(curr_block != p->decl_block->block);
 
 	{
-		LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, value.type);
+		Type *proc_type = base_type(value.type);
+		GB_ASSERT(proc_type->kind == Type_Proc);
+
+		LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, proc_type);
 		LLVMTypeRef ftp = LLVMPointerType(fnp, 0);
 		LLVMValueRef fn = value.value;
 		if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) {
@@ -775,10 +851,11 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 				// LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
 				GB_ASSERT_MSG(
 					arg_type == param_type,
-					"Parameter types do not match: %s != %s, argument: %s",
+					"Parameter types do not match: %s != %s, argument: %s\n\t%s",
 					LLVMPrintTypeToString(arg_type),
 					LLVMPrintTypeToString(param_type),
-					LLVMPrintValueToString(args[i])
+					LLVMPrintValueToString(args[i]),
+					LLVMPrintTypeToString(fnp)
 				);
 			}
 		}
@@ -818,20 +895,20 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 }
 
 
-lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) {
+gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) {
 	AstPackage *pkg = m->info->runtime_package;
 	Entity *e = scope_lookup_current(pkg->scope, name);
 	return lb_find_procedure_value_from_entity(m, e);
 }
 
 
-lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) {
+gb_internal lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) {
 	String name = make_string_c(c_name);
 	lbValue proc = lb_lookup_runtime_procedure(p->module, name);
 	return lb_emit_call(p, proc, args);
 }
 
-lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
+gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
 	lbValue res = {};
 	Type *t = val.type;
 	if (is_type_complex(t)) {
@@ -882,7 +959,7 @@ lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
 	return lb_emit_load(p, res);
 }
 
-lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, bool use_copy_elision_hint) {
+gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining) {
 	lbModule *m = p->module;
 
 	Type *pt = base_type(value.type);
@@ -915,8 +992,9 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 
 		bool is_odin_cc = is_calling_convention_odin(pt->Proc.calling_convention);
 
-		lbFunctionType *ft = lb_get_function_type(m, p, pt);
+		lbFunctionType *ft = lb_get_function_type(m, pt);
 		bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
+		bool split_returns = ft->multiple_return_original_type != nullptr;
 
 		unsigned param_index = 0;
 		for (isize i = 0; i < param_count; i++) {
@@ -979,18 +1057,19 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 		}
 
 		Type *rt = reduce_tuple_to_single_type(results);
-		if (return_by_pointer) {
-			lbValue return_ptr = {};
-			if (use_copy_elision_hint && p->copy_elision_hint.ptr.value != nullptr) {
-				if (are_types_identical(type_deref(p->copy_elision_hint.ptr.type), rt)) {
-					return_ptr = lb_consume_copy_elision_hint(p);
-				}
+		Type *original_rt = rt;
+		if (split_returns) {
+			GB_ASSERT(rt->kind == Type_Tuple);
+			for (isize j = 0; j < rt->Tuple.variables.count-1; j++) {
+				Type *partial_return_type = rt->Tuple.variables[j]->type;
+				lbValue partial_return_ptr = lb_add_local(p, partial_return_type, nullptr, true, false).addr;
+				array_add(&processed_args, partial_return_ptr);
 			}
-			if (return_ptr.value == nullptr) {
-				lbAddr r = lb_add_local_generated(p, rt, true);
-				return_ptr = r.addr;
-			}
-			GB_ASSERT(is_type_pointer(return_ptr.type));
+			rt = reduce_tuple_to_single_type(rt->Tuple.variables[rt->Tuple.variables.count-1]->type);
+		}
+
+		if (return_by_pointer) {
+			lbValue return_ptr = lb_add_local_generated(p, rt, true).addr;
 			lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
 			result = lb_emit_load(p, return_ptr);
 		} else if (rt != nullptr) {
@@ -1010,6 +1089,47 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 			lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
 		}
 
+		if (original_rt != rt) {
+			GB_ASSERT(split_returns);
+			GB_ASSERT(is_type_tuple(original_rt));
+
+			// IMPORTANT NOTE(bill, 2022-11-24)
+			// result_ptr is a dummy value which is only used to reference a tuple
+			// value for the "tuple-fix"
+			//
+			// The reason for the fake stack allocation is to have a unique pointer
+			// for the value to be used as a key within the procedure itself
+
+			lbValue result_ptr = lb_add_local_generated(p, original_rt, false).addr;
+			isize ret_count = original_rt->Tuple.variables.count;
+
+			auto tuple_fix_values = slice_make<lbValue>(permanent_allocator(), ret_count);
+			auto tuple_geps = slice_make<lbValue>(permanent_allocator(), ret_count);
+
+			isize offset = ft->original_arg_count;
+			for (isize j = 0; j < ret_count-1; j++) {
+				lbValue ret_arg_ptr = processed_args[offset + j];
+				lbValue ret_arg = lb_emit_load(p, ret_arg_ptr);
+				tuple_fix_values[j] = ret_arg;
+			}
+			tuple_fix_values[ret_count-1] = result;
+
+		#if 0
+			for (isize j = 0; j < ret_count; j++) {
+				tuple_geps[j] = lb_emit_struct_ep(p, result_ptr, cast(i32)j);
+			}
+			for (isize j = 0; j < ret_count; j++) {
+				lb_emit_store(p, tuple_geps[j], tuple_fix_values[j]);
+			}
+		#endif
+
+			result = lb_emit_load(p, result_ptr);
+
+			lbTupleFix tf = {tuple_fix_values};
+			map_set(&p->tuple_fix_map, result_ptr.value, tf);
+			map_set(&p->tuple_fix_map, result.value, tf);
+		}
+
 	}
 
 	Entity **found = map_get(&p->module->procedure_values, value.value);
@@ -1049,15 +1169,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 	return result;
 }
 
-LLVMValueRef llvm_splat_float(i64 count, LLVMTypeRef type, f64 value) {
-	LLVMValueRef v = LLVMConstReal(type, value);
-	LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
-	for (i64 i = 0; i < count; i++) {
-		values[i] = v;
-	}
-	return LLVMConstVector(values, cast(unsigned)count);
-}
-LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) {
+gb_internal LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) {
 	LLVMValueRef v = LLVMConstInt(type, value, is_signed);
 	LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
 	for (i64 i = 0; i < count; i++) {
@@ -1067,7 +1179,7 @@ LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_sign
 }
 
 
-lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) {
+gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) {
 	ast_node(ce, CallExpr, expr);
 
 	lbModule *m = p->module;
@@ -1483,7 +1595,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 }
 
 
-lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
+gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
 	ast_node(ce, CallExpr, expr);
 
 	if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
@@ -2300,7 +2412,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		);
 		LLVMSetWeak(value, weak);
 
-		if (tv.type->kind == Type_Tuple) {
+		if (is_type_tuple(tv.type)) {
 			Type *fix_typed = alloc_type_tuple();
 			slice_init(&fix_typed->Tuple.variables, permanent_allocator(), 2);
 			fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0];
@@ -2834,8 +2946,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 					LLVMTypeRef func_type = lb_get_procedure_raw_type(p->module, type);
 					LLVMValueRef the_asm = llvm_get_inline_asm(
 						func_type,
-						str_lit("rolq $3, %rdi; rolq $13, %rdi\n rolq $61, %rdi; rolq $51, %rdi\n xchgq %rbx, %rbx"),
-						str_lit("={rdx},{rdx},{rax},cc,memory"),
+						str_lit("rolq $$3, %rdi; rolq $$13, %rdi\n rolq $$61, %rdi; rolq $$51, %rdi\n xchgq %rbx, %rbx"),
+						str_lit("={rdx},{rdx},{rax},~{cc},~{memory}"),
 						true
 					);
 
@@ -2863,7 +2975,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 }
 
 
-lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
 	switch (param_value.kind) {
 	case ParameterValue_Constant:
 		if (is_type_constant_type(parameter_type)) {
@@ -2898,9 +3010,9 @@ lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterVal
 }
 
 
-lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr);
+gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr);
 
-lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
 	expr = unparen_expr(expr);
 	ast_node(ce, CallExpr, expr);
 
@@ -2913,7 +3025,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
 	}
 	return res;
 }
-lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 	lbModule *m = p->module;
 
 	TypeAndValue tv = type_and_value_of_expr(expr);
@@ -3015,6 +3127,8 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 				GB_ASSERT(e->kind == Entity_Variable);
 				if (args[i].value == nullptr) {
 					args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos);
+				} else if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) {
+					args[i] = lb_typeid(p->module, args[i].type);
 				} else {
 					args[i] = lb_emit_conv(p, args[i], e->type);
 				}
@@ -3032,7 +3146,7 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 			}
 		}
 
-		return lb_emit_call(p, value, args, ce->inlining, p->copy_elision_hint.ast == expr);
+		return lb_emit_call(p, value, args, ce->inlining);
 	}
 
 	isize arg_index = 0;
@@ -3044,7 +3158,7 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 		GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode);
 		GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
 		Type *at = tav.type;
-		if (at->kind == Type_Tuple) {
+		if (is_type_tuple(at)) {
 			arg_count += at->Tuple.variables.count;
 		} else {
 			arg_count++;
@@ -3084,9 +3198,16 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 			lbValue a = lb_build_expr(p, arg);
 			Type *at = a.type;
 			if (at->kind == Type_Tuple) {
-				for_array(i, at->Tuple.variables) {
-					lbValue v = lb_emit_struct_ev(p, a, cast(i32)i);
-					args[arg_index++] = v;
+				lbTupleFix *tf = map_get(&p->tuple_fix_map, a.value);
+				if (tf) {
+					for_array(j, tf->values) {
+						args[arg_index++] = tf->values[j];
+					}
+				} else {
+					for_array(j, at->Tuple.variables) {
+						lbValue v = lb_emit_struct_ev(p, a, cast(i32)j);
+						args[arg_index++] = v;
+					}
 				}
 			} else {
 				args[arg_index++] = a;
@@ -3157,7 +3278,12 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 						continue;
 					}
 					GB_ASSERT_MSG(args[i].value != nullptr, "%.*s", LIT(e->token.string));
-					args[i] = lb_emit_conv(p, args[i], e->type);
+					if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) {
+						GB_ASSERT(LLVMIsNull(args[i].value));
+						args[i] = lb_typeid(p->module, args[i].type);
+					} else {
+						args[i] = lb_emit_conv(p, args[i], e->type);
+					}
 				}
 			}
 		}
@@ -3213,6 +3339,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 	}
 
 	auto call_args = array_slice(args, 0, final_count);
-	return lb_emit_call(p, value, call_args, ce->inlining, p->copy_elision_hint.ast == expr);
+	return lb_emit_call(p, value, call_args, ce->inlining);
 }
 

+ 180 - 185
src/llvm_backend_stmt.cpp

@@ -1,32 +1,4 @@
-lbCopyElisionHint lb_set_copy_elision_hint(lbProcedure *p, lbAddr const &addr, Ast *ast) {
-	lbCopyElisionHint prev = p->copy_elision_hint;
-	p->copy_elision_hint.used = false;
-	p->copy_elision_hint.ptr = {};
-	p->copy_elision_hint.ast = nullptr;
-	#if 0
-	if (addr.kind == lbAddr_Default && addr.addr.value != nullptr) {
-		p->copy_elision_hint.ptr = lb_addr_get_ptr(p, addr);
-		p->copy_elision_hint.ast = unparen_expr(ast);
-	}
-	#endif
-	return prev;
-}
-
-void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint) {
-	p->copy_elision_hint = prev_hint;
-}
-
-
-lbValue lb_consume_copy_elision_hint(lbProcedure *p) {
-	lbValue return_ptr = p->copy_elision_hint.ptr;
-	p->copy_elision_hint.used = true;
-	p->copy_elision_hint.ptr = {};
-	p->copy_elision_hint.ast = nullptr;
-	return return_ptr;
-}
-
-
-void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
+gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 	if (vd == nullptr || vd->is_mutable) {
 		return;
 	}
@@ -35,8 +7,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 
 	static i32 global_guid = 0;
 
-	for_array(i, vd->names) {
-		Ast *ident = vd->names[i];
+	for (Ast *ident : vd->names) {
 		GB_ASSERT(ident->kind == Ast_Ident);
 		Entity *e = entity_of_node(ident);
 		GB_ASSERT(e != nullptr);
@@ -61,7 +32,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 			continue;
 		}
 
-		lb_set_nested_type_name_ir_mangled_name(e, p);
+		lb_set_nested_type_name_ir_mangled_name(e, p, p->module);
 	}
 
 	for_array(i, vd->names) {
@@ -79,21 +50,20 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 			continue; // It's an alias
 		}
 
-		CheckerInfo *info = p->module->info;
 		DeclInfo *decl = decl_info_of_entity(e);
 		ast_node(pl, ProcLit, decl->proc_lit);
 		if (pl->body != nullptr) {
-			auto *found = map_get(&info->gen_procs, ident);
-			if (found) {
-				auto procs = *found;
-				for_array(i, procs) {
-					Entity *e = procs[i];
+			GenProcsData *gpd = e->Procedure.gen_procs;
+			if (gpd) {
+				rw_mutex_shared_lock(&gpd->mutex);
+				for (Entity *e : gpd->procs) {
 					if (!ptr_set_exists(min_dep_set, e)) {
 						continue;
 					}
 					DeclInfo *d = decl_info_of_entity(e);
 					lb_build_nested_proc(p, &d->proc_lit->ProcLit, e);
 				}
+				rw_mutex_shared_unlock(&gpd->mutex);
 			} else {
 				lb_build_nested_proc(p, pl, e);
 			}
@@ -133,9 +103,8 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 }
 
 
-void lb_build_stmt_list(lbProcedure *p, Slice<Ast *> const &stmts) {
-	for_array(i, stmts) {
-		Ast *stmt = stmts[i];
+gb_internal void lb_build_stmt_list(lbProcedure *p, Slice<Ast *> const &stmts) {
+	for (Ast *stmt : stmts) {
 		switch (stmt->kind) {
 		case_ast_node(vd, ValueDecl, stmt);
 			lb_build_constant_value_decl(p, vd);
@@ -146,21 +115,20 @@ void lb_build_stmt_list(lbProcedure *p, Slice<Ast *> const &stmts) {
 		case_end;
 		}
 	}
-	for_array(i, stmts) {
-		lb_build_stmt(p, stmts[i]);
+	for (Ast *stmt : stmts) {
+		lb_build_stmt(p, stmt);
 	}
 }
 
 
 
-lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
+gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
 	GB_ASSERT(ident->kind == Ast_Ident);
 	Entity *e = entity_of_node(ident);
 	GB_ASSERT(e->kind == Entity_Label);
-	for_array(i, p->branch_blocks) {
-		lbBranchBlocks *b = &p->branch_blocks[i];
-		if (b->label == e->Label.node) {
-			return *b;
+	for (lbBranchBlocks const &b : p->branch_blocks) {
+		if (b.label == e->Label.node) {
+			return b;
 		}
 	}
 
@@ -170,7 +138,7 @@ lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
 }
 
 
-lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
+gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
 	lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
 	tl->prev = p->target_list;
 	tl->break_ = break_;
@@ -181,13 +149,12 @@ lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, l
 	if (label != nullptr) { // Set label blocks
 		GB_ASSERT(label->kind == Ast_Label);
 
-		for_array(i, p->branch_blocks) {
-			lbBranchBlocks *b = &p->branch_blocks[i];
-			GB_ASSERT(b->label != nullptr && label != nullptr);
-			GB_ASSERT(b->label->kind == Ast_Label);
-			if (b->label == label) {
-				b->break_    = break_;
-				b->continue_ = continue_;
+		for (lbBranchBlocks &b : p->branch_blocks) {
+			GB_ASSERT(b.label != nullptr && label != nullptr);
+			GB_ASSERT(b.label->kind == Ast_Label);
+			if (b.label == label) {
+				b.break_    = break_;
+				b.continue_ = continue_;
 				return tl;
 			}
 		}
@@ -198,11 +165,11 @@ lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, l
 	return tl;
 }
 
-void lb_pop_target_list(lbProcedure *p) {
+gb_internal void lb_pop_target_list(lbProcedure *p) {
 	p->target_list = p->target_list->prev;
 }
 
-void lb_open_scope(lbProcedure *p, Scope *s) {
+gb_internal void lb_open_scope(lbProcedure *p, Scope *s) {
 	lbModule *m = p->module;
 	if (m->debug_builder) {
 		LLVMMetadataRef curr_metadata = lb_get_llvm_metadata(m, s);
@@ -239,7 +206,7 @@ void lb_open_scope(lbProcedure *p, Scope *s) {
 
 }
 
-void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool pop_stack=true) {
+gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool pop_stack=true) {
 	lb_emit_defer_stmts(p, kind, block);
 	GB_ASSERT(p->scope_index > 0);
 
@@ -258,7 +225,7 @@ void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool p
 	array_pop(&p->scope_stack);
 }
 
-void lb_build_when_stmt(lbProcedure *p, AstWhenStmt *ws) {
+gb_internal void lb_build_when_stmt(lbProcedure *p, AstWhenStmt *ws) {
 	TypeAndValue tv = type_and_value_of_expr(ws->cond);
 	GB_ASSERT(is_type_boolean(tv.type));
 	GB_ASSERT(tv.value.kind == ExactValue_Bool);
@@ -281,8 +248,8 @@ void lb_build_when_stmt(lbProcedure *p, AstWhenStmt *ws) {
 
 
 
-void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValue count_ptr,
-                            lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValue count_ptr,
+                                        lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
 	lbModule *m = p->module;
 
 	lbValue count = {};
@@ -370,7 +337,7 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
 	if (done_) *done_ = done;
 }
 
-lbValue lb_map_cell_index_static(lbProcedure *p, Type *type, lbValue cells_ptr, lbValue index) {
+gb_internal lbValue lb_map_cell_index_static(lbProcedure *p, Type *type, lbValue cells_ptr, lbValue index) {
 	i64 size, len;
 	i64 elem_sz = type_size_of(type);
 	map_cell_size_and_len(type, &size, &len);
@@ -406,17 +373,7 @@ lbValue lb_map_cell_index_static(lbProcedure *p, Type *type, lbValue cells_ptr,
 	return lb_emit_ptr_offset(p, elems_ptr, data_index);
 }
 
-void lb_map_kvh_data_static(lbProcedure *p, lbValue map_value, lbValue *ks_, lbValue *vs_, lbValue *hs_) {
-	lbValue capacity = lb_map_cap(p, map_value);
-	lbValue ks = lb_map_data_uintptr(p, map_value);
-	lbValue vs = {};
-	lbValue hs = {};
-	if (ks_) *ks_ = ks;
-	if (vs_) *vs_ = vs;
-	if (hs_) *hs_ = hs;
-}
-
-lbValue lb_map_hash_is_valid(lbProcedure *p, lbValue hash) {
+gb_internal lbValue lb_map_hash_is_valid(lbProcedure *p, lbValue hash) {
 	// N :: size_of(uintptr)*8 - 1
 	// (hash != 0) & (hash>>N == 0)
 
@@ -432,8 +389,8 @@ lbValue lb_map_hash_is_valid(lbProcedure *p, lbValue hash) {
 	return lb_emit_arith(p, Token_And, not_deleted, not_empty, t_uintptr);
 }
 
-void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type,
-                        lbValue *val_, lbValue *key_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type,
+                                    lbValue *val_, lbValue *key_, lbBlock **loop_, lbBlock **done_) {
 	lbModule *m = p->module;
 
 	Type *type = base_type(type_deref(expr.type));
@@ -494,8 +451,8 @@ void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type,
 
 
 
-void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
-                            lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
+                                       lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
 	lbModule *m = p->module;
 	lbValue count = lb_const_int(m, t_int, 0);
 	Type *expr_type = base_type(expr.type);
@@ -554,8 +511,8 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
 }
 
 
-void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
-                             AstRangeStmt *rs, Scope *scope) {
+gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
+                                         AstRangeStmt *rs, Scope *scope) {
 	bool ADD_EXTRA_WRAPPING_CHECK = true;
 
 	lbModule *m = p->module;
@@ -658,7 +615,7 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
 	lb_start_block(p, done);
 }
 
-void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
 	lbModule *m = p->module;
 
 	Type *t = enum_type;
@@ -711,8 +668,8 @@ void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValu
 	if (done_) *done_ = done;
 }
 
-void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1_type,
-                          lbValue *val0_, lbValue *val1_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1_type,
+                                      lbValue *val0_, lbValue *val1_, lbBlock **loop_, lbBlock **done_) {
 	lbBlock *loop = lb_create_block(p, "for.tuple.loop");
 	lb_emit_jump(p, loop);
 	lb_start_block(p, loop);
@@ -726,18 +683,18 @@ void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1
 	i32 tuple_count = cast(i32)tuple->Tuple.variables.count;
 	i32 cond_index = tuple_count-1;
 
-	lbValue cond = lb_emit_struct_ev(p, tuple_value, cond_index);
+	lbValue cond = lb_emit_tuple_ev(p, tuple_value, cond_index);
 	lb_emit_if(p, cond, body, done);
 	lb_start_block(p, body);
 
 
-	if (val0_) *val0_ = lb_emit_struct_ev(p, tuple_value, 0);
-	if (val1_) *val1_ = lb_emit_struct_ev(p, tuple_value, 1);
+	if (val0_) *val0_ = lb_emit_tuple_ev(p, tuple_value, 0);
+	if (val1_) *val1_ = lb_emit_tuple_ev(p, tuple_value, 1);
 	if (loop_) *loop_ = loop;
 	if (done_) *done_ = done;
 }
 
-void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
+gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 	Ast *expr = unparen_expr(rs->expr);
 	TypeAndValue tav = type_and_value_of_expr(expr);
 
@@ -806,7 +763,7 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco
 
 }
 
-void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
+gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 	Ast *expr = unparen_expr(rs->expr);
 
 	if (is_ast_range(expr)) {
@@ -951,7 +908,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 	lb_start_block(p, done);
 }
 
-void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *scope) {
+gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *scope) {
 	lbModule *m = p->module;
 
 	lb_open_scope(p, scope); // Open scope here
@@ -1117,7 +1074,7 @@ void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s
 	lb_close_scope(p, lbDeferExit_Default, nullptr);
 }
 
-bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_found_) {
+gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_found_) {
 	if (ss->tag == nullptr) {
 		return false;
 	}
@@ -1133,8 +1090,7 @@ bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_f
 	}
 
 	ast_node(body, BlockStmt, ss->body);
-	for_array(i, body->stmts) {
-		Ast *clause = body->stmts[i];
+	for (Ast *clause : body->stmts) {
 		ast_node(cc, CaseClause, clause);
 
 		if (cc->list.count == 0) {
@@ -1142,8 +1098,8 @@ bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_f
 			continue;
 		}
 
-		for_array(j, cc->list) {
-			Ast *expr = unparen_expr(cc->list[j]);
+		for (Ast *expr : cc->list) {
+			expr = unparen_expr(expr);
 			if (is_ast_range(expr)) {
 				return false;
 			}
@@ -1166,7 +1122,7 @@ bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_f
 }
 
 
-void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
+gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 	lb_open_scope(p, scope);
 
 	if (ss->init != nullptr) {
@@ -1204,8 +1160,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 	LLVMValueRef switch_instr = nullptr;
 	if (is_trivial) {
 		isize num_cases = 0;
-		for_array(i, body->stmts) {
-			Ast *clause = body->stmts[i];
+		for (Ast *clause : body->stmts) {
 			ast_node(cc, CaseClause, clause);
 			num_cases += cc->list.count;
 		}
@@ -1242,8 +1197,8 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 		}
 
 		lbBlock *next_cond = nullptr;
-		for_array(j, cc->list) {
-			Ast *expr = unparen_expr(cc->list[j]);
+		for (Ast *expr : cc->list) {
+			expr = unparen_expr(expr);
 
 			if (switch_instr != nullptr) {
 				lbValue on_val = {};
@@ -1328,7 +1283,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 	lb_close_scope(p, lbDeferExit_Default, done);
 }
 
-void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
 	Entity *e = implicit_entity_of_node(clause);
 	GB_ASSERT(e != nullptr);
 	if (e->flags & EntityFlag_Value) {
@@ -1343,7 +1298,7 @@ void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
 	}
 }
 
-lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value) {
+gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value) {
 	Entity *e = entity_of_node(stmt_val);
 	if (e == nullptr) {
 		return {};
@@ -1363,7 +1318,7 @@ lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value) {
 	return addr;
 }
 
-void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
+gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
 	ast_node(cc, CaseClause, clause);
 
 	lb_push_target_list(p, label, done, nullptr, nullptr);
@@ -1374,7 +1329,7 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l
 	lb_emit_jump(p, done);
 }
 
-void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
+gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
 	lbModule *m = p->module;
 	lb_open_scope(p, ss->scope);
 
@@ -1422,8 +1377,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
 	lbBlock *default_block = nullptr;
 	isize num_cases = 0;
 
-	for_array(i, body->stmts) {
-		Ast *clause = body->stmts[i];
+	for (Ast *clause : body->stmts) {
 		ast_node(cc, CaseClause, clause);
 		num_cases += cc->list.count;
 		if (cc->list.count == 0) {
@@ -1443,8 +1397,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
 		switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases);
 	}
 
-	for_array(i, body->stmts) {
-		Ast *clause = body->stmts[i];
+	for (Ast *clause : body->stmts) {
 		ast_node(cc, CaseClause, clause);
 		lb_open_scope(p, cc->scope);
 		if (cc->list.count == 0) {
@@ -1458,9 +1411,8 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
 		if (p->debug_info != nullptr) {
 			LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause));
 		}
-		Type *case_type = nullptr;
-		for_array(type_index, cc->list) {
-			case_type = type_of_expr(cc->list[type_index]);
+		for (Ast *type_expr : cc->list) {
+			Type *case_type = type_of_expr(type_expr);
 			lbValue on_val = {};
 			if (switch_kind == TypeSwitch_Union) {
 				Type *ut = base_type(type_deref(parent.type));
@@ -1508,7 +1460,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
 }
 
 
-void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
+gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
 	for_array(i, vd->names) {
 		lbValue value = {};
 		if (vd->values.count > 0) {
@@ -1571,35 +1523,48 @@ void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
 		lb_add_member(p->module, mangled_name, global_val);
 	}
 }
+gb_internal void lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_values, lbValue src_value) {
+	Type *t = src_value.type;
+	if (t->kind == Type_Tuple) {
+		lbTupleFix *tf = map_get(&p->tuple_fix_map, src_value.value);
+		if (tf) {
+			for (lbValue const &value : tf->values) {
+				array_add(dst_values, value);
+			}
+		} else {
+			for_array(i, t->Tuple.variables) {
+				lbValue v = lb_emit_tuple_ev(p, src_value, cast(i32)i);
+				array_add(dst_values, v);
+			}
+		}
+	} else {
+		array_add(dst_values, src_value);
+	}
+}
 
 
-void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice<Ast *> const &values) {
+gb_internal void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice<Ast *> const &values) {
 	if (values.count == 0) {
 		return;
 	}
 
 	auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
 
-	for_array(i, values) {
-		Ast *rhs = values[i];
-		if (is_type_tuple(type_of_expr(rhs))) {
-			lbValue init = lb_build_expr(p, rhs);
-			Type *t = init.type;
-			GB_ASSERT(t->kind == Type_Tuple);
-			for_array(i, t->Tuple.variables) {
-				lbValue v = lb_emit_struct_ev(p, init, cast(i32)i);
-				array_add(&inits, v);
-			}
-		} else {
-			auto prev_hint = lb_set_copy_elision_hint(p, lvals[inits.count], rhs);
-			lbValue init = lb_build_expr(p, rhs);
-			if (p->copy_elision_hint.used) {
-				lvals[inits.count] = {}; // zero lval
-			}
-			lb_reset_copy_elision_hint(p, prev_hint);
-			array_add(&inits, init);
+	for (Ast *rhs : values) {
+		lbValue init = lb_build_expr(p, rhs);
+		lb_append_tuple_values(p, &inits, init);
+	}
+
+	bool prev_in_assignment = p->in_multi_assignment;
+
+	isize lval_count = 0;
+	for (lbAddr const &lval : lvals) {
+		if (lval.addr.value != nullptr) {
+			// check if it is not a blank identifier
+			lval_count += 1;
 		}
 	}
+	p->in_multi_assignment = lval_count > 1;
 
 	GB_ASSERT(lvals.count == inits.count);
 	for_array(i, inits) {
@@ -1607,11 +1572,18 @@ void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice<Ast *> cons
 		lbValue init = inits[i];
 		lb_addr_store(p, lval, init);
 	}
+
+	p->in_multi_assignment = prev_in_assignment;
 }
 
-void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res) {
-	lbFunctionType *ft = lb_get_function_type(p->module, p, p->type);
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
+	lbFunctionType *ft = lb_get_function_type(p->module, p->type);
 	bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
+	bool split_returns = ft->multiple_return_original_type != nullptr;
+
+	if (split_returns) {
+		GB_ASSERT(res.value == nullptr || !is_type_tuple(res.type));
+	}
 
 	if (return_by_pointer) {
 		if (res.value != nullptr) {
@@ -1641,7 +1613,7 @@ void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res) {
 		LLVMBuildRet(p->builder, ret_val);
 	}
 }
-void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 	lb_ensure_abi_function_type(p->module, p);
 
 	lbValue res = {};
@@ -1650,7 +1622,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 	isize return_count = p->type->Proc.result_count;
 	isize res_count = return_results.count;
 
-	lbFunctionType *ft = lb_get_function_type(p->module, p, p->type);
+	lbFunctionType *ft = lb_get_function_type(p->module, p->type);
 	bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
 
 	if (return_count == 0) {
@@ -1683,15 +1655,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 		if (res_count != 0) {
 			for (isize res_index = 0; res_index < res_count; res_index++) {
 				lbValue res = lb_build_expr(p, return_results[res_index]);
-				Type *t = res.type;
-				if (t->kind == Type_Tuple) {
-					for_array(i, t->Tuple.variables) {
-						lbValue v = lb_emit_struct_ev(p, res, cast(i32)i);
-						array_add(&results, v);
-					}
-				} else {
-					array_add(&results, res);
-				}
+				lb_append_tuple_values(p, &results, res);
 			}
 		} else {
 			for (isize res_index = 0; res_index < return_count; res_index++) {
@@ -1727,40 +1691,73 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 			}
 		}
 
-		Type *ret_type = p->type->Proc.results;
+		bool split_returns = ft->multiple_return_original_type != nullptr;
+		if (split_returns) {
+			auto result_values = slice_make<lbValue>(temporary_allocator(), results.count);
+			auto result_eps = slice_make<lbValue>(temporary_allocator(), results.count-1);
+
+			for_array(i, results) {
+				result_values[i] = lb_emit_conv(p, results[i], tuple->variables[i]->type);
+			}
+
+			isize param_offset = return_by_pointer ? 1 : 0;
+			param_offset += ft->original_arg_count;
+			for_array(i, result_eps) {
+				lbValue result_ep = {};
+				result_ep.value = LLVMGetParam(p->value, cast(unsigned)(param_offset+i));
+				result_ep.type = alloc_type_pointer(tuple->variables[i]->type);
+				result_eps[i] = result_ep;
+			}
+			for_array(i, result_eps) {
+				lb_emit_store(p, result_eps[i], result_values[i]);
+			}
+			if (return_by_pointer) {
+				GB_ASSERT(result_values.count-1 == result_eps.count);
+				lb_addr_store(p, p->return_ptr, result_values[result_values.count-1]);
+
+				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+				LLVMBuildRetVoid(p->builder);
+				return;
+			} else {
+				return lb_build_return_stmt_internal(p, result_values[result_values.count-1]);
+			}
 
-		// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
-		if (return_by_pointer) {
-			res = p->return_ptr.addr;
 		} else {
-			res = lb_add_local_generated(p, ret_type, false).addr;
-		}
+			Type *ret_type = p->type->Proc.results;
 
-		auto result_values = slice_make<lbValue>(temporary_allocator(), results.count);
-		auto result_eps = slice_make<lbValue>(temporary_allocator(), results.count);
+			// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+			if (return_by_pointer) {
+				res = p->return_ptr.addr;
+			} else {
+				res = lb_add_local_generated(p, ret_type, false).addr;
+			}
 
-		for_array(i, results) {
-			result_values[i] = lb_emit_conv(p, results[i], tuple->variables[i]->type);
-		}
-		for_array(i, results) {
-			result_eps[i] = lb_emit_struct_ep(p, res, cast(i32)i);
-		}
-		for_array(i, result_values) {
-			lb_emit_store(p, result_eps[i], result_values[i]);
-		}
+			auto result_values = slice_make<lbValue>(temporary_allocator(), results.count);
+			auto result_eps = slice_make<lbValue>(temporary_allocator(), results.count);
 
-		if (return_by_pointer) {
-			lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
-			LLVMBuildRetVoid(p->builder);
-			return;
-		}
+			for_array(i, results) {
+				result_values[i] = lb_emit_conv(p, results[i], tuple->variables[i]->type);
+			}
+			for_array(i, results) {
+				result_eps[i] = lb_emit_struct_ep(p, res, cast(i32)i);
+			}
+			for_array(i, result_eps) {
+				lb_emit_store(p, result_eps[i], result_values[i]);
+			}
+
+			if (return_by_pointer) {
+				lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+				LLVMBuildRetVoid(p->builder);
+				return;
+			}
 
-		res = lb_emit_load(p, res);
+			res = lb_emit_load(p, res);
+		}
 	}
 	lb_build_return_stmt_internal(p, res);
 }
 
-void lb_build_if_stmt(lbProcedure *p, Ast *node) {
+gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	ast_node(is, IfStmt, node);
 	lb_open_scope(p, is->scope); // Scope #1
 	defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
@@ -1847,7 +1844,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	lb_start_block(p, done);
 }
 
-void lb_build_for_stmt(lbProcedure *p, Ast *node) {
+gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 	ast_node(fs, ForStmt, node);
 
 	lb_open_scope(p, fs->scope); // Open Scope here
@@ -1907,7 +1904,7 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 	lb_close_scope(p, lbDeferExit_Default, nullptr);
 }
 
-void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
+gb_internal void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
 	GB_ASSERT(op != Token_Eq);
 
 	Type *lhs_type = lb_addr_type(lhs);
@@ -1976,8 +1973,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
 		auto indices_handled = slice_make<bool>(temporary_allocator(), bt->Array.count);
 		auto indices = slice_make<i32>(temporary_allocator(), bt->Array.count);
 		i32 index_count = 0;
-		for_array(i, lhs.swizzle_large.indices) {
-			i32 index = lhs.swizzle_large.indices[i];
+		for (i32 index : lhs.swizzle_large.indices) {
 			if (indices_handled[index]) {
 				continue;
 			}
@@ -2050,12 +2046,11 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
 		lb_loop_end(p, loop_data);
 	}
 }
-void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) {
+gb_internal void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) {
 	if (as->op.kind == Token_Eq) {
 		auto lvals = array_make<lbAddr>(permanent_allocator(), 0, as->lhs.count);
 
-		for_array(i, as->lhs) {
-			Ast *lhs = as->lhs[i];
+		for (Ast *lhs : as->lhs) {
 			lbAddr lval = {};
 			if (!is_blank_ident(lhs)) {
 				lval = lb_build_addr(p, lhs);
@@ -2065,6 +2060,7 @@ void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) {
 		lb_build_assignment(p, lvals, as->rhs);
 		return;
 	}
+
 	GB_ASSERT(as->lhs.count == 1);
 	GB_ASSERT(as->rhs.count == 1);
 	// NOTE(bill): Only 1 += 1 is allowed, no tuples
@@ -2108,7 +2104,7 @@ void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) {
 }
 
 
-void lb_build_stmt(lbProcedure *p, Ast *node) {
+gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
 	Ast *prev_stmt = p->curr_stmt;
 	defer (p->curr_stmt = prev_stmt);
 	p->curr_stmt = node;
@@ -2190,12 +2186,12 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 
 		bool is_static = false;
 		if (vd->names.count > 0) {
-			for_array(i, vd->names) {
-				Ast *name = vd->names[i];
+			for (Ast *name : vd->names) {
 				if (!is_blank_ident(name)) {
+					GB_ASSERT(name->kind == Ast_Ident);
 					Entity *e = entity_of_node(name);
 					TokenPos pos = ast_token(name).pos;
-					GB_ASSERT_MSG(e != nullptr, "%s", token_pos_to_string(pos));
+					GB_ASSERT_MSG(e != nullptr, "\n%s missing entity for %.*s", token_pos_to_string(pos), LIT(name->Ident.token.string));
 					if (e->flags & EntityFlag_Static) {
 						// NOTE(bill): If one of the entities is static, they all are
 						is_static = true;
@@ -2212,8 +2208,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 
 		auto lvals = array_make<lbAddr>(permanent_allocator(), 0, vd->names.count);
 
-		for_array(i, vd->names) {
-			Ast *name = vd->names[i];
+		for (Ast *name : vd->names) {
 			lbAddr lval = {};
 			if (!is_blank_ident(name)) {
 				Entity *e = entity_of_node(name);
@@ -2303,7 +2298,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 
 
 
-void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
+gb_internal void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
 	if (p->curr_block == nullptr) {
 		return;
 	}
@@ -2332,7 +2327,7 @@ void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
 	}
 }
 
-void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
 	isize count = p->defer_stmts.count;
 	isize i = count;
 	while (i --> 0) {
@@ -2340,7 +2335,7 @@ void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
 
 		if (kind == lbDeferExit_Default) {
 			if (p->scope_index == d.scope_index &&
-			    d.scope_index > 0) { // TODO(bill): Which is correct: > 0 or > 1?
+			    d.scope_index > 0) {
 				lb_build_defer_stmt(p, d);
 				array_pop(&p->defer_stmts);
 				continue;
@@ -2359,7 +2354,7 @@ void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
 	}
 }
 
-void lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
+gb_internal void lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
 	Type *pt = base_type(p->type);
 	GB_ASSERT(pt->kind == Type_Proc);
 	if (pt->Proc.calling_convention == ProcCC_Odin) {
@@ -2374,7 +2369,7 @@ void lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
 	d->stmt = stmt;
 }
 
-void lb_add_defer_proc(lbProcedure *p, isize scope_index, lbValue deferred, Array<lbValue> const &result_as_args) {
+gb_internal void lb_add_defer_proc(lbProcedure *p, isize scope_index, lbValue deferred, Array<lbValue> const &result_as_args) {
 	Type *pt = base_type(p->type);
 	GB_ASSERT(pt->kind == Type_Proc);
 	if (pt->Proc.calling_convention == ProcCC_Odin) {

+ 15 - 14
src/llvm_backend_type.cpp

@@ -1,10 +1,11 @@
-isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
+gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
 	auto *set = &info->minimum_dependency_type_info_set;
 	isize index = type_info_index(info, type, err_on_not_found);
 	if (index >= 0) {
-		isize i = ptr_entry_index(set, index);
-		if (i >= 0) {
-			return i+1;
+		auto *found = map_get(set, index);
+		if (found) {
+			GB_ASSERT(*found >= 0);
+			return *found + 1;
 		}
 	}
 	if (err_on_not_found) {
@@ -13,7 +14,7 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
 	return -1;
 }
 
-lbValue lb_typeid(lbModule *m, Type *type) {
+gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
 	GB_ASSERT(!build_context.disallow_rtti);
 
 	type = default_type(type);
@@ -90,7 +91,7 @@ lbValue lb_typeid(lbModule *m, Type *type) {
 	return res;
 }
 
-lbValue lb_type_info(lbModule *m, Type *type) {
+gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
 	GB_ASSERT(!build_context.disallow_rtti);
 
 	type = default_type(type);
@@ -102,36 +103,36 @@ lbValue lb_type_info(lbModule *m, Type *type) {
 	return lb_emit_array_epi(m, data, index);
 }
 
-LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
+gb_internal LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
 	return lb_type_internal_for_procedures_raw(m, type);
 }
 
 
-lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) {
+gb_internal lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) {
 	GB_ASSERT(p->module == &p->module->gen->default_module);
 	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index);
 	lb_global_type_info_member_types_index += cast(i32)count;
 	return offset;
 }
-lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) {
+gb_internal lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) {
 	GB_ASSERT(p->module == &p->module->gen->default_module);
 	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index);
 	lb_global_type_info_member_names_index += cast(i32)count;
 	return offset;
 }
-lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) {
+gb_internal lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) {
 	GB_ASSERT(p->module == &p->module->gen->default_module);
 	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index);
 	lb_global_type_info_member_offsets_index += cast(i32)count;
 	return offset;
 }
-lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) {
+gb_internal lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) {
 	GB_ASSERT(p->module == &p->module->gen->default_module);
 	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index);
 	lb_global_type_info_member_usings_index += cast(i32)count;
 	return offset;
 }
-lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
+gb_internal lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
 	GB_ASSERT(p->module == &p->module->gen->default_module);
 	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index);
 	lb_global_type_info_member_tags_index += cast(i32)count;
@@ -139,7 +140,7 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
 }
 
 
-void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
 	if (build_context.disallow_rtti) {
 		return;
 	}
@@ -185,7 +186,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 		if (entry_index <= 0) {
 			continue;
 		}
-		
+
 		if (entries_handled[entry_index]) {
 			continue;
 		}

+ 180 - 140
src/llvm_backend_utility.cpp

@@ -1,6 +1,6 @@
-lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
+gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
 
-bool lb_is_type_aggregate(Type *t) {
+gb_internal bool lb_is_type_aggregate(Type *t) {
 	t = base_type(t);
 	switch (t->kind) {
 	case Type_Basic:
@@ -39,7 +39,7 @@ bool lb_is_type_aggregate(Type *t) {
 	return false;
 }
 
-void lb_emit_unreachable(lbProcedure *p) {
+gb_internal void lb_emit_unreachable(lbProcedure *p) {
 	LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
 	if (instr == nullptr || !lb_is_instr_terminating(instr)) {
 		lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
@@ -47,7 +47,7 @@ void lb_emit_unreachable(lbProcedure *p) {
 	}
 }
 
-lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
 	Type *src = core_type(value.type);
 	GB_ASSERT(is_type_integer(src) || is_type_float(src));
 	if (is_type_different_to_arch_endianness(src)) {
@@ -57,7 +57,7 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
 	return value;
 }
 
-LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
+gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
 	bool is_inlinable = false;
 
 	i64 const_len = 0;
@@ -103,7 +103,7 @@ LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValu
 
 }
 
-void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) {
+gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) {
 	LLVMTypeRef llvm_type = lb_type(p->module, type);
 
 	LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
@@ -123,7 +123,7 @@ void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alig
 	}
 }
 
-lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y) {
+gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y) {
 	cond = lb_emit_conv(p, cond, t_llvm_bool);
 	lbValue res = {};
 	res.value = LLVMBuildSelect(p->builder, cond.value, x.value, y.value, "");
@@ -131,19 +131,19 @@ lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y) {
 	return res;
 }
 
-lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
+gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
 	x = lb_emit_conv(p, x, t);
 	y = lb_emit_conv(p, y, t);
 	return lb_emit_select(p, lb_emit_comp(p, Token_Lt, x, y), x, y);
 }
-lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
+gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
 	x = lb_emit_conv(p, x, t);
 	y = lb_emit_conv(p, y, t);
 	return lb_emit_select(p, lb_emit_comp(p, Token_Gt, x, y), x, y);
 }
 
 
-lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue min, lbValue max) {
+gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue min, lbValue max) {
 	lbValue z = {};
 	z = lb_emit_max(p, t, x, min);
 	z = lb_emit_min(p, t, z, max);
@@ -152,7 +152,7 @@ lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue min, lbValue m
 
 
 
-lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
+gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
 	if (false && lb_is_const(str_elem) && lb_is_const(str_len)) {
 		LLVMValueRef values[2] = {
 			str_elem.value,
@@ -171,7 +171,7 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
 }
 
 
-lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
+gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
 	Type *src_type = value.type;
 	if (are_types_identical(t, src_type)) {
 		return value;
@@ -259,7 +259,7 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
 	return res;
 }
 
-lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 alignment) {
+gb_internal lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 alignment) {
 	i64 type_alignment = type_align_of(new_type);
 	if (alignment < type_alignment) {
 		alignment = type_alignment;
@@ -274,7 +274,7 @@ lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 al
 }
 
 
-lbValue lb_soa_zip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
+gb_internal lbValue lb_soa_zip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
 	GB_ASSERT(ce->args.count > 0);
 
 	auto slices = slice_make<lbValue>(temporary_allocator(), ce->args.count);
@@ -305,7 +305,7 @@ lbValue lb_soa_zip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
 	return lb_addr_load(p, res);
 }
 
-lbValue lb_soa_unzip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
+gb_internal lbValue lb_soa_unzip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
 	GB_ASSERT(ce->args.count == 1);
 
 	lbValue arg = lb_build_expr(p, ce->args[0]);
@@ -331,7 +331,7 @@ lbValue lb_soa_unzip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
 	return lb_addr_load(p, res);
 }
 
-void lb_emit_try_lhs_rhs(lbProcedure *p, Ast *arg, TypeAndValue const &tv, lbValue *lhs_, lbValue *rhs_) {
+gb_internal void lb_emit_try_lhs_rhs(lbProcedure *p, Ast *arg, TypeAndValue const &tv, lbValue *lhs_, lbValue *rhs_) {
 	lbValue lhs = {};
 	lbValue rhs = {};
 
@@ -339,16 +339,16 @@ void lb_emit_try_lhs_rhs(lbProcedure *p, Ast *arg, TypeAndValue const &tv, lbVal
 	if (is_type_tuple(value.type)) {
 		i32 n = cast(i32)(value.type->Tuple.variables.count-1);
 		if (value.type->Tuple.variables.count == 2) {
-			lhs = lb_emit_struct_ev(p, value, 0);
+			lhs = lb_emit_tuple_ev(p, value, 0);
 		} else {
 			lbAddr lhs_addr = lb_add_local_generated(p, tv.type, false);
 			lbValue lhs_ptr = lb_addr_get_ptr(p, lhs_addr);
 			for (i32 i = 0; i < n; i++) {
-				lb_emit_store(p, lb_emit_struct_ep(p, lhs_ptr, i), lb_emit_struct_ev(p, value, i));
+				lb_emit_store(p, lb_emit_struct_ep(p, lhs_ptr, i), lb_emit_tuple_ev(p, value, i));
 			}
 			lhs = lb_addr_load(p, lhs_addr);
 		}
-		rhs = lb_emit_struct_ev(p, value, n);
+		rhs = lb_emit_tuple_ev(p, value, n);
 	} else {
 		rhs = value;
 	}
@@ -360,7 +360,7 @@ void lb_emit_try_lhs_rhs(lbProcedure *p, Ast *arg, TypeAndValue const &tv, lbVal
 }
 
 
-lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) {
+gb_internal lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) {
 	lbValue has_value = {};
 	if (is_type_boolean(rhs.type)) {
 		has_value = rhs;
@@ -373,7 +373,7 @@ lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) {
 }
 
 
-lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue const &tv) {
+gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue const &tv) {
 	if (arg->state_flags & StateFlag_DirectiveWasFalse) {
 		return lb_build_expr(p, else_expr);
 	}
@@ -435,10 +435,10 @@ lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue c
 	}
 }
 
-void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
-void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res);
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res);
 
-lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
+gb_internal lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
 	lbValue lhs = {};
 	lbValue rhs = {};
 	lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
@@ -479,7 +479,7 @@ lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
 }
 
 
-void lb_emit_increment(lbProcedure *p, lbValue addr) {
+gb_internal void lb_emit_increment(lbProcedure *p, lbValue addr) {
 	GB_ASSERT(is_type_pointer(addr.type));
 	Type *type = type_deref(addr.type);
 	lbValue v_one = lb_const_value(p->module, type, exact_value_i64(1));
@@ -487,7 +487,7 @@ void lb_emit_increment(lbProcedure *p, lbValue addr) {
 
 }
 
-lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
+gb_internal lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
 	GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
 
 	if (type_size_of(value.type) < 2) {
@@ -526,7 +526,7 @@ lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
 
 
 
-lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
 	x = lb_emit_conv(p, x, type);
 
 	char const *name = "llvm.ctpop";
@@ -539,7 +539,7 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
 	return res;
 }
 
-lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
 	Type *elem = base_array_type(type);
 	i64 sz = 8*type_size_of(elem);
 	lbValue size = lb_const_int(p->module, elem, cast(u64)sz);
@@ -550,7 +550,7 @@ lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
 
 
 
-lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) {
 	x = lb_emit_conv(p, x, type);
 
 	char const *name = "llvm.cttz";
@@ -566,7 +566,7 @@ lbValue lb_emit_count_trailing_zeros(lbProcedure *p, lbValue x, Type *type) {
 	return res;
 }
 
-lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type) {
 	x = lb_emit_conv(p, x, type);
 
 	char const *name = "llvm.ctlz";
@@ -584,7 +584,7 @@ lbValue lb_emit_count_leading_zeros(lbProcedure *p, lbValue x, Type *type) {
 
 
 
-lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) {
+gb_internal lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) {
 	x = lb_emit_conv(p, x, type);
 
 	char const *name = "llvm.bitreverse";
@@ -599,15 +599,7 @@ lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type) {
 }
 
 
-lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x) {
-	GB_ASSERT(is_type_bit_set(x.type));
-	Type *underlying = bit_set_to_int(x.type);
-	lbValue card = lb_emit_count_ones(p, x, underlying);
-	return lb_emit_conv(p, card, t_int);
-}
-
-
-lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+gb_internal lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
 	GB_ASSERT(is_type_tuple(type));
 	lbModule *m = p->module;
 
@@ -654,7 +646,7 @@ lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *ty
 	return lb_addr_load(p, v);
 }
 
-lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
 	lbModule *m = p->module;
 
 	Type *src_type = value.type;
@@ -753,7 +745,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
 	return lb_addr_load(p, v);
 }
 
-lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
 	lbModule *m = p->module;
 
 	Type *src_type = value.type;
@@ -826,13 +818,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
 	}
 	return v;
 }
-lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+gb_internal lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
 	return lb_addr_load(p, lb_emit_any_cast_addr(p, value, type, pos));
 }
 
 
 
-lbAddr lb_find_or_generate_context_ptr(lbProcedure *p) {
+gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p) {
 	if (p->context_stack.count > 0) {
 		return p->context_stack[p->context_stack.count-1].ctx;
 	}
@@ -850,7 +842,7 @@ lbAddr lb_find_or_generate_context_ptr(lbProcedure *p) {
 	return c;
 }
 
-lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value) {
 	if (LLVMIsALoadInst(value.value)) {
 		lbValue res = {};
 		res.value = LLVMGetOperand(value.value, 0);
@@ -864,7 +856,7 @@ lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value) {
 	lb_addr_store(p, res, value);
 	return res.addr;
 }
-lbValue lb_address_from_load(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_address_from_load(lbProcedure *p, lbValue value) {
 	if (LLVMIsALoadInst(value.value)) {
 		lbValue res = {};
 		res.value = LLVMGetOperand(value.value, 0);
@@ -877,7 +869,7 @@ lbValue lb_address_from_load(lbProcedure *p, lbValue value) {
 }
 
 
-lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) {
+gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) {
 	t = base_type(t);
 	LLVMTypeRef struct_type = lb_type(m, t);
 	auto *field_remapping = map_get(&m->struct_field_remapping, cast(void *)struct_type);
@@ -888,7 +880,7 @@ lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) {
 	return *field_remapping;
 }
 
-i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
+gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
 	if (t->kind == Type_Struct) {
 		auto field_remapping = lb_get_struct_remapping(m, t);
 		index = field_remapping[index];
@@ -896,7 +888,7 @@ i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
 	return index;
 }
 
-LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align) {
+gb_internal LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align) {
 	// NOTE(bill): limit to `[N x u64]` to prevent ABI issues
 	padding_align = gb_clamp(padding_align, 1, 8);
 	if (padding % padding_align == 0) {
@@ -921,7 +913,7 @@ LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align)
 }
 
 
-char const *llvm_type_kinds[] = {
+gb_global char const *llvm_type_kinds[] = {
 	"LLVMVoidTypeKind",
 	"LLVMHalfTypeKind",
 	"LLVMFloatTypeKind",
@@ -943,7 +935,55 @@ char const *llvm_type_kinds[] = {
 	"LLVMBFloatTypeKind",
 };
 
-lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
+gb_internal lbValue lb_emit_struct_ep_internal(lbProcedure *p, lbValue s, i32 index, Type *result_type) {
+	Type *t = base_type(type_deref(s.type));
+
+	i32 original_index = index;
+	index = lb_convert_struct_index(p->module, t, index);
+
+	if (lb_is_const(s)) {
+		// NOTE(bill): this cannot be replaced with lb_emit_epi
+		lbModule *m = p->module;
+		lbValue res = {};
+		LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
+		res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
+		res.type = alloc_type_pointer(result_type);
+		return res;
+	} else {
+		lbValue res = {};
+		LLVMTypeRef st = lb_type(p->module, type_deref(s.type));
+		// gb_printf_err("%s\n", type_to_string(s.type));
+		// gb_printf_err("%s\n", LLVMPrintTypeToString(LLVMTypeOf(s.value)));
+		// gb_printf_err("%d\n", index);
+		GB_ASSERT_MSG(LLVMGetTypeKind(st) == LLVMStructTypeKind, "%s", llvm_type_kinds[LLVMGetTypeKind(st)]);
+		unsigned count = LLVMCountStructElementTypes(st);
+		GB_ASSERT_MSG(count >= cast(unsigned)index, "%u %d %d", count, index, original_index);
+
+		res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
+		res.type = alloc_type_pointer(result_type);
+		return res;
+	}
+}
+
+gb_internal lbValue lb_emit_tuple_ep(lbProcedure *p, lbValue ptr, i32 index) {
+	Type *t = type_deref(ptr.type);
+	GB_ASSERT(is_type_tuple(t));
+	Type *result_type = t->Tuple.variables[index]->type;
+
+	lbValue res = {};
+	lbTupleFix *tf = map_get(&p->tuple_fix_map, ptr.value);
+	if (tf) {
+		res = tf->values[index];
+		GB_ASSERT(are_types_identical(res.type, result_type));
+		res = lb_address_from_load_or_generate_local(p, res);
+	} else {
+		res = lb_emit_struct_ep_internal(p, ptr, index, result_type);
+	}
+	return res;
+}
+
+
+gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 	GB_ASSERT(is_type_pointer(s.type));
 	Type *t = base_type(type_deref(s.type));
 	Type *result_type = nullptr;
@@ -958,8 +998,7 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 		GB_ASSERT(index == -1);
 		return lb_emit_union_tag_ptr(p, s);
 	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->Tuple.variables.count > 0);
-		result_type = t->Tuple.variables[index]->type;
+		return lb_emit_tuple_ep(p, s, index);
 	} else if (is_type_complex(t)) {
 		Type *ft = base_complex_elem_type(t);
 		switch (index) {
@@ -1024,34 +1063,45 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 
 	GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
 	
-	i32 original_index = index;
-	index = lb_convert_struct_index(p->module, t, index);
-	
-	if (lb_is_const(s)) {
-		// NOTE(bill): this cannot be replaced with lb_emit_epi
-		lbModule *m = p->module;
-		lbValue res = {};
-		LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
-		res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
-		res.type = alloc_type_pointer(result_type);
-		return res;
+	return lb_emit_struct_ep_internal(p, s, index, result_type);
+}
+
+gb_internal lbValue lb_emit_tuple_ev(lbProcedure *p, lbValue value, i32 index) {
+	Type *t = value.type;
+	GB_ASSERT(is_type_tuple(t));
+	Type *result_type = t->Tuple.variables[index]->type;
+
+	lbValue res = {};
+	lbTupleFix *tf = map_get(&p->tuple_fix_map, value.value);
+	if (tf) {
+		res = tf->values[index];
+		GB_ASSERT(are_types_identical(res.type, result_type));
 	} else {
-		lbValue res = {};
-		LLVMTypeRef st = lb_type(p->module, type_deref(s.type));
-		// gb_printf_err("%s\n", type_to_string(s.type));
-		// gb_printf_err("%s\n", LLVMPrintTypeToString(LLVMTypeOf(s.value)));
-		// gb_printf_err("%d\n", index);
-		GB_ASSERT_MSG(LLVMGetTypeKind(st) == LLVMStructTypeKind, "%s", llvm_type_kinds[LLVMGetTypeKind(st)]);
-		unsigned count = LLVMCountStructElementTypes(st);
-		GB_ASSERT_MSG(count >= cast(unsigned)index, "%u %d %d", count, index, original_index);
-		
-		res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
-		res.type = alloc_type_pointer(result_type);
-		return res;
+		if (t->Tuple.variables.count == 1) {
+			GB_ASSERT(index == 0);
+			// value.type = result_type;
+			return value;
+		}
+		if (LLVMIsALoadInst(value.value)) {
+			lbValue res = {};
+			res.value = LLVMGetOperand(value.value, 0);
+			res.type = alloc_type_pointer(value.type);
+			lbValue ptr = lb_emit_struct_ep(p, res, index);
+			return lb_emit_load(p, ptr);
+		}
+
+		res.value = LLVMBuildExtractValue(p->builder, value.value, cast(unsigned)index, "");
+		res.type = result_type;
 	}
+	return res;
 }
 
-lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
+gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
+	Type *t = base_type(s.type);
+	if (is_type_tuple(t)) {
+		return lb_emit_tuple_ev(p, s, index);
+	}
+
 	if (LLVMIsALoadInst(s.value)) {
 		lbValue res = {};
 		res.value = LLVMGetOperand(s.value, 0);
@@ -1060,7 +1110,6 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 		return lb_emit_load(p, ptr);
 	}
 
-	Type *t = base_type(s.type);
 	Type *result_type = nullptr;
 
 	switch (t->kind) {
@@ -1113,12 +1162,7 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 		GB_PANIC("lb_emit_union_tag_value");
 
 	case Type_Tuple:
-		GB_ASSERT(t->Tuple.variables.count > 0);
-		result_type = t->Tuple.variables[index]->type;
-		if (t->Tuple.variables.count == 1) {
-			return s;
-		}
-		break;
+		return lb_emit_tuple_ev(p, s, index);
 	case Type_Slice:
 		switch (index) {
 		case 0: result_type = alloc_type_pointer(t->Slice.elem); break;
@@ -1171,7 +1215,7 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 	return res;
 }
 
-lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
+gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
 	GB_ASSERT(sel.index.count > 0);
 	Type *type = type_deref(e.type);
 
@@ -1259,14 +1303,14 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
 }
 
 
-lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel) {
+gb_internal lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel) {
 	lbValue ptr = lb_address_from_load_or_generate_local(p, e);
 	lbValue res = lb_emit_deep_field_gep(p, ptr, sel);
 	return lb_emit_load(p, res);
 }
 
 
-lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
+gb_internal lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
 	Type *t = s.type;
 	GB_ASSERT_MSG(is_type_pointer(t), "%s", type_to_string(t));
 	Type *st = base_type(type_deref(t));
@@ -1289,7 +1333,7 @@ lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
 	return res;
 }
 
-lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
+gb_internal lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
 	Type *t = s.type;
 	GB_ASSERT(is_type_pointer(t));
 	Type *st = base_type(type_deref(t));
@@ -1297,7 +1341,7 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
 	GB_ASSERT(0 <= index);
 	return lb_emit_epi(p, s, index);
 }
-lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) {
+gb_internal lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) {
 	Type *t = s.type;
 	GB_ASSERT(is_type_pointer(t));
 	Type *st = base_type(type_deref(t));
@@ -1306,7 +1350,7 @@ lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) {
 	return lb_emit_epi(m, s, index);
 }
 
-lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
+gb_internal lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
 	index = lb_emit_conv(p, index, t_int);
 	LLVMValueRef indices[1] = {index.value};
 	lbValue res = {};
@@ -1321,7 +1365,7 @@ lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
 	return res;
 }
 
-lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column) {
+gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column) {
 	Type *t = s.type;
 	GB_ASSERT(is_type_pointer(t));
 	Type *mt = base_type(type_deref(t));
@@ -1339,7 +1383,7 @@ lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column) {
 	return lb_emit_epi(p, s, offset);
 }
 
-lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column) {
+gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column) {
 	Type *t = s.type;
 	GB_ASSERT(is_type_pointer(t));
 	Type *mt = base_type(type_deref(t));
@@ -1371,7 +1415,7 @@ lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column
 }
 
 
-lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column) {
+gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column) {
 	Type *st = base_type(s.type);
 	GB_ASSERT_MSG(is_type_matrix(st), "%s", type_to_string(st));
 	
@@ -1381,14 +1425,14 @@ lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column) {
 }
 
 
-void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len) {
+gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len) {
 	Type *t = lb_addr_type(slice);
 	GB_ASSERT(is_type_slice(t));
 	lbValue ptr = lb_addr_get_ptr(p, slice);
 	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 0), base_elem);
 	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
 }
-void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue base_elem, lbValue len) {
+gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue base_elem, lbValue len) {
 	Type *t = lb_addr_type(string);
 	GB_ASSERT(is_type_string(t));
 	lbValue ptr = lb_addr_get_ptr(p, string);
@@ -1396,18 +1440,18 @@ void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue base_elem, lbV
 	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
 }
 
-lbValue lb_string_elem(lbProcedure *p, lbValue string) {
+gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) {
 	Type *t = base_type(string.type);
 	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
 	return lb_emit_struct_ev(p, string, 0);
 }
-lbValue lb_string_len(lbProcedure *p, lbValue string) {
+gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string) {
 	Type *t = base_type(string.type);
 	GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
 	return lb_emit_struct_ev(p, string, 1);
 }
 
-lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
 	GB_ASSERT(is_type_cstring(value.type));
 	auto args = array_make<lbValue>(permanent_allocator(), 1);
 	args[0] = lb_emit_conv(p, value, t_cstring);
@@ -1415,43 +1459,39 @@ lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
 }
 
 
-lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) {
+gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) {
 	Type *t = type_deref(array_ptr.type);
 	GB_ASSERT(is_type_array(t));
 	return lb_emit_struct_ep(p, array_ptr, 0);
 }
 
-lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
+gb_internal lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
 	GB_ASSERT(is_type_slice(slice.type));
 	return lb_emit_struct_ev(p, slice, 0);
 }
-lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
+gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
 	GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
 	return lb_emit_struct_ev(p, slice, 1);
 }
-lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
+gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
 	GB_ASSERT(is_type_dynamic_array(da.type));
 	return lb_emit_struct_ev(p, da, 0);
 }
-lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da) {
+gb_internal lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da) {
 	GB_ASSERT(is_type_dynamic_array(da.type));
 	return lb_emit_struct_ev(p, da, 1);
 }
-lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da) {
+gb_internal lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da) {
 	GB_ASSERT(is_type_dynamic_array(da.type));
 	return lb_emit_struct_ev(p, da, 2);
 }
-lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) {
-	GB_ASSERT(is_type_dynamic_array(da.type));
-	return lb_emit_struct_ev(p, da, 3);
-}
 
-lbValue lb_map_len(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_map_len(lbProcedure *p, lbValue value) {
 	GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
 	lbValue len = lb_emit_struct_ev(p, value, 1);
 	return lb_emit_conv(p, len, t_int);
 }
-lbValue lb_map_len_ptr(lbProcedure *p, lbValue map_ptr) {
+gb_internal lbValue lb_map_len_ptr(lbProcedure *p, lbValue map_ptr) {
 	Type *type = map_ptr.type;
 	GB_ASSERT(is_type_pointer(type));
 	type = type_deref(type);
@@ -1459,7 +1499,7 @@ lbValue lb_map_len_ptr(lbProcedure *p, lbValue map_ptr) {
 	return lb_emit_struct_ep(p, map_ptr, 1);
 }
 
-lbValue lb_map_cap(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_map_cap(lbProcedure *p, lbValue value) {
 	GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
 	lbValue zero = lb_const_int(p->module, t_uintptr, 0);
 	lbValue one = lb_const_int(p->module, t_uintptr, 1);
@@ -1473,7 +1513,7 @@ lbValue lb_map_cap(lbProcedure *p, lbValue value) {
 	return lb_emit_conv(p, lb_emit_select(p, cmp, zero, cap), t_int);
 }
 
-lbValue lb_map_data_uintptr(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_map_data_uintptr(lbProcedure *p, lbValue value) {
 	GB_ASSERT(is_type_map(value.type) || are_types_identical(value.type, t_raw_map));
 	lbValue data = lb_emit_struct_ev(p, value, 0);
 	u64 mask_value = 0;
@@ -1487,7 +1527,7 @@ lbValue lb_map_data_uintptr(lbProcedure *p, lbValue value) {
 }
 
 
-lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
 	Type *t = base_type(value.type);
 	bool is_ptr = false;
 	if (is_type_pointer(t)) {
@@ -1520,7 +1560,7 @@ lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
 	return lb_emit_struct_ev(p, value, cast(i32)n);
 }
 
-lbValue lb_soa_struct_cap(lbProcedure *p, lbValue value) {
+gb_internal lbValue lb_soa_struct_cap(lbProcedure *p, lbValue value) {
 	Type *t = base_type(value.type);
 
 	bool is_ptr = false;
@@ -1552,7 +1592,7 @@ lbValue lb_soa_struct_cap(lbProcedure *p, lbValue value) {
 	return lb_emit_struct_ev(p, value, cast(i32)n);
 }
 
-lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t) {
+gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t) {
 	lbModule *m = p->module;
 	
 	a = lb_emit_conv(p, a, t);
@@ -1595,7 +1635,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
 	}
 }
 
-LLVMValueRef llvm_mask_iota(lbModule *m, unsigned start, unsigned count) {
+gb_internal LLVMValueRef llvm_mask_iota(lbModule *m, unsigned start, unsigned count) {
 	auto iota = slice_make<LLVMValueRef>(temporary_allocator(), count);
 	for (unsigned i = 0; i < count; i++) {
 		iota[i] = lb_const_int(m, t_u32, start+i).value;
@@ -1603,7 +1643,7 @@ LLVMValueRef llvm_mask_iota(lbModule *m, unsigned start, unsigned count) {
 	return LLVMConstVector(iota.data, count);
 }
 
-LLVMValueRef llvm_mask_zero(lbModule *m, unsigned count) {
+gb_internal LLVMValueRef llvm_mask_zero(lbModule *m, unsigned count) {
 	return LLVMConstNull(LLVMVectorType(lb_type(m, t_u32), count));
 }
 
@@ -1611,16 +1651,16 @@ LLVMValueRef llvm_mask_zero(lbModule *m, unsigned count) {
 // #define LLVM_VECTOR_DUMMY_VALUE(type) LLVMConstNull((type))
 
 
-LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask) {
+gb_internal LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask) {
 	return LLVMBuildShuffleVector(p->builder, vector, LLVM_VECTOR_DUMMY_VALUE(LLVMTypeOf(vector)), mask, "");
 }
-LLVMValueRef llvm_basic_const_shuffle(LLVMValueRef vector, LLVMValueRef mask) {
+gb_internal LLVMValueRef llvm_basic_const_shuffle(LLVMValueRef vector, LLVMValueRef mask) {
 	return LLVMConstShuffleVector(vector, LLVM_VECTOR_DUMMY_VALUE(LLVMTypeOf(vector)), mask);
 }
 
 
 
-LLVMValueRef llvm_vector_broadcast(lbProcedure *p, LLVMValueRef value, unsigned count) {
+gb_internal LLVMValueRef llvm_vector_broadcast(lbProcedure *p, LLVMValueRef value, unsigned count) {
 	GB_ASSERT(count > 0);
 	if (LLVMIsConstant(value)) {
 		LLVMValueRef single = LLVMConstVector(&value, 1);
@@ -1640,7 +1680,7 @@ LLVMValueRef llvm_vector_broadcast(lbProcedure *p, LLVMValueRef value, unsigned
 	return llvm_basic_shuffle(p, single, mask);
 }
 
-LLVMValueRef llvm_vector_shuffle_reduction(lbProcedure *p, LLVMValueRef value, LLVMOpcode op_code) {
+gb_internal LLVMValueRef llvm_vector_shuffle_reduction(lbProcedure *p, LLVMValueRef value, LLVMOpcode op_code) {
 	LLVMTypeRef original_vector_type = LLVMTypeOf(value);
 	
 	GB_ASSERT(LLVMGetTypeKind(original_vector_type) == LLVMVectorTypeKind);
@@ -1667,7 +1707,7 @@ LLVMValueRef llvm_vector_shuffle_reduction(lbProcedure *p, LLVMValueRef value, L
 	return LLVMBuildExtractElement(p->builder, value, v_zero32, "");
 }
 
-LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef value) {
+gb_internal LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef value) {
 	LLVMTypeRef vector_type = LLVMTypeOf(value);
 	unsigned len = LLVMGetVectorSize(vector_type);
 	if (len == 1) {
@@ -1682,7 +1722,7 @@ LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef val
 	return LLVMBuildShuffleVector(p->builder, value, LLVMConstNull(vector_type), mask, "");
 }
 
-LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
+gb_internal LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
 	LLVMTypeRef type = LLVMTypeOf(value);
 	GB_ASSERT(LLVMGetTypeKind(type) == LLVMVectorTypeKind);
 	LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
@@ -1758,7 +1798,7 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
 #endif
 }
 
-LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
+gb_internal LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
 	GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
 	
 	LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
@@ -1769,7 +1809,7 @@ LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
 	return LLVMBuildFAdd(p->builder, a, b, "");
 }
 
-LLVMValueRef llvm_vector_mul(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
+gb_internal LLVMValueRef llvm_vector_mul(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
 	GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
 	
 	LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
@@ -1781,11 +1821,11 @@ LLVMValueRef llvm_vector_mul(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
 }
 
 
-LLVMValueRef llvm_vector_dot(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
+gb_internal LLVMValueRef llvm_vector_dot(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
 	return llvm_vector_reduce_add(p, llvm_vector_mul(p, a, b));
 }
 
-LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) {
+gb_internal LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) {
 
 	LLVMTypeRef t = LLVMTypeOf(a);
 	GB_ASSERT(t == LLVMTypeOf(b));
@@ -1819,7 +1859,7 @@ LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b,
 	}
 }
 
-LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, String const &clobbers, bool has_side_effects=true, bool is_align_stack=false, LLVMInlineAsmDialect dialect=LLVMInlineAsmDialectATT) {
+gb_internal LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, String const &clobbers, bool has_side_effects=true, bool is_align_stack=false, LLVMInlineAsmDialect dialect=LLVMInlineAsmDialectATT) {
 	return LLVMGetInlineAsm(func_type,
 		cast(char *)str.text, cast(size_t)str.len,
 		cast(char *)clobbers.text, cast(size_t)clobbers.len,
@@ -1832,7 +1872,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
 }
 
 
-void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entity, String import_name) {
+gb_internal void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entity, String import_name) {
 	if (!is_arch_wasm()) {
 		return;
 	}
@@ -1854,7 +1894,7 @@ void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entity, String im
 }
 
 
-void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
+gb_internal void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
 	if (!is_arch_wasm()) {
 		return;
 	}
@@ -1866,7 +1906,7 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
 
 
 
-lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
+gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
 	lbAddr *found = string_map_get(&p->module->objc_selectors, name);
 	if (found) {
 		return *found;
@@ -1886,7 +1926,7 @@ lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &na
 	}
 }
 
-lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
 	ast_node(ce, CallExpr, expr);
 
 	auto tav = ce->args[0]->tav;
@@ -1895,7 +1935,7 @@ lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
 	return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name));
 }
 
-lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
 	ast_node(ce, CallExpr, expr);
 	lbModule *m = p->module;
 
@@ -1912,7 +1952,7 @@ lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
 	return lb_addr_load(p, dst);
 }
 
-lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
+gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
 	lbAddr *found = string_map_get(&p->module->objc_classes, name);
 	if (found) {
 		return *found;
@@ -1932,7 +1972,7 @@ lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name)
 	}
 }
 
-lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
 	ast_node(ce, CallExpr, expr);
 
 	auto tav = ce->args[0]->tav;
@@ -1941,7 +1981,7 @@ lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
 	return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
 }
 
-lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
 	ast_node(ce, CallExpr, expr);
 	lbModule *m = p->module;
 
@@ -1961,7 +2001,7 @@ lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
 }
 
 
-lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
 	TypeAndValue const &tav = type_and_value_of_expr(expr);
 	if (tav.mode == Addressing_Type) {
 		Type *type = tav.type;
@@ -1992,7 +2032,7 @@ lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
 	return lb_build_expr(p, expr);
 }
 
-lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
+gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
 	ast_node(ce, CallExpr, expr);
 
 	lbModule *m = p->module;
@@ -2035,7 +2075,7 @@ lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
 
 
 
-LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
+gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
 	GB_ASSERT(value.kind == ExactValue_Integer);
 	i64 v = exact_value_to_i64(value);
 	switch (v) {
@@ -2051,7 +2091,7 @@ LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
 }
 
 
-LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
+gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
 	ExactValue value = type_and_value_of_expr(expr).value;
 	return llvm_atomic_ordering_from_odin(value);
 }

+ 124 - 204
src/main.cpp

@@ -3,31 +3,41 @@
 #include "common.cpp"
 #include "timings.cpp"
 #include "tokenizer.cpp"
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(push)
+	#pragma warning(disable: 4505)
+#endif
 #include "big_int.cpp"
+#if defined(GB_SYSTEM_WINDOWS)
+	#pragma warning(pop)
+#endif
 #include "exact_value.cpp"
 #include "build_settings.cpp"
-
 gb_global ThreadPool global_thread_pool;
-void init_global_thread_pool(void) {
+gb_internal void init_global_thread_pool(void) {
 	isize thread_count = gb_max(build_context.thread_count, 1);
-	isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work
-	thread_pool_init(&global_thread_pool, permanent_allocator(), worker_count, "ThreadPoolWorker");
+	isize worker_count = thread_count; // +1
+	thread_pool_init(&global_thread_pool, worker_count, "ThreadPoolWorker");
 }
-bool global_thread_pool_add_task(WorkerTaskProc *proc, void *data) {
+gb_internal bool thread_pool_add_task(WorkerTaskProc *proc, void *data) {
 	return thread_pool_add_task(&global_thread_pool, proc, data);
 }
-void global_thread_pool_wait(void) {
+gb_internal void thread_pool_wait(void) {
 	thread_pool_wait(&global_thread_pool);
 }
 
 
-void debugf(char const *fmt, ...) {
+gb_global BlockingMutex debugf_mutex;
+
+gb_internal void debugf(char const *fmt, ...) {
 	if (build_context.show_debug_messages) {
+		mutex_lock(&debugf_mutex);
 		gb_printf_err("[DEBUG] ");
 		va_list va;
 		va_start(va, fmt);
 		(void)gb_printf_err_va(fmt, va);
 		va_end(va);
+		mutex_unlock(&debugf_mutex);
 	}
 }
 
@@ -58,11 +68,10 @@ gb_global Timings global_timings = {0};
 	#endif
 #endif
 
-#include "query_data.cpp"
 #include "bug_report.cpp"
 
 // NOTE(bill): 'name' is used in debugging and profiling modes
-i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
+gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
 	isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
 	char *cmd_line = gb_alloc_array(gb_heap_allocator(), char, cmd_cap);
 	isize cmd_len = 0;
@@ -124,7 +133,7 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
 }
 
 
-i32 linker_stage(lbGenerator *gen) {
+gb_internal i32 linker_stage(lbGenerator *gen) {
 	i32 result = 0;
 	Timings *timings = &global_timings;
 
@@ -207,15 +216,14 @@ i32 linker_stage(lbGenerator *gen) {
 
 
 			StringSet libs = {};
-			string_set_init(&libs, heap_allocator(), 64);
+			string_set_init(&libs, 64);
 			defer (string_set_destroy(&libs));
 
 			StringSet asm_files = {};
-			string_set_init(&asm_files, heap_allocator(), 64);
+			string_set_init(&asm_files, 64);
 			defer (string_set_destroy(&asm_files));
 
-			for_array(j, gen->foreign_libraries) {
-				Entity *e = gen->foreign_libraries[j];
+			for (Entity *e : gen->foreign_libraries) {
 				GB_ASSERT(e->kind == Entity_LibraryName);
 				for_array(i, e->LibraryName.paths) {
 					String lib = string_trim_whitespace(e->LibraryName.paths[i]);
@@ -278,8 +286,7 @@ i32 linker_stage(lbGenerator *gen) {
 
 			gbString object_files = gb_string_make(heap_allocator(), "");
 			defer (gb_string_free(object_files));
-			for_array(i, gen->output_object_paths) {
-				String object_path = gen->output_object_paths[i];
+			for (String const &object_path : gen->output_object_paths) {
 				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
 			}
 
@@ -313,12 +320,18 @@ i32 linker_stage(lbGenerator *gen) {
 					}
 				}
 
+				switch (build_context.build_mode) {
+				case BuildMode_Executable:
+					link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
+					break;
+				}
+
 				result = system_exec_command_line_app("msvc-link",
 					"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
 					"/nologo /incremental:no /opt:ref /subsystem:%s "
-					" %.*s "
-					" %.*s "
-					" %s "
+					"%.*s "
+					"%.*s "
+					"%s "
 					"",
 					LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
 					link_settings,
@@ -334,9 +347,9 @@ i32 linker_stage(lbGenerator *gen) {
 				result = system_exec_command_line_app("msvc-lld-link",
 					"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
 					"/nologo /incremental:no /opt:ref /subsystem:%s "
-					" %.*s "
-					" %.*s "
-					" %s "
+					"%.*s "
+					"%.*s "
+					"%s "
 					"",
 					LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
 					link_settings,
@@ -366,14 +379,13 @@ i32 linker_stage(lbGenerator *gen) {
 			defer (gb_string_free(lib_str));
 
 			StringSet libs = {};
-			string_set_init(&libs, heap_allocator(), 64);
+			string_set_init(&libs, 64);
 			defer (string_set_destroy(&libs));
 
-			for_array(j, gen->foreign_libraries) {
-				Entity *e = gen->foreign_libraries[j];
+			for (Entity *e : gen->foreign_libraries) {
 				GB_ASSERT(e->kind == Entity_LibraryName);
-				for_array(i, e->LibraryName.paths) {
-					String lib = string_trim_whitespace(e->LibraryName.paths[i]);
+				for (String lib : e->LibraryName.paths) {
+					lib = string_trim_whitespace(lib);
 					if (lib.len == 0) {
 						continue;
 					}
@@ -425,8 +437,7 @@ i32 linker_stage(lbGenerator *gen) {
 
 			gbString object_files = gb_string_make(heap_allocator(), "");
 			defer (gb_string_free(object_files));
-			for_array(i, gen->output_object_paths) {
-				String object_path = gen->output_object_paths[i];
+			for (String object_path : gen->output_object_paths) {
 				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
 			}
 
@@ -524,7 +535,7 @@ i32 linker_stage(lbGenerator *gen) {
 	return result;
 }
 
-Array<String> setup_args(int argc, char const **argv) {
+gb_internal Array<String> setup_args(int argc, char const **argv) {
 	gbAllocator a = heap_allocator();
 
 #if defined(GB_SYSTEM_WINDOWS)
@@ -553,7 +564,7 @@ Array<String> setup_args(int argc, char const **argv) {
 #endif
 }
 
-void print_usage_line(i32 indent, char const *fmt, ...) {
+gb_internal void print_usage_line(i32 indent, char const *fmt, ...) {
 	while (indent --> 0) {
 		gb_printf_err("\t");
 	}
@@ -564,7 +575,7 @@ void print_usage_line(i32 indent, char const *fmt, ...) {
 	gb_printf_err("\n");
 }
 
-void usage(String argv0) {
+gb_internal void usage(String argv0) {
 	print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
 	print_usage_line(0, "Usage:");
 	print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
@@ -573,7 +584,6 @@ void usage(String argv0) {
 	print_usage_line(1, "                  one must contain the program's entry point, all must be in the same package.");
 	print_usage_line(1, "run               same as 'build', but also then runs the newly compiled executable.");
 	print_usage_line(1, "check             parse, and type check a directory of .odin files");
-	print_usage_line(1, "query             parse, type check, and output a .json file containing information about the program");
 	print_usage_line(1, "strip-semicolon   parse, type check, and remove unneeded semicolons from the entire program");
 	print_usage_line(1, "test              build and runs procedures with the attribute @(test) in the initial package");
 	print_usage_line(1, "doc               generate documentation on a directory of .odin files");
@@ -613,7 +623,6 @@ enum BuildFlagKind {
 	BuildFlag_NoEntryPoint,
 	BuildFlag_UseLLD,
 	BuildFlag_UseSeparateModules,
-	BuildFlag_ThreadedChecker,
 	BuildFlag_NoThreadedChecker,
 	BuildFlag_ShowDebugMessages,
 	BuildFlag_Vet,
@@ -655,6 +664,7 @@ enum BuildFlagKind {
 
 	// internal use only
 	BuildFlag_InternalIgnoreLazy,
+	BuildFlag_InternalIgnoreLLVMBuild,
 
 #if defined(GB_SYSTEM_WINDOWS)
 	BuildFlag_IgnoreVsSearch,
@@ -687,12 +697,12 @@ struct BuildFlag {
 };
 
 
-void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) {
+gb_internal void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) {
 	BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple};
 	array_add(build_flags, flag);
 }
 
-ExactValue build_param_to_exact_value(String name, String param) {
+gb_internal ExactValue build_param_to_exact_value(String name, String param) {
 	ExactValue value = {};
 
 	/*
@@ -747,7 +757,7 @@ ExactValue build_param_to_exact_value(String name, String param) {
 }
 
 // Writes a did-you-mean message for formerly deprecated flags.
-void did_you_mean_flag(String flag) {
+gb_internal void did_you_mean_flag(String flag) {
 	gbAllocator a = heap_allocator();
 	String name = copy_string(a, flag);
 	defer (gb_free(a, name.text));
@@ -760,11 +770,11 @@ void did_you_mean_flag(String flag) {
 	gb_printf_err("Unknown flag: '%.*s'\n", LIT(flag));
 }
 
-bool parse_build_flags(Array<String> args) {
+gb_internal bool parse_build_flags(Array<String> args) {
 	auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
 	add_flag(&build_flags, BuildFlag_Help,                    str_lit("help"),                      BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_SingleFile,              str_lit("file"),                      BuildFlagParam_None,    Command__does_build | Command__does_check);
-	add_flag(&build_flags, BuildFlag_OutFile,                 str_lit("out"),                       BuildFlagParam_String,  Command__does_build &~ Command_test);
+	add_flag(&build_flags, BuildFlag_OutFile,                 str_lit("out"),                       BuildFlagParam_String,  Command__does_build | Command_test);
 	add_flag(&build_flags, BuildFlag_OptimizationMode,        str_lit("o"),                         BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_OptimizationMode,        str_lit("O"),                         BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ShowTimings,             str_lit("show-timings"),              BuildFlagParam_None,    Command__does_check);
@@ -788,7 +798,6 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_NoEntryPoint,            str_lit("no-entry-point"),            BuildFlagParam_None,    Command__does_check &~ Command_test);
 	add_flag(&build_flags, BuildFlag_UseLLD,                  str_lit("lld"),                       BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_UseSeparateModules,      str_lit("use-separate-modules"),      BuildFlagParam_None,    Command__does_build);
-	add_flag(&build_flags, BuildFlag_ThreadedChecker,         str_lit("threaded-checker"),          BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_NoThreadedChecker,       str_lit("no-threaded-checker"),       BuildFlagParam_None,    Command__does_check);
 	add_flag(&build_flags, BuildFlag_ShowDebugMessages,       str_lit("show-debug-messages"),       BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_Vet,                     str_lit("vet"),                       BuildFlagParam_None,    Command__does_check);
@@ -817,12 +826,6 @@ bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_UseStaticMapCalls,       str_lit("use-static-map-calls"),      BuildFlagParam_None,    Command__does_check);
 
-
-	add_flag(&build_flags, BuildFlag_Compact,                 str_lit("compact"),                   BuildFlagParam_None,    Command_query);
-	add_flag(&build_flags, BuildFlag_GlobalDefinitions,       str_lit("global-definitions"),        BuildFlagParam_None,    Command_query);
-	add_flag(&build_flags, BuildFlag_GoToDefinitions,         str_lit("go-to-definitions"),         BuildFlagParam_None,    Command_query);
-
-
 	add_flag(&build_flags, BuildFlag_Short,                   str_lit("short"),                     BuildFlagParam_None,    Command_doc);
 	add_flag(&build_flags, BuildFlag_AllPackages,             str_lit("all-packages"),              BuildFlagParam_None,    Command_doc);
 	add_flag(&build_flags, BuildFlag_DocFormat,               str_lit("doc-format"),                BuildFlagParam_None,    Command_doc);
@@ -833,6 +836,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_ErrorPosStyle,           str_lit("error-pos-style"),           BuildFlagParam_String,  Command_all);
 
 	add_flag(&build_flags, BuildFlag_InternalIgnoreLazy,      str_lit("internal-ignore-lazy"),      BuildFlagParam_None,    Command_all);
+	add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None,    Command_all);
 
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch,          str_lit("ignore-vs-search"),          BuildFlagParam_None,    Command__does_build);
@@ -848,8 +852,7 @@ bool parse_build_flags(Array<String> args) {
 	bool set_flags[BuildFlag_COUNT] = {};
 
 	bool bad_flags = false;
-	for_array(i, flag_args) {
-		String flag = flag_args[i];
+	for (String flag : flag_args) {
 		if (flag[0] != '-') {
 			gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
 			continue;
@@ -878,8 +881,7 @@ bool parse_build_flags(Array<String> args) {
 		bool found = false;
 
 		BuildFlag found_bf = {};
-		for_array(build_flag_index, build_flags) {
-			BuildFlag bf = build_flags[build_flag_index];
+		for (BuildFlag const &bf : build_flags) {
 			if (bf.name == name) {
 				found = true;
 				found_bf = bf;
@@ -1311,20 +1313,8 @@ bool parse_build_flags(Array<String> args) {
 						case BuildFlag_UseSeparateModules:
 							build_context.use_separate_modules = true;
 							break;
-						case BuildFlag_ThreadedChecker: {
-							#if defined(DEFAULT_TO_THREADED_CHECKER)
-							gb_printf_err("-threaded-checker is the default on this platform\n");
-							bad_flags = true;
-							#endif
-							build_context.threaded_checker = true;
-							break;
-						}
 						case BuildFlag_NoThreadedChecker: {
-							#if !defined(DEFAULT_TO_THREADED_CHECKER)
-							gb_printf_err("-no-threaded-checker is the default on this platform\n");
-							bad_flags = true;
-							#endif
-							build_context.threaded_checker = false;
+							build_context.no_threaded_checker = true;
 							break;
 						}
 						case BuildFlag_ShowDebugMessages:
@@ -1445,39 +1435,6 @@ bool parse_build_flags(Array<String> args) {
 							build_context.strict_style_init_only = true;
 							break;
 						}
-						case BuildFlag_Compact: {
-							if (!build_context.query_data_set_settings.ok) {
-								gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n");
-								bad_flags = true;
-							} else {
-								build_context.query_data_set_settings.compact = true;
-							}
-							break;
-						}
-						case BuildFlag_GlobalDefinitions: {
-							if (!build_context.query_data_set_settings.ok) {
-								gb_printf_err("Invalid use of -global-definitions flag, only allowed with 'odin query'\n");
-								bad_flags = true;
-							} else if (build_context.query_data_set_settings.kind != QueryDataSet_Invalid) {
-								gb_printf_err("Invalid use of -global-definitions flag, a previous flag for 'odin query' was set\n");
-								bad_flags = true;
-							} else {
-								build_context.query_data_set_settings.kind = QueryDataSet_GlobalDefinitions;
-							}
-							break;
-						}
-						case BuildFlag_GoToDefinitions: {
-							if (!build_context.query_data_set_settings.ok) {
-								gb_printf_err("Invalid use of -go-to-definitions flag, only allowed with 'odin query'\n");
-								bad_flags = true;
-							} else if (build_context.query_data_set_settings.kind != QueryDataSet_Invalid) {
-								gb_printf_err("Invalid use of -global-definitions flag, a previous flag for 'odin query' was set\n");
-								bad_flags = true;
-							} else {
-								build_context.query_data_set_settings.kind = QueryDataSet_GoToDefinitions;
-							}
-							break;
-						}
 						case BuildFlag_Short:
 							build_context.cmd_doc_flags |= CmdDocFlag_Short;
 							break;
@@ -1525,6 +1482,9 @@ bool parse_build_flags(Array<String> args) {
 						case BuildFlag_InternalIgnoreLazy:
 							build_context.ignore_lazy = true;
 							break;
+						case BuildFlag_InternalIgnoreLLVMBuild:
+							build_context.ignore_llvm_build = true;
+							break;
 					#if defined(GB_SYSTEM_WINDOWS)
 						case BuildFlag_IgnoreVsSearch: {
 							GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1638,20 +1598,10 @@ bool parse_build_flags(Array<String> args) {
 		gb_printf_err("`-export-timings:<format>` requires `-show-timings` or `-show-more-timings` to be present\n");
 		bad_flags = true;
 	}
-
-	if (build_context.query_data_set_settings.ok) {
-		if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) {
-			gb_printf_err("'odin query' requires a flag determining the kind of query data set to be returned\n");
-			gb_printf_err("\t-global-definitions : outputs a JSON file of global definitions\n");
-			gb_printf_err("\t-go-to-definitions  : outputs a OGTD binary file of go to definitions for identifiers within an Odin project\n");
-			bad_flags = true;
-		}
-	}
-
 	return !bad_flags;
 }
 
-void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = false) {
+gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = false) {
 	GB_ASSERT((!(build_context.export_timings_format == TimingsExportUnspecified) && build_context.export_timings_file.len > 0));
 
 	/*
@@ -1690,10 +1640,9 @@ void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = fal
 		isize files           = 0;
 		isize packages        = p->packages.count;
 		isize total_file_size = 0;
-		for_array(i, p->packages) {
-			files += p->packages[i]->files.count;
-			for_array(j, p->packages[i]->files) {
-				AstFile *file = p->packages[i]->files[j];
+		for (AstPackage *pkg : p->packages) {
+			files += pkg->files.count;
+			for (AstFile *file : pkg->files) {
 				total_file_size += file->tokenizer.end - file->tokenizer.start;
 			}
 		}
@@ -1717,8 +1666,7 @@ void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = fal
 		gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n",
 		    LIT(t->total.label), total_time);
 
-		for_array(i, t->sections) {
-			TimeStamp ts = t->sections[i];
+		for (TimeStamp const &ts : t->sections) {
 			f64 section_time = time_stamp(ts, t->freq, unit);
 			gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n",
 			    LIT(ts.label), section_time);
@@ -1739,8 +1687,7 @@ void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = fal
 		*/
 		gb_fprintf(&f, "\"%.*s\", %d\n", LIT(t->total.label), int(total_time));
 
-		for_array(i, t->sections) {
-			TimeStamp ts = t->sections[i];
+		for (TimeStamp const &ts : t->sections) {
 			f64 section_time = time_stamp(ts, t->freq, unit);
 			gb_fprintf(&f, "\"%.*s\", %d\n", LIT(ts.label), int(section_time));
 		}
@@ -1749,7 +1696,7 @@ void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = fal
 	gb_printf("Done.\n");
 }
 
-void show_timings(Checker *c, Timings *t) {
+gb_internal void show_timings(Checker *c, Timings *t) {
 	Parser *p      = c->parser;
 	isize lines    = p->total_line_count;
 	isize tokens   = p->total_token_count;
@@ -1758,10 +1705,9 @@ void show_timings(Checker *c, Timings *t) {
 	isize total_file_size = 0;
 	f64 total_tokenizing_time = 0;
 	f64 total_parsing_time = 0;
-	for_array(i, p->packages) {
-		files += p->packages[i]->files.count;
-		for_array(j, p->packages[i]->files) {
-			AstFile *file = p->packages[i]->files[j];
+	for (AstPackage *pkg : p->packages) {
+		files += pkg->files.count;
+		for (AstFile *file : pkg->files) {
 			total_tokenizing_time += file->time_to_tokenize;
 			total_parsing_time += file->time_to_parse;
 			total_file_size += file->tokenizer.end - file->tokenizer.start;
@@ -1770,6 +1716,16 @@ void show_timings(Checker *c, Timings *t) {
 
 	timings_print_all(t);
 
+	if (build_context.show_more_timings) {
+	#if defined(GB_SYSTEM_WINDOWS)
+		PROCESS_MEMORY_COUNTERS p = {sizeof(p)};
+		if (GetProcessMemoryInfo(GetCurrentProcess(), &p, sizeof(p))) {
+			gb_printf("\n");
+			gb_printf("Peak Memory Size: %.3f MiB\n", (cast(f64)p.PeakWorkingSetSize) / cast(f64)(1024ull * 1024ull));
+		}
+	#endif
+	}
+
 	if (!(build_context.export_timings_format == TimingsExportUnspecified)) {
 		timings_export_all(t, c, true);
 	}
@@ -1812,8 +1768,7 @@ void show_timings(Checker *c, Timings *t) {
 		}
 		{
 			TimeStamp ts = {};
-			for_array(i, t->sections) {
-				TimeStamp s = t->sections[i];
+			for (TimeStamp const &s : t->sections) {
 				if (s.label == "parse files") {
 					ts = s;
 					break;
@@ -1836,8 +1791,7 @@ void show_timings(Checker *c, Timings *t) {
 		{
 			TimeStamp ts = {};
 			TimeStamp ts_end = {};
-			for_array(i, t->sections) {
-				TimeStamp s = t->sections[i];
+			for (TimeStamp const &s : t->sections) {
 				if (s.label == "type check") {
 					ts = s;
 				}
@@ -1878,13 +1832,12 @@ void show_timings(Checker *c, Timings *t) {
 	}
 }
 
-void remove_temp_files(lbGenerator *gen) {
+gb_internal void remove_temp_files(lbGenerator *gen) {
 	if (build_context.keep_temp_files) return;
 
 	TIME_SECTION("remove keep temp files");
 
-	for_array(i, gen->output_temp_paths) {
-		String path = gen->output_temp_paths[i];
+	for (String const &path : gen->output_temp_paths) {
 		gb_file_remove(cast(char const *)path.text);
 	}
 
@@ -1892,8 +1845,7 @@ void remove_temp_files(lbGenerator *gen) {
 		switch (build_context.build_mode) {
 		case BuildMode_Executable:
 		case BuildMode_DynamicLibrary:
-			for_array(i, gen->output_object_paths) {
-				String path = gen->output_object_paths[i];
+			for (String const &path : gen->output_object_paths) {
 				gb_file_remove(cast(char const *)path.text);
 			}
 			break;
@@ -1902,7 +1854,7 @@ void remove_temp_files(lbGenerator *gen) {
 }
 
 
-void print_show_help(String const arg0, String const &command) {
+gb_internal void print_show_help(String const arg0, String const &command) {
 	print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0));
 	print_usage_line(0, "Usage:");
 	print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
@@ -1930,9 +1882,7 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(3, "odin check <dir>                # Type check package in <dir>");
 		print_usage_line(3, "odin check filename.odin -file  # Type check single-file package, must contain entry point.");
 	} else if (command == "test") {
-		print_usage_line(1, "test      Build ands runs procedures with the attribute @(test) in the initial package");
-	} else if (command == "query") {
-		print_usage_line(1, "query     [experimental] Parse, type check, and output a .json file containing information about the program");
+		print_usage_line(1, "test      Build and runs procedures with the attribute @(test) in the initial package");
 	} else if (command == "doc") {
 		print_usage_line(1, "doc       generate documentation from a directory of .odin files");
 		print_usage_line(2, "Examples:");
@@ -2248,12 +2198,11 @@ void print_show_help(String const arg0, String const &command) {
 	}
 }
 
-void print_show_unused(Checker *c) {
+gb_internal void print_show_unused(Checker *c) {
 	CheckerInfo *info = &c->info;
 
 	auto unused = array_make<Entity *>(permanent_allocator(), 0, info->entities.count);
-	for_array(i, info->entities) {
-		Entity *e = info->entities[i];
+	for (Entity *e : info->entities) {
 		if (e == nullptr) {
 			continue;
 		}
@@ -2300,8 +2249,7 @@ void print_show_unused(Checker *c) {
 
 	AstPackage *curr_pkg = nullptr;
 	EntityKind curr_entity_kind = Entity_Invalid;
-	for_array(i, unused) {
-		Entity *e = unused[i];
+	for (Entity *e : unused) {
 		if (curr_pkg != e->pkg) {
 			curr_pkg = e->pkg;
 			curr_entity_kind = Entity_Invalid;
@@ -2322,7 +2270,7 @@ void print_show_unused(Checker *c) {
 	print_usage_line(0, "");
 }
 
-bool check_env(void) {
+gb_internal bool check_env(void) {
 	gbAllocator a = heap_allocator();
 	char const *odin_root = gb_get_env("ODIN_ROOT", a);
 	defer (gb_free(a, cast(void *)odin_root));
@@ -2348,25 +2296,24 @@ struct StripSemicolonFile {
 	i64 written;
 };
 
-gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *written_) {
+gb_internal gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *written_) {
 	i64 written = 0;
 	gbFileError err = gbFileError_None;
 	u8 const *file_data = file->tokenizer.start;
 	i32 prev_offset = 0;
 	i32 const end_offset = cast(i32)(file->tokenizer.end - file->tokenizer.start);
-	for_array(i, file->tokens) {
-		Token *token = &file->tokens[i];
-		if (token->flags & (TokenFlag_Remove|TokenFlag_Replace)) {
-			i32 offset = token->pos.offset;
+	for (Token const &token : file->tokens) {
+		if (token.flags & (TokenFlag_Remove|TokenFlag_Replace)) {
+			i32 offset = token.pos.offset;
 			i32 to_write = offset-prev_offset;
 			if (!gb_file_write(f, file_data+prev_offset, to_write)) {
 				return gbFileError_Invalid;
 			}
 			written += to_write;
-			prev_offset = token_pos_end(*token).offset;
+			prev_offset = token_pos_end(token).offset;
 		}
-		if (token->flags & TokenFlag_Replace) {
-			if (token->kind == Token_Ellipsis) {
+		if (token.flags & TokenFlag_Replace) {
+			if (token.kind == Token_Ellipsis) {
 				if (!gb_file_write(f, "..=", 3)) {
 					return gbFileError_Invalid;
 				}
@@ -2388,24 +2335,19 @@ gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *writt
 	return err;
 }
 
-int strip_semicolons(Parser *parser) {
+gb_internal int strip_semicolons(Parser *parser) {
 	isize file_count = 0;
-	for_array(i, parser->packages) {
-		AstPackage *pkg = parser->packages[i];
+	for (AstPackage *pkg : parser->packages) {
 		file_count += pkg->files.count;
 	}
 
 	auto generated_files = array_make<StripSemicolonFile>(permanent_allocator(), 0, file_count);
 
-	for_array(i, parser->packages) {
-		AstPackage *pkg = parser->packages[i];
-		for_array(j, pkg->files) {
-			AstFile *file = pkg->files[j];
-
+	for (AstPackage *pkg : parser->packages) {
+		for (AstFile *file : pkg->files) {
 			bool nothing_to_change = true;
-			for_array(i, file->tokens) {
-				Token *token = &file->tokens[i];
-				if (token->flags) {
+			for (Token const &token : file->tokens) {
+				if (token.flags) {
 					nothing_to_change = false;
 					break;
 				}
@@ -2433,9 +2375,8 @@ int strip_semicolons(Parser *parser) {
 	isize generated_count = 0;
 	bool failed = false;
 
-	for_array(i, generated_files) {
-		auto *file = &generated_files[i];
-		char const *filename = cast(char const *)file->new_fullpath.text;
+	for (StripSemicolonFile &file : generated_files) {
+		char const *filename = cast(char const *)file.new_fullpath.text;
 		gbFileError err = gbFileError_None;
 		defer (if (err != gbFileError_None) {
 			failed = true;
@@ -2453,11 +2394,11 @@ int strip_semicolons(Parser *parser) {
 		defer (err = gb_file_truncate(&f, written));
 
 		debugf("Write file with stripped tokens: %s\n", filename);
-		err = write_file_with_stripped_tokens(&f, file->file, &written);
+		err = write_file_with_stripped_tokens(&f, file.file, &written);
 		if (err) {
 			break;
 		}
-		file->written = written;
+		file.written = written;
 	}
 
 	if (failed) {
@@ -2472,12 +2413,10 @@ int strip_semicolons(Parser *parser) {
 
 	isize overwritten_files = 0;
 
-	for_array(i, generated_files) {
-		auto *file = &generated_files[i];
-
-		char const *old_fullpath = cast(char const *)file->old_fullpath.text;
-		char const *old_fullpath_backup = cast(char const *)file->old_fullpath_backup.text;
-		char const *new_fullpath = cast(char const *)file->new_fullpath.text;
+	for (StripSemicolonFile const &file : generated_files) {
+		char const *old_fullpath = cast(char const *)file.old_fullpath.text;
+		char const *old_fullpath_backup = cast(char const *)file.old_fullpath_backup.text;
+		char const *new_fullpath = cast(char const *)file.new_fullpath.text;
 
 		debugf("Copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup);
 		if (!gb_file_copy(old_fullpath, old_fullpath_backup, false)) {
@@ -2537,22 +2476,16 @@ int main(int arg_count, char const **arg_ptr) {
 		usage(make_string_c(arg_ptr[0]));
 		return 1;
 	}
+	virtual_memory_init();
 
 	timings_init(&global_timings, str_lit("Total Time"), 2048);
 	defer (timings_destroy(&global_timings));
 
 	MAIN_TIME_SECTION("initialization");
 
-	virtual_memory_init();
-	mutex_init(&fullpath_mutex);
-	mutex_init(&hash_exact_value_mutex);
-	mutex_init(&global_type_name_objc_metadata_mutex);
-
-	init_string_buffer_memory();
 	init_string_interner();
 	init_global_error_collector();
 	init_keyword_hash_table();
-	init_type_mutex();
 
 	if (!check_env()) {
 		return 1;
@@ -2563,9 +2496,9 @@ int main(int arg_count, char const **arg_ptr) {
 	add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
 	add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
 
-	map_init(&build_context.defined_values, heap_allocator());
+	map_init(&build_context.defined_values);
 	build_context.extra_packages.allocator = heap_allocator();
-	string_set_init(&build_context.test_names, heap_allocator());
+	string_set_init(&build_context.test_names);
 
 	Array<String> args = setup_args(arg_count, arg_ptr);
 
@@ -2627,15 +2560,6 @@ int main(int arg_count, char const **arg_ptr) {
 		build_context.command_kind = Command_strip_semicolon;
 		build_context.no_output_files = true;
 		init_filename = args[2];
-	} else if (command == "query") {
-		if (args.count < 3) {
-			usage(args[0]);
-			return 1;
-		}
-		build_context.command_kind = Command_query;
-		build_context.no_output_files = true;
-		build_context.query_data_set_settings.ok = true;
-		init_filename = args[2];
 	} else if (command == "doc") {
 		if (args.count < 3) {
 			usage(args[0]);
@@ -2824,12 +2748,8 @@ int main(int arg_count, char const **arg_ptr) {
 			print_show_unused(checker);
 		}
 
-		if (build_context.query_data_set_settings.ok) {
-			generate_and_print_query_data(checker, &global_timings);
-		} else {
-			if (build_context.show_timings) {
-				show_timings(checker, &global_timings);
-			}
+		if (build_context.show_timings) {
+			show_timings(checker, &global_timings);
 		}
 
 		if (global_error_collector.count != 0) {
@@ -2844,19 +2764,19 @@ int main(int arg_count, char const **arg_ptr) {
 	if (!lb_init_generator(gen, checker)) {
 		return 1;
 	}
-	lb_generate_code(gen);
-
-	switch (build_context.build_mode) {
-	case BuildMode_Executable:
-	case BuildMode_DynamicLibrary:
-		i32 result = linker_stage(gen);
-		if (result) {
-			if (build_context.show_timings) {
-				show_timings(checker, &global_timings);
+	if (lb_generate_code(gen)) {
+		switch (build_context.build_mode) {
+		case BuildMode_Executable:
+		case BuildMode_DynamicLibrary:
+			i32 result = linker_stage(gen);
+			if (result) {
+				if (build_context.show_timings) {
+					show_timings(checker, &global_timings);
+				}
+				return result;
 			}
-			return result;
+			break;
 		}
-		break;
 	}
 
 	remove_temp_files(gen);

+ 20 - 24
src/microsoft_craziness.h

@@ -45,7 +45,7 @@
 //
 // Here is the API you need to know about:
 //
-gb_global gbAllocator mc_allocator = heap_allocator();
+gb_global gbAllocator mc_allocator = permanent_allocator();
 
 struct Find_Result {
 	int windows_sdk_version;   // Zero if no Windows SDK found.
@@ -58,50 +58,46 @@ struct Find_Result {
 	String vs_library_path;
 };
 
-String mc_wstring_to_string(wchar_t const *str) {
+gb_internal String mc_wstring_to_string(wchar_t const *str) {
 	return string16_to_string(mc_allocator, make_string16_c(str));
 }
 
-String16 mc_string_to_wstring(String str) {
+gb_internal String16 mc_string_to_wstring(String str) {
 	return string_to_string16(mc_allocator, str);
 }
 
-String mc_concat(String a, String b) {
+gb_internal String mc_concat(String a, String b) {
 	return concatenate_strings(mc_allocator, a, b);
 }
 
-String mc_concat(String a, String b, String c) {
+gb_internal String mc_concat(String a, String b, String c) {
 	return concatenate3_strings(mc_allocator, a, b, c);
 }
 
-String mc_concat(String a, String b, String c, String d) {
+gb_internal String mc_concat(String a, String b, String c, String d) {
 	return concatenate4_strings(mc_allocator, a, b, c, d);
 }
 
-String mc_get_env(String key) {
+gb_internal String mc_get_env(String key) {
 	char const * value = gb_get_env((char const *)key.text, mc_allocator);
 	return make_string_c(value);
 }
 
-void mc_free(String str) {
+gb_internal void mc_free(String str) {
 	if (str.len) gb_free(mc_allocator, str.text);
 }
 
-void mc_free(String16 str) {
+gb_internal void mc_free(String16 str) {
 	if (str.len) gb_free(mc_allocator, str.text);
 }
 
-void mc_free_all() {
-	gb_free_all(mc_allocator);
-}
-
 typedef struct _MC_Find_Data {
 	DWORD  file_attributes;
 	String filename;
 } MC_Find_Data;
 
 
-HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
+gb_internal HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
  	WIN32_FIND_DATAW _find_data;
 
  	String16 wildcard_wide = mc_string_to_wstring(wildcard);
@@ -115,7 +111,7 @@ HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
  	return handle;
 }
 
-bool mc_find_next(HANDLE handle, MC_Find_Data *find_data) {
+gb_internal bool mc_find_next(HANDLE handle, MC_Find_Data *find_data) {
  	WIN32_FIND_DATAW _find_data;
  	bool success = !!FindNextFileW(handle, &_find_data);
 
@@ -124,7 +120,7 @@ bool mc_find_next(HANDLE handle, MC_Find_Data *find_data) {
  	return success;
 }
 
-void mc_find_close(HANDLE handle) {
+gb_internal void mc_find_close(HANDLE handle) {
 	FindClose(handle);
 }
 
@@ -216,7 +212,7 @@ struct Version_Data {
 };
 
 typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data *data);
-bool mc_visit_files(String dir_name, Version_Data *data, MC_Visit_Proc proc) {
+gb_internal bool mc_visit_files(String dir_name, Version_Data *data, MC_Visit_Proc proc) {
 
 	// Visit everything in one folder (non-recursively). If it's a directory
 	// that doesn't start with ".", call the visit proc on it. The visit proc
@@ -246,7 +242,7 @@ bool mc_visit_files(String dir_name, Version_Data *data, MC_Visit_Proc proc) {
 	return true;
 }
 
-String find_windows_kit_root(HKEY key, String const version) {
+gb_internal String find_windows_kit_root(HKEY key, String const version) {
 	// Given a key to an already opened registry entry,
 	// get the value stored under the 'version' subkey.
 	// If that's not the right terminology, hey, I never do registry stuff.
@@ -275,7 +271,7 @@ String find_windows_kit_root(HKEY key, String const version) {
 	return value;
 }
 
-void win10_best(String short_name, String full_name, Version_Data *data) {
+gb_internal void win10_best(String short_name, String full_name, Version_Data *data) {
 	// Find the Windows 10 subdirectory with the highest version number.
 
 	int i0, i1, i2, i3;
@@ -307,7 +303,7 @@ void win10_best(String short_name, String full_name, Version_Data *data) {
 	}
 }
 
-void find_windows_kit_paths(Find_Result *result) {
+gb_internal void find_windows_kit_paths(Find_Result *result) {
 	bool sdk_found = false;
 
 	HKEY main_key;
@@ -355,7 +351,7 @@ void find_windows_kit_paths(Find_Result *result) {
 	}
 }
 
-bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) {
+gb_internal bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) {
 	// The name of this procedure is kind of cryptic. Its purpose is
 	// to fight through Microsoft craziness. The things that the fine
 	// Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
@@ -519,7 +515,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
 
 // NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
 // official and portable installations (like mmozeiko's portable msvc script).
-void find_windows_kit_paths_from_env_vars(Find_Result *result) {
+gb_internal void find_windows_kit_paths_from_env_vars(Find_Result *result) {
 	if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
 		return;
 	}
@@ -669,7 +665,7 @@ void find_windows_kit_paths_from_env_vars(Find_Result *result) {
 // NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
 // official and portable installations (like mmozeiko's portable msvc script). This will only use
 // the first paths it finds, and won't overwrite any values that `result` already has.
-void find_visual_studio_paths_from_env_vars(Find_Result *result) {
+gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
 	if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
 		return;
 	}
@@ -756,7 +752,7 @@ void find_visual_studio_paths_from_env_vars(Find_Result *result) {
 	}
 }
 
-Find_Result find_visual_studio_and_windows_sdk() {
+gb_internal Find_Result find_visual_studio_and_windows_sdk() {
 	Find_Result r = {};
 	find_windows_kit_paths(&r);
 	find_visual_studio_by_fighting_through_microsoft_craziness(&r);

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 207 - 218
src/parser.cpp


+ 59 - 51
src/parser.hpp

@@ -62,15 +62,6 @@ enum PackageKind {
 	Package_Init,
 };
 
-struct ImportedPackage {
-	PackageKind kind;
-	String      path;
-	String      rel_path;
-	TokenPos    pos; // import
-	isize       index;
-};
-
-
 struct ImportedFile {
 	AstPackage *pkg;
 	FileInfo    fi;
@@ -99,7 +90,11 @@ struct AstFile {
 	Scope *      scope;
 
 	Ast *        pkg_decl;
+
 	String       fullpath;
+	String       filename;
+	String       directory;
+
 	Tokenizer    tokenizer;
 	Array<Token> tokens;
 	isize        curr_token_index;
@@ -109,6 +104,7 @@ struct AstFile {
 	Token        package_token;
 	String       package_name;
 
+
 	// >= 0: In Expression
 	// <  0: In Control Clause
 	// NOTE(bill): Used to prevent type literals in control clauses
@@ -136,9 +132,8 @@ struct AstFile {
 	CommentGroup *docs;             // current docs
 	Array<CommentGroup *> comments; // All the comments!
 
-	// TODO(bill): make this a basic queue as it does not require
-	// any multiple thread capabilities
-	MPMCQueue<Ast *> delayed_decls_queues[AstDelayQueue_COUNT];
+	// This is effectively a queue but does not require any multi-threading capabilities
+	Array<Ast *> delayed_decls_queues[AstDelayQueue_COUNT];
 
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;
@@ -177,6 +172,12 @@ struct AstPackage {
 	bool                  is_single_file;
 	isize                 order;
 
+	BlockingMutex         files_mutex;
+	BlockingMutex         foreign_files_mutex;
+	BlockingMutex         type_and_value_mutex;
+	BlockingMutex         name_mutex;
+
+	// NOTE(bill): This must be a MPMCQueue
 	MPMCQueue<AstPackageExportedEntity> exported_entity_queue;
 
 	// NOTE(bill): Created/set in checker
@@ -186,20 +187,33 @@ struct AstPackage {
 };
 
 
+struct ParseFileErrorNode {
+	ParseFileErrorNode *next, *prev;
+	ParseFileError      err;
+};
+
 struct Parser {
-	String                    init_fullpath;
-	StringSet                 imported_files; // fullpath
-	Array<AstPackage *>       packages;
-	Array<ImportedPackage>    package_imports;
-	isize                     file_to_process_count;
-	isize                     total_token_count;
-	isize                     total_line_count;
-	BlockingMutex             wait_mutex;
-	BlockingMutex             import_mutex;
-	BlockingMutex             file_add_mutex;
-	BlockingMutex             file_decl_mutex;
-	BlockingMutex             packages_mutex;
-	MPMCQueue<ParseFileError> file_error_queue;
+	String                 init_fullpath;
+
+	StringSet              imported_files; // fullpath
+	BlockingMutex          imported_files_mutex;
+
+	Array<AstPackage *>    packages;
+	BlockingMutex          packages_mutex;
+
+	std::atomic<isize>     file_to_process_count;
+	std::atomic<isize>     total_token_count;
+	std::atomic<isize>     total_line_count;
+
+	// TODO(bill): What should this mutex be per?
+	//  * Parser
+	//  * Package
+	//  * File
+	BlockingMutex          file_decl_mutex;
+
+	BlockingMutex          file_error_mutex;
+	ParseFileErrorNode *   file_error_head;
+	ParseFileErrorNode *   file_error_tail;
 };
 
 struct ParserWorkerData {
@@ -258,7 +272,7 @@ enum ProcCallingConvention : i32 {
 	ProcCC_ForeignBlockDefault = -1,
 };
 
-char const *proc_calling_convention_strings[ProcCC_MAX] = {
+gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = {
 	"",
 	"odin",
 	"contextless",
@@ -272,7 +286,7 @@ char const *proc_calling_convention_strings[ProcCC_MAX] = {
 	"sysv",
 };
 
-ProcCallingConvention default_calling_convention(void) {
+gb_internal ProcCallingConvention default_calling_convention(void) {
 	return ProcCC_Odin;
 }
 
@@ -299,7 +313,7 @@ enum FieldFlag : u32 {
 	FieldFlag_using     = 1<<1,
 	FieldFlag_no_alias  = 1<<2,
 	FieldFlag_c_vararg  = 1<<3,
-	FieldFlag_auto_cast = 1<<4,
+
 	FieldFlag_const     = 1<<5,
 	FieldFlag_any_int   = 1<<6,
 	FieldFlag_subtype   = 1<<7,
@@ -314,7 +328,7 @@ enum FieldFlag : u32 {
 	FieldFlag_Invalid   = 1u<<31,
 
 	// Parameter List Restrictions
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
 	FieldFlag_Struct    = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
 };
 
@@ -332,7 +346,7 @@ enum InlineAsmDialectKind : u8 {
 	InlineAsmDialect_COUNT,
 };
 
-char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
+gb_global char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
 	"",
 	"att",
 	"intel",
@@ -457,11 +471,6 @@ AST_KIND(_StmtBegin,     "", bool) \
 	AST_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
 	AST_KIND(EmptyStmt,  "empty statement",               struct { Token token; }) \
 	AST_KIND(ExprStmt,   "expression statement",          struct { Ast *expr; } ) \
-	AST_KIND(TagStmt,    "tag statement", struct { \
-		Token token; \
-		Token name; \
-		Ast * stmt; \
-	}) \
 	AST_KIND(AssignStmt, "assign statement", struct { \
 		Token op; \
 		Slice<Ast *> lhs, rhs; \
@@ -729,7 +738,7 @@ enum AstKind : u16 {
 	Ast_COUNT,
 };
 
-String const ast_strings[] = {
+gb_global String const ast_strings[] = {
 	{cast(u8 *)"invalid node", gb_size_of("invalid node")},
 #define AST_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1},
 	AST_KINDS
@@ -742,7 +751,7 @@ String const ast_strings[] = {
 #undef AST_KIND
 
 
-isize const ast_variant_sizes[] = {
+gb_global isize const ast_variant_sizes[] = {
 	0,
 #define AST_KIND(_kind_name_, name, ...) gb_size_of(GB_JOIN2(Ast, _kind_name_)),
 	AST_KINDS
@@ -754,7 +763,7 @@ struct AstCommonStuff {
 	u8           state_flags;
 	u8           viral_state_flags;
 	i32          file_id;
-	TypeAndValue tav; // TODO(bill): Make this a pointer to minimize 'Ast' size
+	TypeAndValue tav; // NOTE(bill): Making this a pointer is slower
 };
 
 struct Ast {
@@ -762,7 +771,7 @@ struct Ast {
 	u8           state_flags;
 	u8           viral_state_flags;
 	i32          file_id;
-	TypeAndValue tav; // TODO(bill): Make this a pointer to minimize 'Ast' size
+	TypeAndValue tav; // NOTE(bill): Making this a pointer is slower
 
 	// IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant
 	union {
@@ -793,33 +802,32 @@ struct Ast {
 #endif
 
 
-gb_inline bool is_ast_expr(Ast *node) {
+gb_internal gb_inline bool is_ast_expr(Ast *node) {
 	return gb_is_between(node->kind, Ast__ExprBegin+1, Ast__ExprEnd-1);
 }
-gb_inline bool is_ast_stmt(Ast *node) {
+gb_internal gb_inline bool is_ast_stmt(Ast *node) {
 	return gb_is_between(node->kind, Ast__StmtBegin+1, Ast__StmtEnd-1);
 }
-gb_inline bool is_ast_complex_stmt(Ast *node) {
+gb_internal gb_inline bool is_ast_complex_stmt(Ast *node) {
 	return gb_is_between(node->kind, Ast__ComplexStmtBegin+1, Ast__ComplexStmtEnd-1);
 }
-gb_inline bool is_ast_decl(Ast *node) {
+gb_internal gb_inline bool is_ast_decl(Ast *node) {
 	return gb_is_between(node->kind, Ast__DeclBegin+1, Ast__DeclEnd-1);
 }
-gb_inline bool is_ast_type(Ast *node) {
+gb_internal gb_inline bool is_ast_type(Ast *node) {
 	return gb_is_between(node->kind, Ast__TypeBegin+1, Ast__TypeEnd-1);
 }
-gb_inline bool is_ast_when_stmt(Ast *node) {
+gb_internal gb_inline bool is_ast_when_stmt(Ast *node) {
 	return node->kind == Ast_WhenStmt;
 }
 
 gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
 
-gbAllocator ast_allocator(AstFile *f) {
-	Arena *arena = &global_thread_local_ast_arena;
-	return arena_allocator(arena);
+gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) {
+	return arena_allocator(&global_thread_local_ast_arena);
 }
 
-Ast *alloc_ast_node(AstFile *f, AstKind kind);
+gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind);
 
-gbString expr_to_string(Ast *expression);
-bool allow_field_separator(AstFile *f);
+gb_internal gbString expr_to_string(Ast *expression);
+gb_internal bool allow_field_separator(AstFile *f);

+ 2 - 4
src/parser_pos.cpp

@@ -1,4 +1,4 @@
-Token ast_token(Ast *node) {
+gb_internal Token ast_token(Ast *node) {
 	switch (node->kind) {
 	case Ast_Ident:          return node->Ident.token;
 	case Ast_Implicit:       return node->Implicit;
@@ -53,7 +53,6 @@ Token ast_token(Ast *node) {
 	case Ast_BadStmt:            return node->BadStmt.begin;
 	case Ast_EmptyStmt:          return node->EmptyStmt.token;
 	case Ast_ExprStmt:           return ast_token(node->ExprStmt.expr);
-	case Ast_TagStmt:            return node->TagStmt.token;
 	case Ast_AssignStmt:         return node->AssignStmt.op;
 	case Ast_BlockStmt:          return node->BlockStmt.open;
 	case Ast_IfStmt:             return node->IfStmt.token;
@@ -197,7 +196,6 @@ Token ast_end_token(Ast *node) {
 	case Ast_BadStmt:            return node->BadStmt.end;
 	case Ast_EmptyStmt:          return node->EmptyStmt.token;
 	case Ast_ExprStmt:           return ast_end_token(node->ExprStmt.expr);
-	case Ast_TagStmt:            return ast_end_token(node->TagStmt.stmt);
 	case Ast_AssignStmt:
 		if (node->AssignStmt.rhs.count > 0) {
 			return ast_end_token(node->AssignStmt.rhs[node->AssignStmt.rhs.count-1]);
@@ -360,6 +358,6 @@ Token ast_end_token(Ast *node) {
 	return empty_token;
 }
 
-TokenPos ast_end_pos(Ast *node) {
+gb_internal TokenPos ast_end_pos(Ast *node) {
 	return token_pos_end(ast_end_token(node));
 }

+ 26 - 22
src/path.cpp

@@ -1,7 +1,10 @@
 /*
 	Path handling utilities.
 */
-String remove_extension_from_path(String const &s) {
+gb_internal String remove_extension_from_path(String const &s) {
+	if (s.len != 0 && s.text[s.len-1] == '.') {
+		return s;
+	}
 	for (isize i = s.len-1; i >= 0; i--) {
 		if (s[i] == '.') {
 			return substring(s, 0, i);
@@ -10,7 +13,7 @@ String remove_extension_from_path(String const &s) {
 	return s;
 }
 
-String remove_directory_from_path(String const &s) {
+gb_internal String remove_directory_from_path(String const &s) {
 	isize len = 0;
 	for (isize i = s.len-1; i >= 0; i--) {
 		if (s[i] == '/' ||
@@ -22,9 +25,9 @@ String remove_directory_from_path(String const &s) {
 	return substring(s, s.len-len, s.len);
 }
 
-bool path_is_directory(String path);
+gb_internal bool path_is_directory(String path);
 
-String directory_from_path(String const &s) {
+gb_internal String directory_from_path(String const &s) {
 	if (path_is_directory(s)) {
 		return s;
 	}
@@ -43,7 +46,7 @@ String directory_from_path(String const &s) {
 }
 
 #if defined(GB_SYSTEM_WINDOWS)
-	bool path_is_directory(String path) {
+	gb_internal bool path_is_directory(String path) {
 		gbAllocator a = heap_allocator();
 		String16 wstr = string_to_string16(a, path);
 		defer (gb_free(a, wstr.text));
@@ -55,7 +58,7 @@ String directory_from_path(String const &s) {
 	}
 
 #else
-	bool path_is_directory(String path) {
+	gb_internal bool path_is_directory(String path) {
 		gbAllocator a = heap_allocator();
 		char *copy = cast(char *)copy_string(a, path).text;
 		defer (gb_free(a, copy));
@@ -69,7 +72,7 @@ String directory_from_path(String const &s) {
 #endif
 
 
-String path_to_full_path(gbAllocator a, String path) {
+gb_internal String path_to_full_path(gbAllocator a, String path) {
 	gbAllocator ha = heap_allocator();
 	char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
 	defer (gb_free(ha, path_c));
@@ -93,7 +96,7 @@ struct Path {
 };
 
 // NOTE(Jeroen): Naively turns a Path into a string.
-String path_to_string(gbAllocator a, Path path) {
+gb_internal String path_to_string(gbAllocator a, Path path) {
 	if (path.basename.len + path.name.len + path.ext.len == 0) {
 		return make_string(nullptr, 0);
 	}
@@ -107,7 +110,9 @@ String path_to_string(gbAllocator a, Path path) {
 
 	isize i = 0;
 	gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+	
 	gb_memmove(str+i, "/", 1);                                i += 1;
+	
 	gb_memmove(str+i, path.name.text,     path.name.len);     i += path.name.len;
 	if (path.ext.len > 0) {
 		gb_memmove(str+i, ".", 1);                            i += 1;
@@ -121,7 +126,7 @@ String path_to_string(gbAllocator a, Path path) {
 }
 
 // NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-String path_to_full_path(gbAllocator a, Path path) {
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
 	String temp = path_to_string(heap_allocator(), path);
 	defer (gb_free(heap_allocator(), temp.text));
 
@@ -130,7 +135,7 @@ String path_to_full_path(gbAllocator a, Path path) {
 
 // NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
 // and then breaks it into its components to make a Path.
-Path path_from_string(gbAllocator a, String const &path) {
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
 	Path res = {};
 
 	if (path.len == 0) return res;
@@ -150,6 +155,7 @@ Path path_from_string(gbAllocator a, String const &path) {
 		return res;
 	}
 
+	// Note(Dragos): Is the copy_string required if it's a substring?
 	isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
 	res.name         = substring(fullpath, name_start, fullpath.len);
 	res.name         = remove_extension_from_path(res.name);
@@ -161,7 +167,7 @@ Path path_from_string(gbAllocator a, String const &path) {
 }
 
 // NOTE(Jeroen): Takes a path String and returns the last path element.
-String last_path_element(String const &path) {
+gb_internal String last_path_element(String const &path) {
 	isize count = 0;
 	u8 * start = (u8 *)(&path.text[path.len - 1]);
 	for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
@@ -177,7 +183,7 @@ String last_path_element(String const &path) {
 	return STR_LIT("");
 }
 
-bool path_is_directory(Path path) {
+gb_internal bool path_is_directory(Path path) {
 	String path_string = path_to_full_path(heap_allocator(), path);
 	defer (gb_free(heap_allocator(), path_string.text));
 
@@ -204,7 +210,7 @@ enum ReadDirectoryError {
 	ReadDirectory_COUNT,
 };
 
-i64 get_file_size(String path) {
+gb_internal i64 get_file_size(String path) {
 	char *c_str = alloc_cstring(heap_allocator(), path);
 	defer (gb_free(heap_allocator(), c_str));
 
@@ -219,10 +225,9 @@ i64 get_file_size(String path) {
 
 
 #if defined(GB_SYSTEM_WINDOWS)
-ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 	GB_ASSERT(fi != nullptr);
 
-	gbAllocator a = heap_allocator();
 
 	while (path.len > 0) {
 		Rune end = path[path.len-1];
@@ -239,9 +244,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 		return ReadDirectory_InvalidPath;
 	}
 	{
-		char *c_str = alloc_cstring(a, path);
-		defer (gb_free(a, c_str));
-
+		char *c_str = alloc_cstring(temporary_allocator(), path);
 		gbFile f = {};
 		gbFileError file_err = gb_file_open(&f, c_str);
 		defer (gb_file_close(&f));
@@ -258,6 +261,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 	}
 
 
+	gbAllocator a = heap_allocator();
 	char *new_path = gb_alloc_array(a, char, path.len+3);
 	defer (gb_free(a, new_path));
 
@@ -280,8 +284,8 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 
 	do {
 		wchar_t *filename_w = file_data.cFileName;
-		i64 size = cast(i64)file_data.nFileSizeLow;
-		size |= (cast(i64)file_data.nFileSizeHigh) << 32;
+		u64 size = cast(u64)file_data.nFileSizeLow;
+		size |= (cast(u64)file_data.nFileSizeHigh) << 32;
 		String name = string16_to_string(a, make_string16_c(filename_w));
 		if (name == "." || name == "..") {
 			gb_free(a, name.text);
@@ -299,7 +303,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 		FileInfo info = {};
 		info.name = name;
 		info.fullpath = path_to_full_path(a, filepath);
-		info.size = size;
+		info.size = cast(i64)size;
 		info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
 		array_add(fi, info);
 	} while (FindNextFileW(find_file, &file_data));
@@ -314,7 +318,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 
 #include <dirent.h>
 
-ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 	GB_ASSERT(fi != nullptr);
 
 	gbAllocator a = heap_allocator();

+ 7 - 7
src/priority_queue.cpp

@@ -7,7 +7,7 @@ struct PriorityQueue {
 };
 
 template <typename T>
-bool priority_queue_shift_down(PriorityQueue<T> *pq, isize i0, isize n) {
+gb_internal bool priority_queue_shift_down(PriorityQueue<T> *pq, isize i0, isize n) {
 	// O(n log n)
 	isize i = i0;
 	isize j, j1, j2;
@@ -29,7 +29,7 @@ bool priority_queue_shift_down(PriorityQueue<T> *pq, isize i0, isize n) {
 }
 
 template <typename T>
-void priority_queue_shift_up(PriorityQueue<T> *pq, isize j) {
+gb_internal void priority_queue_shift_up(PriorityQueue<T> *pq, isize j) {
 	while (0 <= j && j < pq->queue.count) {
 		isize i = (j-1)/2;
 		if (i == j || pq->cmp(&pq->queue[0], j, i) >= 0) {
@@ -43,20 +43,20 @@ void priority_queue_shift_up(PriorityQueue<T> *pq, isize j) {
 // NOTE(bill): When an element at index `i0` has changed its value, this will fix the
 // the heap ordering. This using a basic "heapsort" with shift up and a shift down parts.
 template <typename T>
-void priority_queue_fix(PriorityQueue<T> *pq, isize i) {
+gb_internal void priority_queue_fix(PriorityQueue<T> *pq, isize i) {
 	if (!priority_queue_shift_down(pq, i, pq->queue.count)) {
 		priority_queue_shift_up(pq, i);
 	}
 }
 
 template <typename T>
-void priority_queue_push(PriorityQueue<T> *pq, T const &value) {
+gb_internal void priority_queue_push(PriorityQueue<T> *pq, T const &value) {
 	array_add(&pq->queue, value);
 	priority_queue_shift_up(pq, pq->queue.count-1);
 }
 
 template <typename T>
-T priority_queue_pop(PriorityQueue<T> *pq) {
+gb_internal T priority_queue_pop(PriorityQueue<T> *pq) {
 	GB_ASSERT(pq->queue.count > 0);
 
 	isize n = pq->queue.count - 1;
@@ -67,7 +67,7 @@ T priority_queue_pop(PriorityQueue<T> *pq) {
 
 
 template <typename T>
-T priority_queue_remove(PriorityQueue<T> *pq, isize i) {
+gb_internal T priority_queue_remove(PriorityQueue<T> *pq, isize i) {
 	GB_ASSERT(0 <= i && i < pq->queue.count);
 	isize n = pq->queue.count - 1;
 	if (n != i) {
@@ -80,7 +80,7 @@ T priority_queue_remove(PriorityQueue<T> *pq, isize i) {
 
 
 template <typename T>
-PriorityQueue<T> priority_queue_create(Array<T> queue,
+gb_internal PriorityQueue<T> priority_queue_create(Array<T> queue,
                                        int  (* cmp) (T *q, isize i, isize j),
                                        void (* swap)(T *q, isize i, isize j)) {
 	PriorityQueue<T> pq = {};

+ 223 - 102
src/ptr_map.cpp

@@ -2,6 +2,13 @@
 
 typedef u32 MapIndex;
 
+enum {
+	MAP_CACHE_LINE_SIZE_POW = 6,
+	MAP_CACHE_LINE_SIZE = 1<<MAP_CACHE_LINE_SIZE_POW,
+	MAP_CACHE_LINE_MASK = MAP_CACHE_LINE_SIZE-1,
+};
+
+
 struct MapFindResult {
 	MapIndex hash_index;
 	MapIndex entry_prev;
@@ -21,12 +28,16 @@ struct PtrMapEntry {
 
 template <typename K, typename V>
 struct PtrMap {
-	Slice<MapIndex>           hashes;
-	Array<PtrMapEntry<K, V> > entries;
+	MapIndex *         hashes;
+	usize              hashes_count;
+	PtrMapEntry<K, V> *entries;
+	u32                count;
+	u32                entries_capacity;
 };
 
 
-u32 ptr_map_hash_key(uintptr key) {
+gb_internal gb_inline u32 ptr_map_hash_key(uintptr key) {
+	u32 res;
 #if defined(GB_ARCH_64_BIT)
 	key = (~key) + (key << 21);
 	key = key ^ (key >> 24);
@@ -34,80 +45,96 @@ u32 ptr_map_hash_key(uintptr key) {
 	key = key ^ (key >> 14);
 	key = (key + (key << 2)) + (key << 4);
 	key = key ^ (key << 28);
-	return cast(u32)key;
+	res = cast(u32)key;
 #elif defined(GB_ARCH_32_BIT)
 	u32 state = ((u32)key) * 747796405u + 2891336453u;
 	u32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
-	return (word >> 22u) ^ word;
+	res = (word >> 22u) ^ word;
 #endif
+	return res;
 }
-u32 ptr_map_hash_key(void const *key) {
+gb_internal gb_inline u32 ptr_map_hash_key(void const *key) {
 	return ptr_map_hash_key((uintptr)key);
 }
 
 
-template <typename K, typename V> void map_init             (PtrMap<K, V> *h, gbAllocator a, isize capacity = 16);
-template <typename K, typename V> void map_destroy          (PtrMap<K, V> *h);
-template <typename K, typename V> V *  map_get              (PtrMap<K, V> *h, K key);
-template <typename K, typename V> void map_set              (PtrMap<K, V> *h, K key, V const &value);
-template <typename K, typename V> void map_remove           (PtrMap<K, V> *h, K key);
-template <typename K, typename V> void map_clear            (PtrMap<K, V> *h);
-template <typename K, typename V> void map_grow             (PtrMap<K, V> *h);
-template <typename K, typename V> void map_rehash           (PtrMap<K, V> *h, isize new_count);
-template <typename K, typename V> void map_reserve          (PtrMap<K, V> *h, isize cap);
+template <typename K, typename V> gb_internal void map_init             (PtrMap<K, V> *h, isize capacity = 16);
+template <typename K, typename V> gb_internal void map_destroy          (PtrMap<K, V> *h);
+template <typename K, typename V> gb_internal V *  map_get              (PtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void map_set              (PtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> gb_internal bool map_set_if_not_previously_exists(PtrMap<K, V> *h, K key, V const &value); // returns true if it previously existed
+template <typename K, typename V> gb_internal void map_remove           (PtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void map_clear            (PtrMap<K, V> *h);
+template <typename K, typename V> gb_internal void map_grow             (PtrMap<K, V> *h);
+template <typename K, typename V> gb_internal void map_rehash           (PtrMap<K, V> *h, isize new_count);
+template <typename K, typename V> gb_internal void map_reserve          (PtrMap<K, V> *h, isize cap);
 
 #if PTR_MAP_ENABLE_MULTI_MAP
 // Mutlivalued map procedure
-template <typename K, typename V> PtrMapEntry<K, V> * multi_map_find_first(PtrMap<K, V> *h, K key);
-template <typename K, typename V> PtrMapEntry<K, V> * multi_map_find_next (PtrMap<K, V> *h, PtrMapEntry<K, V> *e);
-
-template <typename K, typename V> isize multi_map_count     (PtrMap<K, V> *h, K key);
-template <typename K, typename V> void  multi_map_get_all   (PtrMap<K, V> *h, K key, V *items);
-template <typename K, typename V> void  multi_map_insert    (PtrMap<K, V> *h, K key, V const &value);
-template <typename K, typename V> void  multi_map_remove    (PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e);
-template <typename K, typename V> void  multi_map_remove_all(PtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal PtrMapEntry<K, V> * multi_map_find_first(PtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal PtrMapEntry<K, V> * multi_map_find_next (PtrMap<K, V> *h, PtrMapEntry<K, V> *e);
+
+template <typename K, typename V> gb_internal isize multi_map_count     (PtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void  multi_map_get_all   (PtrMap<K, V> *h, K key, V *items);
+template <typename K, typename V> gb_internal void  multi_map_insert    (PtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> gb_internal void  multi_map_remove    (PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e);
+template <typename K, typename V> gb_internal void  multi_map_remove_all(PtrMap<K, V> *h, K key);
 #endif
 
+gb_internal gbAllocator map_allocator(void) {
+	return heap_allocator();
+}
+
 template <typename K, typename V>
-gb_inline void map_init(PtrMap<K, V> *h, gbAllocator a, isize capacity) {
+gb_internal gb_inline void map_init(PtrMap<K, V> *h, isize capacity) {
 	capacity = next_pow2_isize(capacity);
-	slice_init(&h->hashes,  a, capacity);
-	array_init(&h->entries, a, 0, capacity);
-	for (isize i = 0; i < capacity; i++) {
-		h->hashes.data[i] = MAP_SENTINEL;
-	}
+	map_reserve(h, capacity);
 }
 
 template <typename K, typename V>
-gb_inline void map_destroy(PtrMap<K, V> *h) {
-	slice_free(&h->hashes, h->entries.allocator);
-	array_free(&h->entries);
+gb_internal gb_inline void map_destroy(PtrMap<K, V> *h) {
+	gbAllocator a = map_allocator();
+	gb_free(a, h->hashes);
+	gb_free(a, h->entries);
 }
 
+template <typename K, typename V>
+gb_internal void map__resize_hashes(PtrMap<K, V> *h, usize count) {
+	h->hashes_count = cast(u32)resize_array_raw(&h->hashes, map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE);
+}
+
+template <typename K, typename V>
+gb_internal void map__reserve_entries(PtrMap<K, V> *h, usize capacity) {
+	h->entries_capacity = cast(u32)resize_array_raw(&h->entries, map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE);
+}
+
+
 template <typename K, typename V>
 gb_internal MapIndex map__add_entry(PtrMap<K, V> *h, K key) {
 	PtrMapEntry<K, V> e = {};
 	e.key = key;
 	e.next = MAP_SENTINEL;
-	array_add(&h->entries, e);
-	return cast(MapIndex)(h->entries.count-1);
+	map__reserve_entries(h, h->count+1);
+	h->entries[h->count++] = e;
+	return cast(MapIndex)(h->count-1);
 }
 
 template <typename K, typename V>
 gb_internal MapFindResult map__find(PtrMap<K, V> *h, K key) {
 	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
-	if (h->hashes.count == 0) {
+	if (h->hashes_count == 0) {
 		return fr;
 	}
 	u32 hash = ptr_map_hash_key(key);
-	fr.hash_index = cast(MapIndex)(hash & (h->hashes.count-1));
-	fr.entry_index = h->hashes.data[fr.hash_index];
+	fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+	fr.entry_index = h->hashes[fr.hash_index];
 	while (fr.entry_index != MAP_SENTINEL) {
-		if (h->entries.data[fr.entry_index].key == key) {
+		auto *entry = &h->entries[fr.entry_index];
+		if (entry->key == key) {
 			return fr;
 		}
 		fr.entry_prev = fr.entry_index;
-		fr.entry_index = h->entries.data[fr.entry_index].next;
+		fr.entry_index = entry->next;
 	}
 	return fr;
 }
@@ -115,41 +142,41 @@ gb_internal MapFindResult map__find(PtrMap<K, V> *h, K key) {
 template <typename K, typename V>
 gb_internal MapFindResult map__find_from_entry(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
 	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
-	if (h->hashes.count == 0) {
+	if (h->hashes_count == 0) {
 		return fr;
 	}
 	u32 hash = ptr_map_hash_key(e->key);
-	fr.hash_index  = cast(MapIndex)(hash & (h->hashes.count-1));
-	fr.entry_index = h->hashes.data[fr.hash_index];
+	fr.hash_index  = cast(MapIndex)(hash & (h->hashes_count-1));
+	fr.entry_index = h->hashes[fr.hash_index];
 	while (fr.entry_index != MAP_SENTINEL) {
-		if (&h->entries.data[fr.entry_index] == e) {
+		if (&h->entries[fr.entry_index] == e) {
 			return fr;
 		}
 		fr.entry_prev = fr.entry_index;
-		fr.entry_index = h->entries.data[fr.entry_index].next;
+		fr.entry_index = h->entries[fr.entry_index].next;
 	}
 	return fr;
 }
 
 template <typename K, typename V>
 gb_internal b32 map__full(PtrMap<K, V> *h) {
-	return 0.75f * h->hashes.count <= h->entries.count;
+	return 0.75f * h->hashes_count <= h->count;
 }
 
 template <typename K, typename V>
-gb_inline void map_grow(PtrMap<K, V> *h) {
-	isize new_count = gb_max(h->hashes.count<<1, 16);
+gb_internal gb_inline void map_grow(PtrMap<K, V> *h) {
+	isize new_count = gb_max(h->hashes_count<<1, 16);
 	map_rehash(h, new_count);
 }
 
 template <typename K, typename V>
-void map_reset_entries(PtrMap<K, V> *h) {
-	for (isize i = 0; i < h->hashes.count; i++) {
-		h->hashes.data[i] = MAP_SENTINEL;
+gb_internal void map_reset_entries(PtrMap<K, V> *h) {
+	for (usize i = 0; i < h->hashes_count; i++) {
+		h->hashes[i] = MAP_SENTINEL;
 	}
-	for (isize i = 0; i < h->entries.count; i++) {
+	for (usize i = 0; i < h->count; i++) {
 		MapFindResult fr;
-		PtrMapEntry<K, V> *e = &h->entries.data[i];
+		PtrMapEntry<K, V> *e = &h->entries[i];
 		e->next = MAP_SENTINEL;
 		fr = map__find_from_entry(h, e);
 		if (fr.entry_prev == MAP_SENTINEL) {
@@ -161,42 +188,88 @@ void map_reset_entries(PtrMap<K, V> *h) {
 }
 
 template <typename K, typename V>
-void map_reserve(PtrMap<K, V> *h, isize cap) {
-	array_reserve(&h->entries, cap);
-	if (h->entries.count*2 < h->hashes.count) {
+gb_internal void map_reserve(PtrMap<K, V> *h, isize cap) {
+	if (h->count*2 < h->hashes_count) {
 		return;
 	}
-	slice_resize(&h->hashes, h->entries.allocator, cap*2);
+	map__reserve_entries(h, cap);
+	map__resize_hashes(h, cap*2);
 	map_reset_entries(h);
 }
 
 
 template <typename K, typename V>
-void map_rehash(PtrMap<K, V> *h, isize new_count) {
+gb_internal void map_rehash(PtrMap<K, V> *h, isize new_count) {
 	map_reserve(h, new_count);
 }
 
 template <typename K, typename V>
-V *map_get(PtrMap<K, V> *h, K key) {
-	MapIndex index = map__find(h, key).entry_index;
-	if (index != MAP_SENTINEL) {
-		return &h->entries.data[index].value;
+gb_internal V *map_get(PtrMap<K, V> *h, K key) {
+	MapIndex hash_index  = MAP_SENTINEL;
+	MapIndex entry_prev  = MAP_SENTINEL;
+	MapIndex entry_index = MAP_SENTINEL;
+	if (h->hashes_count != 0) {
+		u32 hash = ptr_map_hash_key(key);
+		hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+		entry_index = h->hashes[hash_index];
+		while (entry_index != MAP_SENTINEL) {
+			auto *entry = &h->entries[entry_index];
+			if (entry->key == key) {
+				return &entry->value;
+			}
+			entry_prev = entry_index;
+			entry_index = entry->next;
+		}
+	}
+	return nullptr;
+}
+template <typename K, typename V>
+gb_internal V *map_try_get(PtrMap<K, V> *h, K key, MapFindResult *fr_) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes_count != 0) {
+		u32 hash = ptr_map_hash_key(key);
+		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->key == key) {
+				return &entry->value;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = entry->next;
+		}
+	}
+	if (h->hashes_count == 0 || map__full(h)) {
+		map_grow(h);
 	}
+	if (fr_) *fr_ = fr;
 	return nullptr;
 }
 
+
 template <typename K, typename V>
-V &map_must_get(PtrMap<K, V> *h, K key) {
-	MapIndex index = map__find(h, key).entry_index;
-	GB_ASSERT(index != MAP_SENTINEL);
-	return h->entries.data[index].value;
+gb_internal void map_set_internal_from_try_get(PtrMap<K, V> *h, K key, V const &value, MapFindResult const &fr) {
+	MapIndex index = map__add_entry(h, 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;
 }
 
 template <typename K, typename V>
-void map_set(PtrMap<K, V> *h, K key, V const &value) {
+gb_internal V &map_must_get(PtrMap<K, V> *h, K key) {
+	V *ptr = map_get(h, key);
+	GB_ASSERT(ptr != nullptr);
+	return *ptr;
+}
+
+template <typename K, typename V>
+gb_internal void map_set(PtrMap<K, V> *h, K key, V const &value) {
 	MapIndex index;
 	MapFindResult fr;
-	if (h->hashes.count == 0) {
+	if (h->hashes_count == 0) {
 		map_grow(h);
 	}
 	fr = map__find(h, key);
@@ -205,44 +278,71 @@ void map_set(PtrMap<K, V> *h, K key, V const &value) {
 	} else {
 		index = map__add_entry(h, key);
 		if (fr.entry_prev != MAP_SENTINEL) {
-			h->entries.data[fr.entry_prev].next = index;
+			h->entries[fr.entry_prev].next = index;
+		} else {
+			h->hashes[fr.hash_index] = index;
+		}
+	}
+	h->entries[index].value = value;
+
+	if (map__full(h)) {
+		map_grow(h);
+	}
+}
+
+// returns true if it previously existed
+template <typename K, typename V>
+gb_internal bool map_set_if_not_previously_exists(PtrMap<K, V> *h, K key, V const &value) {
+	MapIndex index;
+	MapFindResult fr;
+	if (h->hashes_count == 0) {
+		map_grow(h);
+	}
+	fr = map__find(h, key);
+	if (fr.entry_index != MAP_SENTINEL) {
+		return true;
+	} else {
+		index = map__add_entry(h, key);
+		if (fr.entry_prev != MAP_SENTINEL) {
+			h->entries[fr.entry_prev].next = index;
 		} else {
-			h->hashes.data[fr.hash_index] = index;
+			h->hashes[fr.hash_index] = index;
 		}
 	}
-	h->entries.data[index].value = value;
+	h->entries[index].value = value;
 
 	if (map__full(h)) {
 		map_grow(h);
 	}
+	return false;
 }
 
 
 template <typename K, typename V>
-void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
+gb_internal void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
 	MapFindResult last;
 	if (fr.entry_prev == MAP_SENTINEL) {
-		h->hashes.data[fr.hash_index] = h->entries.data[fr.entry_index].next;
+		h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
 	} else {
-		h->entries.data[fr.entry_prev].next = h->entries.data[fr.entry_index].next;
+		h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
 	}
-	if (fr.entry_index == h->entries.count-1) {
-		array_pop(&h->entries);
+	if (fr.entry_index == h->count-1) {
+		h->count--;
 		return;
 	}
-	h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1];
-	array_pop(&h->entries);
+	h->entries[fr.entry_index] = h->entries[h->count-1];
+	h->count--;
 	
-	last = map__find(h, h->entries.data[fr.entry_index].key);
+	last = map__find(h, h->entries[fr.entry_index].key);
 	if (last.entry_prev != MAP_SENTINEL) {
-		h->entries.data[last.entry_prev].next = fr.entry_index;
+		h->entries[last.entry_prev].next = fr.entry_index;
 	} else {
-		h->hashes.data[last.hash_index] = fr.entry_index;
+		h->hashes[last.hash_index] = fr.entry_index;
 	}
 }
 
 template <typename K, typename V>
-void map_remove(PtrMap<K, V> *h, K key) {
+gb_internal void map_remove(PtrMap<K, V> *h, K key) {
 	MapFindResult fr = map__find(h, key);
 	if (fr.entry_index != MAP_SENTINEL) {
 		map__erase(h, fr);
@@ -250,38 +350,38 @@ void map_remove(PtrMap<K, V> *h, K key) {
 }
 
 template <typename K, typename V>
-gb_inline void map_clear(PtrMap<K, V> *h) {
-	array_clear(&h->entries);
-	for (isize i = 0; i < h->hashes.count; i++) {
-		h->hashes.data[i] = MAP_SENTINEL;
+gb_internal gb_inline void map_clear(PtrMap<K, V> *h) {
+	h->count = 0;
+	for (usize i = 0; i < h->hashes_count; i++) {
+		h->hashes[i] = MAP_SENTINEL;
 	}
 }
 
 
 #if PTR_MAP_ENABLE_MULTI_MAP
 template <typename K, typename V>
-PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
+gb_internal PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
 	MapIndex i = map__find(h, key).entry_index;
 	if (i == MAP_SENTINEL) {
 		return nullptr;
 	}
-	return &h->entries.data[i];
+	return &h->entries[i];
 }
 
 template <typename K, typename V>
-PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+gb_internal PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
 	MapIndex i = e->next;
 	while (i != MAP_SENTINEL) {
-		if (h->entries.data[i].key == e->key) {
-			return &h->entries.data[i];
+		if (h->entries[i].key == e->key) {
+			return &h->entries[i];
 		}
-		i = h->entries.data[i].next;
+		i = h->entries[i].next;
 	}
 	return nullptr;
 }
 
 template <typename K, typename V>
-isize multi_map_count(PtrMap<K, V> *h, K key) {
+gb_internal isize multi_map_count(PtrMap<K, V> *h, K key) {
 	isize count = 0;
 	PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
 	while (e != nullptr) {
@@ -292,8 +392,8 @@ isize multi_map_count(PtrMap<K, V> *h, K key) {
 }
 
 template <typename K, typename V>
-void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
-	isize i = 0;
+gb_internal void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
+	usize i = 0;
 	PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
 	while (e != nullptr) {
 		items[i++] = e->value;
@@ -302,22 +402,22 @@ void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
 }
 
 template <typename K, typename V>
-void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
+gb_internal void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
 	MapFindResult fr;
 	MapIndex i;
-	if (h->hashes.count == 0) {
+	if (h->hashes_count == 0) {
 		map_grow(h);
 	}
 	// Make
 	fr = map__find(h, key);
 	i = map__add_entry(h, key);
 	if (fr.entry_prev == MAP_SENTINEL) {
-		h->hashes.data[fr.hash_index] = i;
+		h->hashes[fr.hash_index] = i;
 	} else {
-		h->entries.data[fr.entry_prev].next = i;
+		h->entries[fr.entry_prev].next = i;
 	}
-	h->entries.data[i].next = fr.entry_index;
-	h->entries.data[i].value = value;
+	h->entries[i].next = fr.entry_index;
+	h->entries[i].value = value;
 	// Grow if needed
 	if (map__full(h)) {
 		map_grow(h);
@@ -325,7 +425,7 @@ void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
 }
 
 template <typename K, typename V>
-void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
+gb_internal void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
 	MapFindResult fr = map__find_from_entry(h, e);
 	if (fr.entry_index != MAP_SENTINEL) {
 		map__erase(h, fr);
@@ -333,9 +433,30 @@ void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
 }
 
 template <typename K, typename V>
-void multi_map_remove_all(PtrMap<K, V> *h, K key) {
+gb_internal void multi_map_remove_all(PtrMap<K, V> *h, K key) {
 	while (map_get(h, key) != nullptr) {
 		map_remove(h, key);
 	}
 }
 #endif
+
+
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> *begin(PtrMap<K, V> &m) {
+	return m.entries;
+}
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> const *begin(PtrMap<K, V> const &m) {
+	return m.entries;
+}
+
+
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> *end(PtrMap<K, V> &m) {
+	return m.entries + m.count;
+}
+
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> const *end(PtrMap<K, V> const &m) {
+	return m.entries + m.count;
+}

+ 142 - 173
src/ptr_set.cpp

@@ -1,235 +1,204 @@
-template <typename T>
-struct PtrSetEntry {
-	T        ptr;
-	MapIndex next;
-};
-
 template <typename T>
 struct PtrSet {
-	Slice<MapIndex>    hashes;
-	Array<PtrSetEntry<T>> entries;
+	static_assert(TypeIsPointer<T>::value, "PtrSet::T must be a pointer");
+	static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull);
+
+	T *   keys;
+	usize count;
+	usize capacity;
 };
 
-template <typename T> void ptr_set_init   (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
-template <typename T> void ptr_set_destroy(PtrSet<T> *s);
-template <typename T> T    ptr_set_add    (PtrSet<T> *s, T ptr);
-template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existed
-template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
-template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
-template <typename T> void ptr_set_clear  (PtrSet<T> *s);
-template <typename T> void ptr_set_grow   (PtrSet<T> *s);
-template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
-template <typename T> void ptr_set_reserve(PtrSet<T> *h, isize cap);
+template <typename T> gb_internal void ptr_set_init   (PtrSet<T> *s, isize capacity = 16);
+template <typename T> gb_internal void ptr_set_destroy(PtrSet<T> *s);
+template <typename T> gb_internal T    ptr_set_add    (PtrSet<T> *s, T ptr);
+template <typename T> gb_internal bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existed
+template <typename T> gb_internal bool ptr_set_exists (PtrSet<T> *s, T ptr);
+template <typename T> gb_internal void ptr_set_remove (PtrSet<T> *s, T ptr);
+template <typename T> gb_internal void ptr_set_clear  (PtrSet<T> *s);
 
+gb_internal gbAllocator ptr_set_allocator(void) {
+	return heap_allocator();
+}
 
 template <typename T>
-void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
+gb_internal void ptr_set_init(PtrSet<T> *s, isize capacity) {
+	GB_ASSERT(s->keys == nullptr);
 	if (capacity != 0) {
 		capacity = next_pow2_isize(gb_max(16, capacity));
+		s->keys = gb_alloc_array(ptr_set_allocator(), T, capacity);
+		// This memory will be zeroed, no need to explicitly zero it
 	}
-
-	slice_init(&s->hashes,  a, capacity);
-	array_init(&s->entries, a, 0, capacity);
-	for (isize i = 0; i < capacity; i++) {
-		s->hashes.data[i] = MAP_SENTINEL;
-	}
+	s->count = 0;
+	s->capacity = capacity;
 }
 
 template <typename T>
-void ptr_set_destroy(PtrSet<T> *s) {
-	slice_free(&s->hashes, s->entries.allocator);
-	array_free(&s->entries);
+gb_internal void ptr_set_destroy(PtrSet<T> *s) {
+	gb_free(ptr_set_allocator(), s->keys);
+	s->keys = nullptr;
+	s->count = 0;
+	s->capacity = 0;
 }
 
 template <typename T>
-gb_internal MapIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
-	PtrSetEntry<T> e = {};
-	e.ptr = ptr;
-	e.next = MAP_SENTINEL;
-	array_add(&s->entries, e);
-	return cast(MapIndex)(s->entries.count-1);
-}
-
-
-template <typename T>
-gb_internal MapFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
-	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
-	if (s->hashes.count != 0) {
-		u32 hash = ptr_map_hash_key(ptr);
-		fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1));
-		fr.entry_index = s->hashes.data[fr.hash_index];
-		while (fr.entry_index != MAP_SENTINEL) {
-			if (s->entries.data[fr.entry_index].ptr == ptr) {
-				return fr;
+gb_internal isize ptr_set__find(PtrSet<T> *s, T ptr) {
+	GB_ASSERT(ptr != nullptr);
+	if (s->count != 0) {
+	#if 0
+		for (usize i = 0; i < s->capacity; i++) {
+			if (s->keys[i] == ptr) {
+				return i;
 			}
-			fr.entry_prev = fr.entry_index;
-			fr.entry_index = s->entries.data[fr.entry_index].next;
 		}
-	}
-	return fr;
-}
-
-template <typename T>
-gb_internal MapFindResult ptr_set__find_from_entry(PtrSet<T> *s, PtrSetEntry<T> *e) {
-	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
-	if (s->hashes.count != 0) {
-		u32 hash = ptr_map_hash_key(e->ptr);
-		fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1));
-		fr.entry_index = s->hashes.data[fr.hash_index];
-		while (fr.entry_index != MAP_SENTINEL) {
-			if (&s->entries.data[fr.entry_index] == e) {
-				return fr;
+	#else
+		u32 hash = ptr_map_hash_key(ptr);
+		usize mask = s->capacity-1;
+		usize hash_index = cast(usize)hash & mask;
+		for (usize i = 0; i < s->capacity; i++) {
+			T key = s->keys[hash_index];
+			if (key == ptr) {
+				return hash_index;
+			} else if (key == nullptr) {
+				return -1;
 			}
-			fr.entry_prev = fr.entry_index;
-			fr.entry_index = s->entries.data[fr.entry_index].next;
+			hash_index = (hash_index+1)&mask;
 		}
+	#endif
 	}
-	return fr;
+	return -1;
 }
 
 template <typename T>
 gb_internal bool ptr_set__full(PtrSet<T> *s) {
-	return 0.75f * s->hashes.count <= s->entries.count;
-}
-
-template <typename T>
-gb_inline void ptr_set_grow(PtrSet<T> *s) {
-	isize new_count = gb_max(s->hashes.count<<1, 16);
-	ptr_set_rehash(s, new_count);
+	return 0.75f * s->capacity <= s->count;
 }
 
 template <typename T>
-void ptr_set_reset_entries(PtrSet<T> *s) {
-	for (isize i = 0; i < s->hashes.count; i++) {
-		s->hashes.data[i] = MAP_SENTINEL;
-	}
-	for (isize i = 0; i < s->entries.count; i++) {
-		MapFindResult fr;
-		PtrSetEntry<T> *e = &s->entries.data[i];
-		e->next = MAP_SENTINEL;
-		fr = ptr_set__find_from_entry(s, e);
-		if (fr.entry_prev == MAP_SENTINEL) {
-			s->hashes[fr.hash_index] = cast(MapIndex)i;
-		} else {
-			s->entries[fr.entry_prev].next = cast(MapIndex)i;
-		}
+gb_internal gb_inline void ptr_set_grow(PtrSet<T> *old_set) {
+	if (old_set->capacity == 0) {
+		ptr_set_init(old_set);
+		return;
 	}
-}
 
-template <typename T>
-void ptr_set_reserve(PtrSet<T> *s, isize cap) {
-	array_reserve(&s->entries, cap);
-	if (s->entries.count*2 < s->hashes.count) {
-		return;
+	PtrSet<T> new_set = {};
+	ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
+
+	for (T ptr : *old_set) {
+		bool was_new = ptr_set_update(&new_set, ptr);
+		GB_ASSERT(!was_new);
 	}
-	slice_resize(&s->hashes, s->entries.allocator, cap*2);
-	ptr_set_reset_entries(s);
-}
+	GB_ASSERT(old_set->count == new_set.count);
 
+	ptr_set_destroy(old_set);
 
-template <typename T>
-void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
-	ptr_set_reserve(s, new_count);
+	*old_set = new_set;
 }
 
+
 template <typename T>
-gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
-	isize index = ptr_set__find(s, ptr).entry_index;
-	return index != MAP_SENTINEL;
+gb_internal gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
+	return ptr_set__find(s, ptr) >= 0;
 }
 
+
 template <typename T>
-gb_inline isize ptr_entry_index(PtrSet<T> *s, T ptr) {
-	isize index = ptr_set__find(s, ptr).entry_index;
-	if (index != MAP_SENTINEL) {
-		return index;
+gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
+	if (ptr_set_exists(s, ptr)) {
+		return true;
 	}
-	return -1;
-}
 
-// Returns true if it already exists
-template <typename T>
-T ptr_set_add(PtrSet<T> *s, T ptr) {
-	MapIndex index;
-	MapFindResult fr;
-	if (s->hashes.count == 0) {
+	if (s->keys == nullptr) {
+		ptr_set_init(s);
+	} else if (ptr_set__full(s)) {
 		ptr_set_grow(s);
 	}
-	fr = ptr_set__find(s, ptr);
-	if (fr.entry_index == MAP_SENTINEL) {
-		index = ptr_set__add_entry(s, ptr);
-		if (fr.entry_prev != MAP_SENTINEL) {
-			s->entries.data[fr.entry_prev].next = index;
-		} else {
-			s->hashes.data[fr.hash_index] = index;
+	GB_ASSERT(s->count < s->capacity);
+	GB_ASSERT(s->capacity >= 0);
+
+	usize mask = s->capacity-1;
+	u32 hash = ptr_map_hash_key(ptr);
+	usize hash_index = (cast(usize)hash) & mask;
+	GB_ASSERT(hash_index < s->capacity);
+	for (usize i = 0; i < s->capacity; i++) {
+		T *key = &s->keys[hash_index];
+		GB_ASSERT(*key != ptr);
+		if (*key == (T)PtrSet<T>::TOMBSTONE || *key == nullptr) {
+			*key = ptr;
+			s->count++;
+			return false;
 		}
+		hash_index = (hash_index+1)&mask;
 	}
-	if (ptr_set__full(s)) {
-		ptr_set_grow(s);
-	}
+
+	GB_PANIC("ptr set out of memory");
+	return false;
+}
+
+template <typename T>
+gb_internal T ptr_set_add(PtrSet<T> *s, T ptr) {
+	ptr_set_update(s, ptr);
 	return ptr;
 }
 
+
 template <typename T>
-bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
-	bool exists = false;
-	MapIndex index;
-	MapFindResult fr;
-	if (s->hashes.count == 0) {
-		ptr_set_grow(s);
-	}
-	fr = ptr_set__find(s, ptr);
-	if (fr.entry_index != MAP_SENTINEL) {
-		exists = true;
-	} else {
-		index = ptr_set__add_entry(s, ptr);
-		if (fr.entry_prev != MAP_SENTINEL) {
-			s->entries.data[fr.entry_prev].next = index;
-		} else {
-			s->hashes.data[fr.hash_index] = index;
-		}
+gb_internal void ptr_set_remove(PtrSet<T> *s, T ptr) {
+	isize index = ptr_set__find(s, ptr);
+	if (index >= 0) {
+		GB_ASSERT(s->count > 0);
+		s->keys[index] = (T)PtrSet<T>::TOMBSTONE;
+		s->count--;
 	}
-	if (ptr_set__full(s)) {
-		ptr_set_grow(s);
-	}
-	return exists;
 }
 
-
+template <typename T>
+gb_internal gb_inline void ptr_set_clear(PtrSet<T> *s) {
+	s->count = 0;
+	gb_zero_size(s->keys, s->capacity*gb_size_of(T));
+}
 
 template <typename T>
-void ptr_set__erase(PtrSet<T> *s, MapFindResult fr) {
-	MapFindResult last;
-	if (fr.entry_prev == MAP_SENTINEL) {
-		s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next;
-	} else {
-		s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next;
-	}
-	if (cast(isize)fr.entry_index == s->entries.count-1) {
-		array_pop(&s->entries);
-		return;
+struct PtrSetIterator {
+	PtrSet<T> *set;
+	usize index;
+
+	PtrSetIterator<T> &operator++() noexcept {
+		for (;;) {
+			++index;
+			if (set->capacity == index) {
+				return *this;
+			}
+			T key = set->keys[index];
+			if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
+				return *this;
+			}
+		}
 	}
-	s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1];
-	last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr);
-	if (last.entry_prev != MAP_SENTINEL) {
-		s->entries.data[last.entry_prev].next = fr.entry_index;
-	} else {
-		s->hashes.data[last.hash_index] = fr.entry_index;
+
+	bool operator==(PtrSetIterator<T> const &other) const noexcept {
+		return this->set == other.set && this->index == other.index;
 	}
-}
 
-template <typename T>
-void ptr_set_remove(PtrSet<T> *s, T ptr) {
-	MapFindResult fr = ptr_set__find(s, ptr);
-	if (fr.entry_index != MAP_SENTINEL) {
-		ptr_set__erase(s, fr);
+
+	operator T *() const {
+		return &set->keys[index];
 	}
-}
+};
+
 
 template <typename T>
-gb_inline void ptr_set_clear(PtrSet<T> *s) {
-	array_clear(&s->entries);
-	for (isize i = 0; i < s->hashes.count; i++) {
-		s->hashes.data[i] = MAP_SENTINEL;
+gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
+	usize index = 0;
+	while (index < set.capacity) {
+		T key = set.keys[index];
+		if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
+			break;
+		}
+		index++;
 	}
+	return PtrSetIterator<T>{&set, index};
 }
+template <typename T>
+gb_internal PtrSetIterator<T> end(PtrSet<T> &set) noexcept {
+	return PtrSetIterator<T>{&set, set.capacity};
+}

+ 0 - 1030
src/query_data.cpp

@@ -1,1030 +0,0 @@
-struct QueryValue;
-struct QueryValuePair;
-
-gbAllocator query_value_allocator = {};
-
-enum QueryKind {
-	Query_Invalid,
-	Query_String,
-	Query_Boolean,
-	Query_Integer,
-	Query_Float,
-	Query_Array,
-	Query_Map,
-};
-
-struct QueryValuePair {
-	String key;
-	QueryValue *value;
-};
-
-
-struct QueryValue {
-	QueryKind kind;
-	bool packed;
-};
-
-struct QueryValueString : QueryValue {
-	QueryValueString(String const &v) {
-		kind = Query_String;
-		value = v;
-		packed = false;
-	}
-	String value;
-};
-
-struct QueryValueBoolean : QueryValue {
-	QueryValueBoolean(bool v) {
-		kind = Query_Boolean;
-		value = v;
-		packed = false;
-	}
-	bool value;
-};
-
-struct QueryValueInteger : QueryValue {
-	QueryValueInteger(i64 v) {
-		kind = Query_Integer;
-		value = v;
-		packed = false;
-	}
-	i64 value;
-};
-
-struct QueryValueFloat : QueryValue {
-	QueryValueFloat(f64 v) {
-		kind = Query_Float;
-		value = v;
-		packed = false;
-	}
-	f64 value;
-};
-
-struct QueryValueArray : QueryValue {
-	QueryValueArray() {
-		kind = Query_Array;
-		array_init(&value, query_value_allocator);
-		packed = false;
-	}
-	QueryValueArray(Array<QueryValue *> const &v) {
-		kind = Query_Array;
-		value = v;
-		packed = false;
-	}
-	Array<QueryValue *> value;
-
-	void reserve(isize cap) {
-		array_reserve(&value, cap);
-	}
-	void add(QueryValue *v) {
-		array_add(&value, v);
-	}
-	void add(char const *v) {
-		add(make_string_c(cast(char *)v));
-	}
-	void add(String const &v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueString);
-		*val = QueryValueString(v);
-		add(val);
-	}
-	void add(bool v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean);
-		*val = QueryValueBoolean(v);
-		add(val);
-	}
-	void add(i64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueInteger);
-		*val = QueryValueInteger(v);
-		add(val);
-	}
-	void add(f64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueFloat);
-		*val = QueryValueFloat(v);
-		add(val);
-	}
-};
-
-struct QueryValueMap : QueryValue {
-	QueryValueMap() {
-		kind = Query_Map;
-		array_init(&value, query_value_allocator);
-		packed = false;
-	}
-	QueryValueMap(Array<QueryValuePair> const &v) {
-		kind = Query_Map;
-		value = v;
-		packed = false;
-	}
-	Array<QueryValuePair> value;
-
-
-	void reserve(isize cap) {
-		array_reserve(&value, cap);
-	}
-	void add(char const *k, QueryValue *v) {
-		add(make_string_c(cast(char *)k), v);
-	}
-	void add(String const &k, QueryValue *v) {
-		QueryValuePair kv = {k, v};
-		array_add(&value, kv);
-	}
-
-	void add(char const *k, String const &v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueString);
-		*val = QueryValueString(v);
-		add(k, val);
-	}
-	void add(char const *k, char const *v) {
-		add(k, make_string_c(cast(char *)v));
-	}
-	void add(char const *k, bool v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean);
-		*val = QueryValueBoolean(v);
-		add(k, val);
-	}
-	void add(char const *k, i64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueInteger);
-		*val = QueryValueInteger(v);
-		add(k, val);
-	}
-	void add(char const *k, f64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueFloat);
-		*val = QueryValueFloat(v);
-		add(k, val);
-	}
-	void add(String const &k, String const &v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueString);
-		*val = QueryValueString(v);
-		add(k, val);
-	}
-	void add(String const &k, char const *v) {
-		add(k, make_string_c(cast(char *)v));
-	}
-	void add(String const &k, bool v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueBoolean);
-		*val = QueryValueBoolean(v);
-		add(k, val);
-	}
-	void add(String const &k, i64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueInteger);
-		*val = QueryValueInteger(v);
-		add(k, val);
-	}
-	void add(String const &k, f64 v) {
-		auto val = gb_alloc_item(query_value_allocator, QueryValueFloat);
-		*val = QueryValueFloat(v);
-		add(k, val);
-	}
-};
-
-
-#define DEF_QUERY_PROC(TYPE, VALUETYPE, NAME) TYPE *NAME(VALUETYPE value) { \
-	auto v = gb_alloc_item(query_value_allocator, TYPE); \
-	*v = TYPE(value); \
-	return v; \
-}
-#define DEF_QUERY_PROC0(TYPE, NAME) TYPE *NAME() { \
-	auto v = gb_alloc_item(query_value_allocator, TYPE); \
-	*v = TYPE(); \
-	return v; \
-}
-
-DEF_QUERY_PROC(QueryValueString,  String const &,                query_value_string);
-DEF_QUERY_PROC(QueryValueBoolean, bool,                          query_value_boolean);
-DEF_QUERY_PROC(QueryValueInteger, i64,                           query_value_integer);
-DEF_QUERY_PROC(QueryValueFloat,   f64,                           query_value_float);
-DEF_QUERY_PROC(QueryValueArray,   Array<QueryValue *> const &,   query_value_array);
-DEF_QUERY_PROC(QueryValueMap,     Array<QueryValuePair> const &, query_value_map);
-DEF_QUERY_PROC0(QueryValueArray,  query_value_array);
-DEF_QUERY_PROC0(QueryValueMap,    query_value_map);
-
-isize qprintf(bool format, isize indent, char const *fmt, ...) {
-	if (format) while (indent --> 0) {
-		gb_printf("\t");
-	}
-	va_list va;
-	va_start(va, fmt);
-	isize res = gb_printf_va(fmt, va);
-	va_end(va);
-	return res;
-}
-
-bool qv_valid_char(u8 c) {
-	if (c >= 0x80) {
-		return false;
-	}
-
-	switch (c) {
-	case '\"':
-	case '\n':
-	case '\r':
-	case '\t':
-	case '\v':
-	case '\f':
-		return false;
-	}
-
-	return true;
-}
-
-void print_query_data_as_json(QueryValue *value, bool format = true, isize indent = 0) {
-	if (value == nullptr) {
-		gb_printf("null");
-		return;
-	}
-	switch (value->kind) {
-	case Query_String: {
-		auto v = cast(QueryValueString *)value;
-		String name = v->value;
-		isize extra = 0;
-		for (isize i = 0; i < name.len; i++) {
-			u8 c = name[i];
-			if (!qv_valid_char(c)) {
-				extra += 5;
-			}
-		}
-
-		if (extra == 0) {
-			gb_printf("\"%.*s\"", LIT(name));
-			return;
-		}
-
-		char const hex_table[] = "0123456789ABCDEF";
-		isize buf_len = name.len + extra + 2 + 1;
-
-		u8 *buf = gb_alloc_array(temporary_allocator(), u8, buf_len);
-
-		isize j = 0;
-
-		for (isize i = 0; i < name.len; i++) {
-			u8 c = name[i];
-			if (qv_valid_char(c)) {
-				buf[j+0] = c;
-				j += 1;
-			} else if (c == '"') {
-				buf[j+0] = '\\';
-				buf[j+1] = '\"';
-				j += 2;
-			} else {
-				switch (c) {
-				case '\n': buf[j+0] = '\\'; buf[j+1] = 'n'; j += 2; break;
-				case '\r': buf[j+0] = '\\'; buf[j+1] = 'r'; j += 2; break;
-				case '\t': buf[j+0] = '\\'; buf[j+1] = 't'; j += 2; break;
-				case '\v': buf[j+0] = '\\'; buf[j+1] = 'v'; j += 2; break;
-				case '\f':
-				default:
-					buf[j+0] = '\\';
-					buf[j+1] = hex_table[0];
-					buf[j+2] = hex_table[0];
-					buf[j+3] = hex_table[c >> 4];
-					buf[j+4] = hex_table[c & 0x0f];
-					j += 5;
-					break;
-				}
-			}
-		}
-
-		gb_printf("\"%s\"", buf);
-		return;
-	}
-	case Query_Boolean: {
-		auto v = cast(QueryValueBoolean *)value;
-		if (v->value) {
-			gb_printf("true");
-		} else {
-			gb_printf("false");
-		}
-		return;
-	}
-	case Query_Integer: {
-		auto v = cast(QueryValueInteger *)value;
-		gb_printf("%lld", cast(long long)v->value);
-		return;
-	}
-	case Query_Float: {
-		auto v = cast(QueryValueFloat *)value;
-		gb_printf("%f", v->value);
-		return;
-	}
-	case Query_Array: {
-		auto v = cast(QueryValueArray *)value;
-		if (v->value.count > 0) {
-			bool ff = format && !v->packed;
-			gb_printf("[");
-			if (ff) gb_printf("\n");
-			for_array(i, v->value) {
-				qprintf(ff, indent+1, "");
-				print_query_data_as_json(v->value[i], ff, indent+1);
-				if (i < v->value.count-1) {
-					gb_printf(",");
-					if (!ff && format) {
-						gb_printf(" ");
-					}
-				}
-				if (ff) gb_printf("\n");
-			}
-			qprintf(ff, indent, "]");
-		} else {
-			gb_printf("[]");
-		}
-		return;
-	}
-	case Query_Map: {
-		auto v = cast(QueryValueMap *)value;
-		if (v->value.count > 0) {
-			bool ff = format && !v->packed;
-			gb_printf("{");
-			if (ff) gb_printf("\n");
-			for_array(i, v->value) {
-				auto kv = v->value[i];
-				qprintf(ff, indent+1, "\"%.*s\":", LIT(kv.key));
-				if (format) gb_printf(" ");
-				print_query_data_as_json(kv.value, ff, indent+1);
-				if (i < v->value.count-1) {
-					gb_printf(",");
-					if (!ff && format) {
-						gb_printf(" ");
-					}
-				}
-				if (ff) gb_printf("\n");
-			}
-			qprintf(ff, indent, "}");
-		} else {
-			gb_printf("{}");
-		}
-		return;
-	}
-	}
-}
-
-
-
-int query_data_package_compare(void const *a, void const *b) {
-	AstPackage *x = *cast(AstPackage *const *)a;
-	AstPackage *y = *cast(AstPackage *const *)b;
-
-	if (x == y) {
-		return 0;
-	}
-
-	if (x != nullptr && y != nullptr) {
-		return string_compare(x->name, y->name);
-	} else if (x != nullptr && y == nullptr) {
-		return -1;
-	} else if (x == nullptr && y != nullptr) {
-		return +1;
-	}
-	return 0;
-}
-
-int query_data_definition_compare(void const *a, void const *b) {
-	Entity *x = *cast(Entity *const *)a;
-	Entity *y = *cast(Entity *const *)b;
-
-	if (x == y) {
-		return 0;
-	} else if (x != nullptr && y == nullptr) {
-		return -1;
-	} else if (x == nullptr && y != nullptr) {
-		return +1;
-	}
-
-	if (x->pkg != y->pkg) {
-		i32 res = query_data_package_compare(&x->pkg, &y->pkg);
-		if (res != 0) {
-			return res;
-		}
-	}
-
-	return string_compare(x->token.string, y->token.string);
-}
-
-int entity_name_compare(void const *a, void const *b) {
-	Entity *x = *cast(Entity *const *)a;
-	Entity *y = *cast(Entity *const *)b;
-	if (x == y) {
-		return 0;
-	} else if (x != nullptr && y == nullptr) {
-		return -1;
-	} else if (x == nullptr && y != nullptr) {
-		return +1;
-	}
-	return string_compare(x->token.string, y->token.string);
-}
-
-
-void generate_and_print_query_data_global_definitions(Checker *c, Timings *timings);
-void generate_and_print_query_data_go_to_definitions(Checker *c);
-
-void generate_and_print_query_data(Checker *c, Timings *timings) {
-	query_value_allocator = heap_allocator();
-	switch (build_context.query_data_set_settings.kind) {
-	case QueryDataSet_GlobalDefinitions:
-		generate_and_print_query_data_global_definitions(c, timings);
-		return;
-	case QueryDataSet_GoToDefinitions:
-		generate_and_print_query_data_go_to_definitions(c);
-		return;
-	}
-}
-
-
-void generate_and_print_query_data_global_definitions(Checker *c, Timings *timings) {
-	auto *root = query_value_map();
-
-	if (global_error_collector.errors.count > 0) {
-		auto *errors = query_value_array();
-		root->add("errors", errors);
-		for_array(i, global_error_collector.errors) {
-			String err = string_trim_whitespace(global_error_collector.errors[i]);
-			errors->add(err);
-		}
-
-	}
-
-	{ // Packages
-		auto *packages = query_value_array();
-		root->add("packages", packages);
-
-		auto sorted_packages = array_make<AstPackage *>(query_value_allocator, 0, c->info.packages.entries.count);
-		defer (array_free(&sorted_packages));
-
-		for_array(i, c->info.packages.entries) {
-			AstPackage *pkg = c->info.packages.entries[i].value;
-			if (pkg != nullptr) {
-				array_add(&sorted_packages, pkg);
-			}
-		}
-		gb_sort_array(sorted_packages.data, sorted_packages.count, query_data_package_compare);
-		packages->reserve(sorted_packages.count);
-
-		for_array(i, sorted_packages) {
-			AstPackage *pkg = sorted_packages[i];
-			String name = pkg->name;
-			String fullpath = pkg->fullpath;
-
-			auto *files = query_value_array();
-			files->reserve(pkg->files.count);
-			for_array(j, pkg->files) {
-				AstFile *f = pkg->files[j];
-				files->add(f->fullpath);
-			}
-
-			auto *package = query_value_map();
-			package->reserve(3);
-			packages->add(package);
-
-			package->add("name", pkg->name);
-			package->add("fullpath", pkg->fullpath);
-			package->add("files", files);
-		}
-	}
-
-	if (c->info.definitions.count > 0) {
-		auto *definitions = query_value_array();
-		root->add("definitions", definitions);
-
-		auto sorted_definitions = array_make<Entity *>(query_value_allocator, 0, c->info.definitions.count);
-		defer (array_free(&sorted_definitions));
-
-		for_array(i, c->info.definitions) {
-			Entity *e = c->info.definitions[i];
-			String name = e->token.string;
-			if (is_blank_ident(name)) {
-				continue;
-			}
-			if ((e->scope->flags & (ScopeFlag_Pkg|ScopeFlag_File)) == 0) {
-				continue;
-			}
-			if (e->parent_proc_decl != nullptr) {
-				continue;
-			}
-			switch (e->kind) {
-			case Entity_Builtin:
-			case Entity_Nil:
-			case Entity_Label:
-				continue;
-			}
-			if (e->pkg == nullptr) {
-				continue;
-			}
-			if (e->token.pos.line == 0) {
-				continue;
-			}
-			if (e->kind == Entity_Procedure) {
-				Type *t = base_type(e->type);
-				if (t->kind != Type_Proc) {
-					continue;
-				}
-				if (t->Proc.is_poly_specialized) {
-					continue;
-				}
-			}
-			if (e->kind == Entity_TypeName) {
-				Type *t = base_type(e->type);
-				if (t->kind == Type_Struct) {
-					if (t->Struct.is_poly_specialized) {
-						continue;
-					}
-				}
-				if (t->kind == Type_Union) {
-					if (t->Union.is_poly_specialized) {
-						continue;
-					}
-				}
-			}
-
-			array_add(&sorted_definitions, e);
-		}
-
-		gb_sort_array(sorted_definitions.data, sorted_definitions.count, query_data_definition_compare);
-		definitions->reserve(sorted_definitions.count);
-
-		for_array(i, sorted_definitions) {
-			Entity *e = sorted_definitions[i];
-			String name = e->token.string;
-
-			auto *def = query_value_map();
-			def->reserve(16);
-			definitions->add(def);
-
-			def->add("package",     e->pkg->name);
-			def->add("name",        name);
-			def->add("filepath",    get_file_path_string(e->token.pos.file_id));
-			def->add("line",        cast(i64)e->token.pos.line);
-			def->add("column",      cast(i64)e->token.pos.column);
-			def->add("file_offset", cast(i64)e->token.pos.offset);
-
-			switch (e->kind) {
-			case Entity_Constant:    def->add("kind", str_lit("constant"));        break;
-			case Entity_Variable:    def->add("kind", str_lit("variable"));        break;
-			case Entity_TypeName:    def->add("kind", str_lit("type name"));       break;
-			case Entity_Procedure:   def->add("kind", str_lit("procedure"));       break;
-			case Entity_ProcGroup:   def->add("kind", str_lit("procedure group")); break;
-			case Entity_ImportName:  def->add("kind", str_lit("import name"));     break;
-			case Entity_LibraryName: def->add("kind", str_lit("library name"));    break;
-			default: GB_PANIC("Invalid entity kind to be added");
-			}
-
-
-			if (e->type != nullptr && e->type != t_invalid) {
-				Type *t = e->type;
-				Type *bt = t;
-
-				switch (e->kind) {
-				case Entity_TypeName:
-					if (!e->TypeName.is_type_alias) {
-						bt = base_type(t);
-					}
-					break;
-				}
-
-				{
-					gbString str = type_to_string(t);
-					String type_str = make_string(cast(u8 *)str, gb_string_length(str));
-					def->add("type", type_str);
-				}
-				if (t != bt) {
-					gbString str = type_to_string(bt);
-					String type_str = make_string(cast(u8 *)str, gb_string_length(str));
-					def->add("base_type", type_str);
-				}
-				{
-					String type_kind = {};
-					Type *bt = base_type(t);
-					switch (bt->kind) {
-					case Type_Pointer:      type_kind = str_lit("pointer");       break;
-					case Type_Array:        type_kind = str_lit("array");         break;
-					case Type_Slice:        type_kind = str_lit("slice");         break;
-					case Type_DynamicArray: type_kind = str_lit("dynamic array"); break;
-					case Type_Map:          type_kind = str_lit("map");           break;
-					case Type_Struct:       type_kind = str_lit("struct");        break;
-					case Type_Union:        type_kind = str_lit("union");         break;
-					case Type_Enum:         type_kind = str_lit("enum");          break;
-					case Type_Proc:         type_kind = str_lit("procedure");     break;
-					case Type_BitSet:       type_kind = str_lit("bit set");       break;
-					case Type_SimdVector:   type_kind = str_lit("simd vector");   break;
-
-					case Type_Generic:
-					case Type_Tuple:
-						GB_PANIC("Invalid definition type");
-						break;
-					}
-					if (type_kind.len > 0) {
-						def->add("type_kind", type_kind);
-					}
-				}
-			}
-
-			if (e->kind == Entity_TypeName) {
-				def->add("size",  type_size_of(e->type));
-				def->add("align", type_align_of(e->type));
-
-
-				if (is_type_struct(e->type)) {
-					auto *data = query_value_map();
-					data->reserve(6);
-
-					def->add("data", data);
-
-					Type *t = base_type(e->type);
-					GB_ASSERT(t->kind == Type_Struct);
-
-					if (t->Struct.is_polymorphic) {
-						data->add("polymorphic", cast(bool)t->Struct.is_polymorphic);
-					}
-					if (t->Struct.is_poly_specialized) {
-						data->add("polymorphic_specialized", cast(bool)t->Struct.is_poly_specialized);
-					}
-					if (t->Struct.is_packed) {
-						data->add("packed", cast(bool)t->Struct.is_packed);
-					}
-					if (t->Struct.is_raw_union) {
-						data->add("raw_union", cast(bool)t->Struct.is_raw_union);
-					}
-
-					auto *fields = query_value_array();
-					data->add("fields", fields);
-					fields->reserve(t->Struct.fields.count);
-					fields->packed = true;
-
-					for_array(j, t->Struct.fields) {
-						Entity *e = t->Struct.fields[j];
-						String name = e->token.string;
-						if (is_blank_ident(name)) {
-							continue;
-						}
-
-						fields->add(name);
-					}
-				} else if (is_type_union(e->type)) {
-					auto *data = query_value_map();
-					data->reserve(4);
-
-					def->add("data", data);
-					Type *t = base_type(e->type);
-					GB_ASSERT(t->kind == Type_Union);
-
-					if (t->Union.is_polymorphic) {
-						data->add("polymorphic", cast(bool)t->Union.is_polymorphic);
-					}
-					if (t->Union.is_poly_specialized) {
-						data->add("polymorphic_specialized", cast(bool)t->Union.is_poly_specialized);
-					}
-
-					auto *variants = query_value_array();
-					variants->reserve(t->Union.variants.count);
-					data->add("variants", variants);
-
-					for_array(j, t->Union.variants) {
-						Type *vt = t->Union.variants[j];
-
-						gbString str = type_to_string(vt);
-						String type_str = make_string(cast(u8 *)str, gb_string_length(str));
-						variants->add(type_str);
-					}
-				}
-			}
-
-			if (e->kind == Entity_Procedure) {
-				Type *t = base_type(e->type);
-				GB_ASSERT(t->kind == Type_Proc);
-
-				bool is_polymorphic = t->Proc.is_polymorphic;
-				bool is_poly_specialized = t->Proc.is_poly_specialized;
-				bool ok = is_polymorphic || is_poly_specialized;
-				if (ok) {
-					auto *data = query_value_map();
-					data->reserve(4);
-
-					def->add("data", data);
-					if (is_polymorphic) {
-						data->add("polymorphic", cast(bool)is_polymorphic);
-					}
-					if (is_poly_specialized) {
-						data->add("polymorphic_specialized", cast(bool)is_poly_specialized);
-					}
-				}
-			}
-
-			if (e->kind == Entity_ProcGroup) {
-				auto *procedures = query_value_array();
-				procedures->reserve(e->ProcGroup.entities.count);
-
-				for_array(j, e->ProcGroup.entities) {
-					Entity *p = e->ProcGroup.entities[j];
-
-					auto *procedure = query_value_map();
-					procedure->reserve(2);
-					procedure->packed = true;
-
-					procedures->add(procedure);
-
-					procedure->add("package", p->pkg->name);
-					procedure->add("name",    p->token.string);
-				}
-				def->add("procedures", procedures);
-			}
-
-			DeclInfo *di = e->decl_info;
-			if (di != nullptr) {
-				if (di->is_using) {
-					def->add("using", query_value_boolean(true));
-				}
-			}
-		}
-	}
-
-	if (build_context.show_timings) {
-		Timings *t = timings;
-		timings__stop_current_section(t);
-		t->total.finish = time_stamp_time_now();
-		isize max_len = gb_min(36, t->total.label.len);
-		for_array(i, t->sections) {
-			TimeStamp ts = t->sections[i];
-			max_len = gb_max(max_len, ts.label.len);
-		}
-		t->total_time_seconds = time_stamp_as_s(t->total, t->freq);
-
-		auto *tims = query_value_map();
-		tims->reserve(8);
-		root->add("timings", tims);
-		tims->add("time_unit", str_lit("s"));
-
-		tims->add(t->total.label, cast(f64)t->total_time_seconds);
-
-
-		Parser *p = c->parser;
-		if (p != nullptr) {
-			isize lines    = p->total_line_count;
-			isize tokens   = p->total_token_count;
-			isize files    = 0;
-			isize packages = p->packages.count;
-			isize total_file_size = 0;
-			for_array(i, p->packages) {
-				files += p->packages[i]->files.count;
-				for_array(j, p->packages[i]->files) {
-					AstFile *file = p->packages[i]->files[j];
-					total_file_size += file->tokenizer.end - file->tokenizer.start;
-				}
-			}
-
-			tims->add("total_lines",     cast(i64)lines);
-			tims->add("total_tokens",    cast(i64)tokens);
-			tims->add("total_files",     cast(i64)files);
-			tims->add("total_packages",  cast(i64)packages);
-			tims->add("total_file_size", cast(i64)total_file_size);
-
-			auto *sections = query_value_map();
-			sections->reserve(t->sections.count);
-			tims->add("sections", sections);
-			for_array(i, t->sections) {
-				TimeStamp ts = t->sections[i];
-				f64 section_time = time_stamp_as_s(ts, t->freq);
-
-				auto *section = query_value_map();
-				section->reserve(2);
-				sections->add(ts.label, section);
-				section->add("time", cast(f64)section_time);
-				section->add("total_fraction", cast(f64)(section_time/t->total_time_seconds));
-			}
-		}
-	}
-
-
-	print_query_data_as_json(root, !build_context.query_data_set_settings.compact);
-	gb_printf("\n");
-}
-
-
-
-template <typename T>
-struct BinaryArray {
-	u32 offset; // Offset in bytes from the top of the file
-	u32 length; // Number of elements in array of type T
-};
-
-template <typename T>
-Array<T> binary_array_from_data(BinaryArray<T> ba, void *data) {
-	Array<T> res = {};
-	res.data     = cast(T *)(cast(u8 *)data + ba.offset);
-	res.count    = ba.length;
-	res.capacity = ba.length;
-	return res;
-}
-
-typedef BinaryArray<u8> BinaryString;
-
-struct GoToDefIdent {
-	u64 use_offset;  // offset of identifier use in bytes from the start of the file that contains it
-	u32 len;         // length in bytes of the identifier
-	u32 def_file_id;
-	u64 def_offset;  // offset of entity definition in bytes from the start of the file that contains it
-};
-
-struct GoToDefFile {
-	u32 id;
-	BinaryString path;
-	BinaryArray<GoToDefIdent> idents;
-};
-
-struct GoToDefHeader {
-	u8  magic[4]; // ogtd (odin-go-to-definitions)
-	u32 version;  // 1
-	BinaryArray<GoToDefFile> files;
-};
-
-struct GoToDefFileMap {
-	AstFile *f;
-	u32 id;
-	Array<Ast *> idents;
-};
-
-
-int go_to_def_file_map_compare(void const *a, void const *b) {
-	GoToDefFileMap const *x = cast(GoToDefFileMap const *)a;
-	GoToDefFileMap const *y = cast(GoToDefFileMap const *)b;
-	if (x == y) {
-		return 0;
-	} else if (x != nullptr && y == nullptr) {
-		return -1;
-	} else if (x == nullptr && y != nullptr) {
-		return +1;
-	}
-	if (x->f->id < y->f->id) {
-		return -1;
-	} else if (x->f->id > y->f->id) {
-		return +1;
-	}
-	return 0;
-}
-
-int quick_ident_compare(void const *a, void const *b) {
-	Ast *x = *cast(Ast **)a;
-	Ast *y = *cast(Ast **)b;
-
-	// NOTE(bill): This assumes that the file is same
-	if (x->Ident.token.pos.offset < y->Ident.token.pos.offset) {
-		return -1;
-	} else if (x->Ident.token.pos.offset > y->Ident.token.pos.offset) {
-		return +1;
-	}
-	return 0;
-}
-
-
-void generate_and_print_query_data_go_to_definitions(Checker *c) {
-	GB_ASSERT(c->info.allow_identifier_uses);
-
-	gbAllocator a = query_value_allocator;
-
-	isize file_path_memory_needed = 0;
-	auto files = array_make<GoToDefFileMap>(a, 0, c->info.files.entries.count);
-	for_array(i, c->info.files.entries) {
-		AstFile *f = c->info.files.entries[i].value;
-		file_path_memory_needed += f->fullpath.len+1; // add NUL terminator
-
-
-		GoToDefFileMap x = {};
-		x.f = f;
-		array_init(&x.idents, a);
-		array_add(&files, x);
-	}
-	gb_sort_array(files.data, files.count, go_to_def_file_map_compare);
-
-	auto file_id_map_to_index = array_make<isize>(a, files[files.count-1].f->id + 1);
-	for_array(i, file_id_map_to_index) {
-		file_id_map_to_index[i] = -1;
-	}
-	for_array(i, files) {
-		file_id_map_to_index[files[i].f->id] = i;
-	}
-
-
-
-	for_array(i, c->info.identifier_uses) {
-		Ast *ast = c->info.identifier_uses[i];
-		GB_ASSERT(ast->kind == Ast_Ident);
-		TokenPos pos = ast->Ident.token.pos;
-		Entity *e = ast->Ident.entity;
-		if (e == nullptr) {
-			continue;
-		}
-
-
-		AstFile **use_file_found = string_map_get(&c->info.files, get_file_path_string(pos.file_id));
-		GB_ASSERT(use_file_found != nullptr);
-		AstFile *use_file = *use_file_found;
-		GB_ASSERT(use_file != nullptr);
-
-		if (e->scope == nullptr) {
-			GB_ASSERT(e->flags & EntityFlag_Field);
-			continue;
-		}
-		if (e->scope->flags & ScopeFlag_Global) {
-			continue;
-		}
-
-		isize idx = file_id_map_to_index[use_file->id];
-		if (idx >= 0) {
-			array_add(&files[idx].idents, ast);
-		} else {
-			// TODO(bill): Handle invalid map case?
-		}
-	}
-
-	for_array(i, files) {
-		GoToDefFileMap *f = &files[i];
-		gb_sort_array(f->idents.data, f->idents.count, quick_ident_compare);
-		// gb_printf_err("%lld %.*s -> %lld\n", f->f->id, LIT(f->f->fullpath), f->idents.count);
-	}
-
-
-
-	isize data_min_size = 0;
-
-	u32 header_offset = cast(u32)data_min_size; gb_unused(header_offset);
-	data_min_size += gb_size_of(GoToDefHeader);
-	data_min_size = align_formula_isize(data_min_size, 8);
-
-	u32 file_offset = cast(u32)data_min_size;
-	data_min_size += gb_size_of(GoToDefFile) * files.count;
-	data_min_size = align_formula_isize(data_min_size, 8);
-
-	u32 file_path_offset = cast(u32)data_min_size;
-	data_min_size += file_path_memory_needed;
-	data_min_size = align_formula_isize(data_min_size, 8);
-
-	u32 idents_offset = cast(u32)data_min_size;
-	data_min_size += gb_size_of(GoToDefIdent) * c->info.identifier_uses.count;
-
-
-	auto data = array_make<u8>(a, 0, data_min_size);
-	defer (array_free(&data));
-
-	GoToDefHeader header = {};
-	gb_memmove(header.magic, "ogtd", 4);
-	header.version = 1;
-	header.files.length = cast(u32)files.count;
-	header.files.offset = file_offset;
-
-	array_add_elems(&data, cast(u8 *)&header, gb_size_of(header));
-
-	array_resize(&data, data_min_size);
-
-	auto binary_files = binary_array_from_data(header.files, data.data);
-
-	u32 file_path_offset_index = file_path_offset;
-	u32 idents_offset_index = idents_offset;
-	for_array(i, files) {
-		GoToDefFileMap *f_map = &files[i];
-		AstFile *f = f_map->f;
-		binary_files[i].id = cast(u32)f->id;
-
-		binary_files[i].path.offset = file_path_offset_index;
-		binary_files[i].path.length = cast(u32)f->fullpath.len;
-
-		binary_files[i].idents.offset = idents_offset_index;
-		binary_files[i].idents.length = cast(u32)f_map->idents.count;
-
-		auto path = binary_array_from_data(binary_files[i].path, data.data);
-		gb_memmove(path.data, f->fullpath.text, f->fullpath.len);
-		path.data[f->fullpath.len] = 0;
-
-
-		auto idents = binary_array_from_data(binary_files[i].idents, data.data);
-		for_array(j, f_map->idents) {
-			Ast *ast = f_map->idents[j];
-			GB_ASSERT(ast->kind == Ast_Ident);
-
-			Entity *e = ast->Ident.entity;
-			TokenPos def = e->token.pos;
-			AstFile *def_file = e->file;
-
-			if (def_file == nullptr) {
-				auto *def_file_found = string_map_get(&c->info.files, get_file_path_string(e->token.pos.file_id));
-				if (def_file_found == nullptr) {
-					continue;
-				}
-				def_file = *def_file_found;
-			}
-
-			isize file_index = file_id_map_to_index[def_file->id];
-			GB_ASSERT(file_index >= 0);
-
-			idents[j].use_offset  = cast(u64)ast->Ident.token.pos.offset;
-			idents[j].len         = cast(u32)ast->Ident.token.string.len;
-			idents[j].def_file_id = cast(u32)def_file->id;
-			idents[j].def_offset  = cast(u64)e->token.pos.offset;
-
-			// gb_printf_err("%llu %llu %llu %llu\n", idents[j].len, idents[j].use_offset, idents[j].def_file_id, idents[j].def_offset);
-		}
-
-		file_path_offset_index += cast(u32)(f->fullpath.len + 1);
-		idents_offset_index += cast(u32)(f_map->idents.count * gb_size_of(GoToDefIdent));
-	}
-
-
-	gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.data, data.count*gb_size_of(*data.data));
-}
-

+ 99 - 14
src/queue.cpp

@@ -1,3 +1,86 @@
+template <typename T>
+struct MPSCNode {
+	std::atomic<MPSCNode<T> *> next;
+	T value;
+};
+
+//
+// Multiple Producer Single Consumer Lockless Queue
+// URL: https://www.1024cores.net
+//
+template <typename T>
+struct MPSCQueue {
+	MPSCNode<T> sentinel;
+	std::atomic<MPSCNode<T> *> head;
+	std::atomic<MPSCNode<T> *> tail;
+	std::atomic<isize> count;
+};
+
+template <typename T> gb_internal void  mpsc_init   (MPSCQueue<T> *q);
+template <typename T> gb_internal void  mpsc_destroy(MPSCQueue<T> *q);
+template <typename T> gb_internal isize mpsc_enqueue(MPSCQueue<T> *q, T const &value);
+template <typename T> gb_internal bool  mpsc_dequeue(MPSCQueue<T> *q, T *value_);
+
+template <typename T>
+gb_internal void mpsc_init(MPSCQueue<T> *q, gbAllocator const &allocator) {
+	q->sentinel.next.store(nullptr, std::memory_order_relaxed);
+	q->head.store(&q->sentinel, std::memory_order_relaxed);
+	q->tail.store(&q->sentinel, std::memory_order_relaxed);
+	q->count.store(0, std::memory_order_relaxed);
+}
+
+template <typename T>
+gb_internal void mpsc_destroy(MPSCQueue<T> *q) {
+	GB_ASSERT(q->count.load() == 0);
+}
+
+template <typename T>
+gb_internal MPSCNode<T> *mpsc_alloc_node(MPSCQueue<T> *q, T const &value) {
+	auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
+	new_node->value = value;
+	return new_node;
+}
+
+template <typename T>
+gb_internal void mpsc_free_node(MPSCQueue<T> *q, MPSCNode<T> *node) {
+	// TODO(bill): determine a good way to handle the freed nodes rather than letting them leak
+}
+
+template <typename T>
+gb_internal isize mpsc_enqueue(MPSCQueue<T> *q, MPSCNode<T> *node) {
+	node->next.store(nullptr, std::memory_order_relaxed);
+	auto prev = q->head.exchange(node, std::memory_order_acq_rel);
+	prev->next.store(node, std::memory_order_release);
+	isize count = 1 + q->count.fetch_add(1, std::memory_order_relaxed);
+	return count;
+}
+
+template <typename T>
+gb_internal isize mpsc_enqueue(MPSCQueue<T> *q, T const &value) {
+	auto node = mpsc_alloc_node(q, value);
+	return mpsc_enqueue(q, node);
+}
+
+
+template <typename T>
+gb_internal bool mpsc_dequeue(MPSCQueue<T> *q, T *value_) {
+	auto tail = q->tail.load(std::memory_order_relaxed);
+	auto next = tail->next.load(std::memory_order_relaxed);
+	if (next) {
+		q->tail.store(next, std::memory_order_relaxed);
+		if (value_) *value_ = next->value;
+		q->count.fetch_sub(1, std::memory_order_relaxed);
+		mpsc_free_node(q, tail);
+		return true;
+	}
+	GB_ASSERT(q->count.load(std::memory_order_acquire) == 0);
+	return false;
+}
+
+////////////////////////////
+
+
+
 #define MPMC_CACHE_LINE_SIZE 64
 
 typedef std::atomic<i32> MPMCQueueAtomicIdx;
@@ -9,7 +92,6 @@ struct MPMCQueue {
 
 	T *                 nodes;
 	MPMCQueueAtomicIdx *indices;
-	gbAllocator         allocator;
 	BlockingMutex       mutex;
 	MPMCQueueAtomicIdx  count;
 	i32                 mask; // capacity-1, because capacity must be a power of 2
@@ -22,8 +104,11 @@ struct MPMCQueue {
 };
 
 
+gb_internal gbAllocator mpmc_allocator(void) {
+	return heap_allocator();
+}
 
-void mpmc_internal_init_indices(MPMCQueueAtomicIdx *indices, i32 offset, i32 size) {
+gb_internal void mpmc_internal_init_indices(MPMCQueueAtomicIdx *indices, i32 offset, i32 size) {
 	GB_ASSERT(offset % 8 == 0);
 	GB_ASSERT(size % 8 == 0);
 
@@ -43,7 +128,7 @@ void mpmc_internal_init_indices(MPMCQueueAtomicIdx *indices, i32 offset, i32 siz
 
 
 template <typename T>
-void mpmc_init(MPMCQueue<T> *q, gbAllocator a, isize size_i) {
+gb_internal void mpmc_init(MPMCQueue<T> *q, isize size_i) {
 	if (size_i < 8) {
 		size_i = 8;
 	}
@@ -52,9 +137,8 @@ void mpmc_init(MPMCQueue<T> *q, gbAllocator a, isize size_i) {
 	size = next_pow2(size);
 	GB_ASSERT(gb_is_power_of_two(size));
 
-	mutex_init(&q->mutex);
 	q->mask = size-1;
-	q->allocator = a;
+	gbAllocator a = mpmc_allocator();
 	q->nodes   = gb_alloc_array(a, T, size);
 	q->indices = gb_alloc_array(a, MPMCQueueAtomicIdx, size);
 
@@ -64,25 +148,26 @@ void mpmc_init(MPMCQueue<T> *q, gbAllocator a, isize size_i) {
 
 
 template <typename T>
-void mpmc_destroy(MPMCQueue<T> *q) {
-	mutex_destroy(&q->mutex);
-	gb_free(q->allocator, q->nodes);
-	gb_free(q->allocator, q->indices);
+gb_internal void mpmc_destroy(MPMCQueue<T> *q) {
+	gbAllocator a = mpmc_allocator();
+	gb_free(a, q->nodes);
+	gb_free(a, q->indices);
 }
 
 
 template <typename T>
-bool mpmc_internal_grow(MPMCQueue<T> *q) {
+gb_internal bool mpmc_internal_grow(MPMCQueue<T> *q) {
+	gbAllocator a = mpmc_allocator();
 	mutex_lock(&q->mutex);
 	i32 old_size = q->mask+1;
 	i32 new_size = old_size*2;
-	resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
+	resize_array_raw(&q->nodes, a, old_size, new_size);
 	if (q->nodes == nullptr) {
 		GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
 		mutex_unlock(&q->mutex);
 		return false;
 	}
-	resize_array_raw(&q->indices, q->allocator, old_size, new_size);
+	resize_array_raw(&q->indices, a, old_size, new_size);
 	if (q->indices == nullptr) {
 		GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
 		mutex_unlock(&q->mutex);
@@ -95,7 +180,7 @@ bool mpmc_internal_grow(MPMCQueue<T> *q) {
 }
 
 template <typename T>
-i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
+gb_internal i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 	GB_ASSERT(q->mask != 0);
 
 	i32 head_idx = q->head_idx.load(std::memory_order_relaxed);
@@ -125,7 +210,7 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 }
 
 template <typename T>
-bool mpmc_dequeue(MPMCQueue<T> *q, T *data_) {
+gb_internal bool mpmc_dequeue(MPMCQueue<T> *q, T *data_) {
 	if (q->mask == 0) {
 		return false;
 	}

+ 13 - 13
src/range_cache.cpp

@@ -10,17 +10,17 @@ struct RangeCache {
 };
 
 
-RangeCache range_cache_make(gbAllocator a) {
+gb_internal RangeCache range_cache_make(gbAllocator a) {
 	RangeCache cache = {};
 	array_init(&cache.ranges, a);
 	return cache;
 }
 
-void range_cache_destroy(RangeCache *c) {
+gb_internal void range_cache_destroy(RangeCache *c) {
 	array_free(&c->ranges);
 }
 
-bool range_cache_add_index(RangeCache *c, i64 index) {
+gb_internal bool range_cache_add_index(RangeCache *c, i64 index) {
 	for_array(i, c->ranges) {
 		RangeValue v = c->ranges[i];
 		if (v.lo <= index && index <= v.hi) {
@@ -33,7 +33,7 @@ bool range_cache_add_index(RangeCache *c, i64 index) {
 }
 
 
-bool range_cache_add_range(RangeCache *c, i64 lo, i64 hi) {
+gb_internal bool range_cache_add_range(RangeCache *c, i64 lo, i64 hi) {
 	GB_ASSERT(lo <= hi);
 	for_array(i, c->ranges) {
 		RangeValue v = c->ranges[i];
@@ -59,12 +59,12 @@ bool range_cache_add_range(RangeCache *c, i64 lo, i64 hi) {
 }
 
 
-bool range_cache_index_exists(RangeCache *c, i64 index) {
-	for_array(i, c->ranges) {
-		RangeValue v = c->ranges[i];
-		if (v.lo <= index && index <= v.hi) {
-			return true;
-		}
-	}
-	return false;
-}
+// gb_internal bool range_cache_index_exists(RangeCache *c, i64 index) {
+// 	for_array(i, c->ranges) {
+// 		RangeValue v = c->ranges[i];
+// 		if (v.lo <= index && index <= v.hi) {
+// 			return true;
+// 		}
+// 	}
+// 	return false;
+// }

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä