소스 검색

Merge branch 'master' into odin-ast-changes

gingerBill 3 년 전
부모
커밋
fcab5508be
100개의 변경된 파일5215개의 추가작업 그리고 918개의 파일을 삭제
  1. 1 0
      .github/FUNDING.yml
  2. 4 1
      .github/workflows/ci.yml
  3. 1 1
      .github/workflows/nightly.yml
  4. 14 10
      Makefile
  5. 11 35
      core/bytes/bytes.odin
  6. 4 4
      core/c/c.odin
  7. 2 2
      core/c/libc/complex.odin
  8. 2 2
      core/c/libc/ctype.odin
  9. 18 5
      core/c/libc/errno.odin
  10. 2 2
      core/c/libc/math.odin
  11. 3 3
      core/c/libc/setjmp.odin
  12. 5 5
      core/c/libc/signal.odin
  13. 30 5
      core/c/libc/stdio.odin
  14. 5 5
      core/c/libc/stdlib.odin
  15. 2 2
      core/c/libc/string.odin
  16. 3 3
      core/c/libc/threads.odin
  17. 10 5
      core/c/libc/time.odin
  18. 2 2
      core/c/libc/uchar.odin
  19. 2 2
      core/c/libc/wchar.odin
  20. 10 5
      core/c/libc/wctype.odin
  21. 1 1
      core/crypto/_fiat/field_poly1305/field.odin
  22. 1 1
      core/crypto/chacha20/chacha20.odin
  23. 1 1
      core/crypto/rand_generic.odin
  24. 12 0
      core/crypto/rand_openbsd.odin
  25. 23 0
      core/crypto/rand_windows.odin
  26. 336 0
      core/crypto/siphash/siphash.odin
  27. 1 1
      core/dynlib/lib_unix.odin
  28. 189 179
      core/fmt/fmt.odin
  29. 1 1
      core/image/png/example.odin
  30. 1 1
      core/image/png/helpers.odin
  31. 734 0
      core/math/noise/internal.odin
  32. 171 0
      core/math/noise/opensimplex2.odin
  33. 34 0
      core/mem/doc.odin
  34. 35 19
      core/mem/virtual/virtual.odin
  35. 5 5
      core/mem/virtual/virtual_linux.odin
  36. 21 5
      core/mem/virtual/virtual_platform.odin
  37. 5 5
      core/mem/virtual/virtual_windows.odin
  38. 14 2
      core/odin/parser/parser.odin
  39. 71 0
      core/os/dir_openbsd.odin
  40. 18 7
      core/os/file_windows.odin
  41. 1 1
      core/os/os.odin
  42. 8 8
      core/os/os2/user.odin
  43. 13 4
      core/os/os_darwin.odin
  44. 280 287
      core/os/os_freebsd.odin
  45. 63 39
      core/os/os_linux.odin
  46. 706 0
      core/os/os_openbsd.odin
  47. 1 1
      core/os/stat_unix.odin
  48. 3 3
      core/os/stream.odin
  49. 6 7
      core/path/filepath/match.odin
  50. 5 4
      core/path/filepath/path.odin
  51. 8 3
      core/path/filepath/path_unix.odin
  52. 4 4
      core/reflect/types.odin
  53. 35 2
      core/runtime/core.odin
  54. 1 1
      core/runtime/default_allocators_nil.odin
  55. 1 1
      core/runtime/default_temporary_allocator.odin
  56. 1 1
      core/runtime/entry_unix.odin
  57. 1 1
      core/runtime/entry_windows.odin
  58. 9 9
      core/runtime/error_checks.odin
  59. 1 1
      core/runtime/internal.odin
  60. 2 2
      core/runtime/procs.odin
  61. 21 0
      core/runtime/procs_darwin.odin
  62. 1 0
      core/strconv/strconv.odin
  63. 31 47
      core/strings/strings.odin
  64. 1 1
      core/sync/channel_unix.odin
  65. 78 0
      core/sync/sync2/futex_openbsd.odin
  66. 1 1
      core/sync/sync2/primitives.odin
  67. 1 1
      core/sync/sync2/primitives_internal.odin
  68. 9 0
      core/sync/sync2/primitives_openbsd.odin
  69. 1 1
      core/sync/sync2/primitives_pthreads.odin
  70. 36 0
      core/sync/sync_openbsd.odin
  71. 1 1
      core/sync/sync_unix.odin
  72. 168 0
      core/sys/darwin/xnu_system_call_helpers.odin
  73. 558 0
      core/sys/darwin/xnu_system_call_numbers.odin
  74. 419 0
      core/sys/darwin/xnu_system_call_wrappers.odin
  75. 65 0
      core/sys/unix/pthread_openbsd.odin
  76. 1 1
      core/sys/unix/pthread_unix.odin
  77. 4 4
      core/sys/unix/syscalls_linux.odin
  78. 3 0
      core/sys/win32/user32.odin
  79. 3 0
      core/sys/windows/types.odin
  80. 1 1
      core/thread/thread_unix.odin
  81. 24 12
      core/time/time_unix.odin
  82. 1 0
      examples/all/all_main.odin
  83. 7 14
      examples/all/all_vendor.odin
  84. 10 0
      examples/all/all_vendor_directx.odin
  85. 15 0
      examples/all/all_vendor_stl.odin
  86. 22 16
      examples/demo/demo.odin
  87. 3 1
      src/array.cpp
  88. 24 3
      src/bug_report.cpp
  89. 48 3
      src/build_settings.cpp
  90. 296 7
      src/check_builtin.cpp
  91. 68 0
      src/check_decl.cpp
  92. 73 44
      src/check_expr.cpp
  93. 41 0
      src/check_type.cpp
  94. 136 11
      src/checker.cpp
  95. 18 1
      src/checker.hpp
  96. 23 4
      src/checker_builtin_procs.hpp
  97. 2 2
      src/common.cpp
  98. 17 2
      src/common_memory.cpp
  99. 1 34
      src/docs_writer.cpp
  100. 29 0
      src/entity.cpp

+ 1 - 0
.github/FUNDING.yml

@@ -1,3 +1,4 @@
 # These are supported funding model platforms
 
+github: odin-lang
 patreon: gingerbill

+ 4 - 1
.github/workflows/ci.yml

@@ -38,6 +38,9 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
+      - name: Odin check examples/all for OpenBSD amd64
+        run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
+        timeout-minutes: 10
   build_macOS:
     runs-on: macos-latest
     steps:
@@ -79,7 +82,7 @@ jobs:
           make
         timeout-minutes: 10
   build_windows:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
       - uses: actions/checkout@v1
       - name: build Odin

+ 1 - 1
.github/workflows/nightly.yml

@@ -7,7 +7,7 @@ on:
 
 jobs:
   build_windows:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
       - uses: actions/checkout@v1
       - name: build Odin

+ 14 - 10
Makefile

@@ -1,6 +1,6 @@
 GIT_SHA=$(shell git rev-parse --short HEAD)
 DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
-LDFLAGS=-pthread -ldl -lm -lstdc++
+LDFLAGS=-pthread -lm -lstdc++
 CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
 CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
 CC=clang
@@ -8,9 +8,9 @@ CC=clang
 OS=$(shell uname)
 
 ifeq ($(OS), Darwin)
-    
+
     ARCH=$(shell uname -m)
-    LLVM_CONFIG=
+    LLVM_CONFIG=llvm-config
 
     # allow for arm only llvm's with version 13
     ifeq ($(ARCH), arm64)
@@ -27,9 +27,7 @@ ifeq ($(OS), Darwin)
     LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
     LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
 
-    ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),)
-        LLVM_CONFIG=llvm-config
-    else
+    ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
         ifeq ($(ARCH), arm64)
             $(error "Requirement: llvm-config must be base version 13 for arm64")
         else 
@@ -37,7 +35,7 @@ ifeq ($(OS), Darwin)
         endif 
     endif 
 
-    LDFLAGS:=$(LDFLAGS) -liconv
+    LDFLAGS:=$(LDFLAGS) -liconv -ldl
     CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
     LDFLAGS:=$(LDFLAGS) -lLLVM-C
 endif
@@ -48,13 +46,19 @@ ifeq ($(OS), Linux)
     else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
         LLVM_CONFIG=llvm-config-11-64
     else
-        ifneq ($(shell llvm-config --version | grep '^11\.'),)
-            LLVM_CONFIG=llvm-config
-        else
+        ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
             $(error "Requirement: llvm-config must be version 11")
         endif
     endif
 
+    LDFLAGS:=$(LDFLAGS) -ldl
+    CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
+    LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
+endif
+ifeq ($(OS), OpenBSD)
+    LLVM_CONFIG=/usr/local/bin/llvm-config
+
+    LDFLAGS:=$(LDFLAGS) -liconv
     CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
     LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
 endif

+ 11 - 35
core/bytes/bytes.odin

@@ -218,61 +218,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) ->
 
 
 @private
-_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) {
-	s, n := s, n
-
-	if n == 0 {
-		return
-	}
-
-	if sep == nil {
+_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) {
+	if len(sep) == 0 {
 		res = s[:]
 		ok = true
 		s^ = s[len(s):]
 		return
 	}
 
-	if n < 0 {
-		n = count(s^, sep) + 1
-	}
-
-	n -= 1
-
-	i := 0
-	for ; i < n; i += 1 {
-		m := index(s^, sep)
-		if m < 0 {
-			break
-		}
+	m := index(s^, sep)
+	if m < 0 {
+		// not found
+		res = s[:]
+		ok = len(res) != 0
+		s^ = s[len(s):]
+	} else {
 		res = s[:m+sep_save]
 		ok = true
 		s^ = s[m+len(sep):]
-		return
 	}
-	res = s[:]
-	ok = res != nil
-	s^ = s[len(s):]
 	return
 }
 
 
 split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
-	return _split_iterator(s, sep, 0, -1)
-}
-
-split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
-	return _split_iterator(s, sep, 0, n)
+	return _split_iterator(s, sep, 0)
 }
 
 split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
-	return _split_iterator(s, sep, len(sep), -1)
+	return _split_iterator(s, sep, len(sep))
 }
 
-split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
-	return _split_iterator(s, sep, len(sep), n)
-}
-
-
 
 index_byte :: proc(s: []byte, c: byte) -> int {
 	for i := 0; i < len(s); i += 1 {

+ 4 - 4
core/c/c.odin

@@ -7,20 +7,20 @@ char           :: builtin.u8  // assuming -funsigned-char
 schar          :: builtin.i8
 short          :: builtin.i16
 int            :: builtin.i32
-long           :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64
+long           :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64
 longlong       :: builtin.i64
 
 uchar          :: builtin.u8
 ushort         :: builtin.u16
 uint           :: builtin.u32
-ulong          :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64
+ulong          :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64
 ulonglong      :: builtin.u64
 
 bool           :: builtin.bool
 
 size_t         :: builtin.uint
 ssize_t        :: builtin.int
-wchar_t        :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32
+wchar_t        :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32
 
 float          :: builtin.f32
 double         :: builtin.f64
@@ -48,7 +48,7 @@ int_least64_t  :: builtin.i64
 uint_least64_t :: builtin.u64
 
 // Same on Windows, Linux, and FreeBSD
-when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
 	int_fast8_t    :: builtin.i8
 	uint_fast8_t   :: builtin.u8
 	int_fast16_t   :: builtin.i32

+ 2 - 2
core/c/libc/complex.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.3 Complex arithmetic
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

+ 2 - 2
core/c/libc/ctype.odin

@@ -1,8 +1,8 @@
 package libc
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

+ 18 - 5
core/c/libc/errno.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.5 Errors
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
@@ -14,7 +14,7 @@ when ODIN_OS == "windows" {
 //	EDOM,
 //	EILSEQ
 //	ERANGE
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
 	@(private="file")
 	@(default_calling_convention="c")
 	foreign libc {
@@ -27,7 +27,20 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
 	ERANGE :: 34
 }
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .OpenBSD {
+	@(private="file")
+	@(default_calling_convention="c")
+	foreign libc {
+		@(link_name="__errno")
+		_get_errno :: proc() -> ^int ---
+	}
+
+	EDOM   :: 33
+	EILSEQ :: 84
+	ERANGE :: 34
+}
+
+when ODIN_OS == .Windows {
 	@(private="file")
 	@(default_calling_convention="c")
 	foreign libc {
@@ -40,7 +53,7 @@ when ODIN_OS == "windows" {
 	ERANGE :: 34
 }
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	@(private="file")
 	@(default_calling_convention="c")
 	foreign libc {

+ 2 - 2
core/c/libc/math.odin

@@ -4,9 +4,9 @@ package libc
 
 import "core:intrinsics"
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

+ 3 - 3
core/c/libc/setjmp.odin

@@ -2,14 +2,14 @@ package libc
 
 // 7.13 Nonlocal jumps
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	@(default_calling_convention="c")
 	foreign libc {
 		// 7.13.1 Save calling environment

+ 5 - 5
core/c/libc/signal.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.14 Signal handling
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
@@ -21,7 +21,7 @@ foreign libc {
 	raise  :: proc(sig: int) -> int ---
 }
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	SIG_ERR :: rawptr(~uintptr(0)) 
 	SIG_DFL :: rawptr(uintptr(0))
 	SIG_IGN :: rawptr(uintptr(1))
@@ -34,7 +34,7 @@ when ODIN_OS == "windows" {
 	SIGTERM :: 15
 }
 
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
 	SIG_ERR  :: rawptr(~uintptr(0))
 	SIG_DFL  :: rawptr(uintptr(0))
 	SIG_IGN  :: rawptr(uintptr(1)) 
@@ -47,7 +47,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
 	SIGTERM  :: 15
 }
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	SIG_ERR  :: rawptr(~uintptr(0))
 	SIG_DFL  :: rawptr(uintptr(0))
 	SIG_IGN  :: rawptr(uintptr(1)) 

+ 30 - 5
core/c/libc/stdio.odin

@@ -1,8 +1,8 @@
 package libc
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
@@ -13,7 +13,7 @@ when ODIN_OS == "windows" {
 FILE :: struct {}
 
 // MSVCRT compatible.
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	_IOFBF       :: 0x0000
 	_IONBF       :: 0x0004
 	_IOLBF       :: 0x0040
@@ -48,7 +48,7 @@ when ODIN_OS == "windows" {
 }
 
 // GLIBC and MUSL compatible.
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
 	fpos_t        :: struct #raw_union { _: [16]char, _: longlong, _: double, }
 
 	_IOFBF        :: 0
@@ -78,7 +78,32 @@ when ODIN_OS == "linux" {
 	}
 }
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .OpenBSD {
+	fpos_t :: i64
+
+	_IOFBF :: 0
+	_IOLBF :: 1
+	_IONBF :: 1
+
+	BUFSIZ :: 1024
+
+	EOF :: int(-1)
+
+	FOPEN_MAX	:: 20
+	FILENAME_MAX	:: 1024
+
+	SEEK_SET :: 0
+	SEEK_CUR :: 1
+	SEEK_END :: 2
+
+	foreign libc {
+		stderr: ^FILE
+		stdin:  ^FILE
+		stdout: ^FILE
+	}
+}
+
+when ODIN_OS == .Darwin {
 	fpos_t :: distinct i64
 	
 	_IOFBF        :: 0

+ 5 - 5
core/c/libc/stdlib.odin

@@ -2,15 +2,15 @@ package libc
 
 // 7.22 General utilities
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	RAND_MAX :: 0x7fff
 
 	@(private="file")
@@ -24,7 +24,7 @@ when ODIN_OS == "windows" {
 	}
 }
 
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
 	RAND_MAX :: 0x7fffffff
 
 	// GLIBC and MUSL only
@@ -40,7 +40,7 @@ when ODIN_OS == "linux" {
 }
 
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	RAND_MAX :: 0x7fffffff
 
 	// GLIBC and MUSL only

+ 2 - 2
core/c/libc/string.odin

@@ -4,9 +4,9 @@ import "core:runtime"
 
 // 7.24 String handling
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

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

@@ -5,7 +5,7 @@ package libc
 thrd_start_t :: proc "c" (rawptr) -> int
 tss_dtor_t   :: proc "c" (rawptr)
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc {
 		"system:libucrt.lib", 
 		"system:msvcprt.lib"
@@ -74,7 +74,7 @@ when ODIN_OS == "windows" {
 }
 
 // GLIBC and MUSL compatible constants and types.
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
 	foreign import libc {
 		"system:c",
 		"system:pthread"
@@ -138,6 +138,6 @@ when ODIN_OS == "linux" {
 }
 
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	// TODO: find out what this is meant to be!
 }

+ 10 - 5
core/c/libc/time.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.27 Date and time
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
@@ -12,7 +12,7 @@ when ODIN_OS == "windows" {
 
 // We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
 // we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign libc {
 		// 7.27.2 Time manipulation functions
 		                               clock        :: proc() -> clock_t ---
@@ -45,7 +45,7 @@ when ODIN_OS == "windows" {
 	}
 }
 
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
 	@(default_calling_convention="c")
 	foreign libc {
 		// 7.27.2 Time manipulation functions
@@ -63,7 +63,12 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
 		strftime     :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
 	}
 
-	CLOCKS_PER_SEC :: 1000000
+	when ODIN_OS == .OpenBSD {
+		CLOCKS_PER_SEC :: 100
+	} else {
+		CLOCKS_PER_SEC :: 1000000
+	}
+
 	TIME_UTC       :: 1
 
 	time_t         :: distinct i64

+ 2 - 2
core/c/libc/uchar.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.28 Unicode utilities
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

+ 2 - 2
core/c/libc/wchar.odin

@@ -2,9 +2,9 @@ package libc
 
 // 7.29 Extended multibyte and wide character utilities
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"

+ 10 - 5
core/c/libc/wctype.odin

@@ -2,29 +2,34 @@ package libc
 
 // 7.30 Wide character classification and mapping utilities
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
 	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	wctrans_t :: distinct wchar_t
 	wctype_t  :: distinct ushort
 }
 
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
 	wctrans_t :: distinct intptr_t
 	wctype_t  :: distinct ulong
 }
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	wctrans_t :: distinct int
 	wctype_t  :: distinct u32
 }
 
+when ODIN_OS == .OpenBSD {
+	wctrans_t :: distinct rawptr
+	wctype_t  :: distinct rawptr
+}
+
 @(default_calling_convention="c")
 foreign libc {
 	// 7.30.2.1 Wide character classification functions

+ 1 - 1
core/crypto/_fiat/field_poly1305/field.odin

@@ -22,7 +22,7 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
 
 	assert(len(arg1) == 16)
 
-	when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
+	when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
 		// While it may be unwise to do deserialization here on our
 		// own when fiat-crypto provides equivalent functionality,
 		// doing it this way provides a little under 3x performance

+ 1 - 1
core/crypto/chacha20/chacha20.odin

@@ -346,7 +346,7 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
 		// Until dedicated assembly can be written leverage the fact that
 		// the callers of this routine ensure that src/dst are valid.
 
-		when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
+		when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
 			// util.PUT_U32_LE/util.U32_LE are not required on little-endian
 			// systems that also happen to not be strict about aligned
 			// memory access.

+ 1 - 1
core/crypto/rand_generic.odin

@@ -1,6 +1,6 @@
 package crypto
 
-when ODIN_OS != "linux" {
+when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
 	_rand_bytes :: proc (dst: []byte) {
 		unimplemented("crypto: rand_bytes not supported on this OS")
 	}

+ 12 - 0
core/crypto/rand_openbsd.odin

@@ -0,0 +1,12 @@
+package crypto
+
+import "core:c"
+
+foreign import libc "system:c"
+foreign libc {
+	arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
+}
+
+_rand_bytes :: proc (dst: []byte) {
+	arc4random_buf(raw_data(dst), len(dst))
+}

+ 23 - 0
core/crypto/rand_windows.odin

@@ -0,0 +1,23 @@
+package crypto
+
+import win32 "core:sys/windows"
+import "core:os"
+import "core:fmt"
+
+_rand_bytes :: proc(dst: []byte) {
+	ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
+	if ret != os.ERROR_NONE {
+		switch ret {
+			case os.ERROR_INVALID_HANDLE:
+				// The handle to the first parameter is invalid.
+				// This should not happen here, since we explicitly pass nil to it
+				panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
+			case os.ERROR_INVALID_PARAMETER:
+				// One of the parameters was invalid
+				panic("crypto: BCryptGenRandom Invalid parameter")
+			case:
+				// Unknown error
+				panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
+		}
+	}
+}

+ 336 - 0
core/crypto/siphash/siphash.odin

@@ -0,0 +1,336 @@
+package siphash
+
+/*
+    Copyright 2022 zhibog
+    Made available under the BSD-3 license.
+
+    List of contributors:
+        zhibog:  Initial implementation.
+
+    Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
+
+    Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
+*/
+
+import "core:crypto"
+import "core:crypto/util"
+import "core:mem"
+
+/*
+    High level API
+*/
+
+KEY_SIZE    :: 16
+DIGEST_SIZE :: 8
+
+// sum_string_1_3 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_1_3 :: proc(msg, key: string) -> u64 {
+    return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_1_3 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
+    ctx: Context
+    hash: u64
+    init(&ctx, key, 1, 3)
+    update(&ctx, msg)
+    final(&ctx, &hash)
+    return hash
+}
+
+// sum_string_to_buffer_1_3 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
+    sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_1_3 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
+    assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+    hash  := sum_bytes_1_3(msg, key)
+    _collect_output(dst[:], hash)
+}
+
+sum_1_3 :: proc {
+    sum_string_1_3,
+    sum_bytes_1_3,
+    sum_string_to_buffer_1_3,
+    sum_bytes_to_buffer_1_3,
+}
+
+// verify_u64_1_3 will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
+    return sum_bytes_1_3(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
+    derived_tag: [8]byte
+    sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
+    return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_1_3 :: proc {
+    verify_bytes_1_3,
+    verify_u64_1_3,
+}
+
+// sum_string_2_4 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_2_4 :: proc(msg, key: string) -> u64 {
+    return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_2_4 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
+    ctx: Context
+    hash: u64
+    init(&ctx, key, 2, 4)
+    update(&ctx, msg)
+    final(&ctx, &hash)
+    return hash
+}
+
+// sum_string_to_buffer_2_4 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
+    sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_2_4 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
+    assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+    hash  := sum_bytes_2_4(msg, key)
+    _collect_output(dst[:], hash)
+}
+
+sum_2_4 :: proc {
+    sum_string_2_4,
+    sum_bytes_2_4,
+    sum_string_to_buffer_2_4,
+    sum_bytes_to_buffer_2_4,
+}
+
+sum_string           :: sum_string_2_4
+sum_bytes            :: sum_bytes_2_4
+sum_string_to_buffer :: sum_string_to_buffer_2_4
+sum_bytes_to_buffer  :: sum_bytes_to_buffer_2_4
+sum :: proc {
+    sum_string,
+    sum_bytes,
+    sum_string_to_buffer,
+    sum_bytes_to_buffer,
+}
+
+// verify_u64_2_4 will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
+    return sum_bytes_2_4(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
+    derived_tag: [8]byte
+    sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
+    return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_2_4 :: proc {
+    verify_bytes_2_4,
+    verify_u64_2_4,
+}
+
+verify_bytes :: verify_bytes_2_4
+verify_u64   :: verify_u64_2_4
+verify :: proc {
+    verify_bytes,
+    verify_u64,
+}
+
+// sum_string_4_8 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_4_8 :: proc(msg, key: string) -> u64 {
+    return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_4_8 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
+    ctx: Context
+    hash: u64
+    init(&ctx, key, 4, 8)
+    update(&ctx, msg)
+    final(&ctx, &hash)
+    return hash
+}
+
+// sum_string_to_buffer_4_8 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
+    sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_4_8 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
+    assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+    hash  := sum_bytes_4_8(msg, key)
+    _collect_output(dst[:], hash)
+}
+
+sum_4_8 :: proc {
+    sum_string_4_8,
+    sum_bytes_4_8,
+    sum_string_to_buffer_4_8,
+    sum_bytes_to_buffer_4_8,
+}
+
+// verify_u64_4_8 will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
+    return sum_bytes_4_8(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you 
+// will get from the provided message and key
+verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
+    derived_tag: [8]byte
+    sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
+    return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_4_8 :: proc {
+    verify_bytes_4_8,
+    verify_u64_4_8,
+}
+
+/*
+    Low level API
+*/
+
+init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
+    assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
+    ctx.c_rounds = c_rounds
+    ctx.d_rounds = d_rounds
+    is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
+                        (ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
+                        (ctx.c_rounds == 4 && ctx.d_rounds == 8)
+    assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
+    ctx.k0 = util.U64_LE(key[:8])
+    ctx.k1 = util.U64_LE(key[8:])
+    ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
+    ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
+    ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
+    ctx.v3 = 0x7465646279746573 ~ ctx.k1
+    ctx.is_initialized = true
+}
+
+update :: proc(ctx: ^Context, data: []byte) {
+    assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
+    ctx.last_block = len(data) / 8 * 8
+    ctx.buf = data
+    i := 0
+    m: u64
+    for i < ctx.last_block {
+        m = u64(ctx.buf[i] & 0xff)
+        i += 1
+
+        for r in u64(1)..<8 {
+            m |= u64(ctx.buf[i] & 0xff) << (r * 8)
+            i += 1
+        }
+
+        ctx.v3 ~= m
+        for _ in 0..<ctx.c_rounds {
+            _compress(ctx)
+        }
+
+        ctx.v0 ~= m
+    }
+}
+
+final :: proc(ctx: ^Context, dst: ^u64) {
+    m: u64
+    for i := len(ctx.buf) - 1; i >= ctx.last_block; i -= 1 {
+        m <<= 8
+        m |= u64(ctx.buf[i] & 0xff)
+    }
+    m |= u64(len(ctx.buf) << 56)
+
+    ctx.v3 ~= m
+
+    for _ in 0..<ctx.c_rounds {
+        _compress(ctx)
+    }
+
+    ctx.v0 ~= m
+    ctx.v2 ~= 0xff
+
+    for _ in 0..<ctx.d_rounds {
+        _compress(ctx)
+    }
+
+    dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
+
+    reset(ctx)
+}
+
+reset :: proc(ctx: ^Context) {
+    ctx.k0, ctx.k1 = 0, 0
+    ctx.v0, ctx.v1 = 0, 0
+    ctx.v2, ctx.v3 = 0, 0
+    ctx.last_block = 0
+    ctx.c_rounds = 0
+    ctx.d_rounds = 0
+    ctx.is_initialized = false
+}
+
+Context :: struct {
+    v0, v1, v2, v3: u64,    // State values
+    k0, k1:         u64,    // Split key
+    c_rounds:       int,    // Number of message rounds
+    d_rounds:       int,    // Number of finalization rounds
+    buf:            []byte, // Provided data
+    last_block:     int,    // Offset from the last block
+    is_initialized: bool,
+}
+
+_get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byte {
+    return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
+}
+
+_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
+    dst[0] = _get_byte(7, hash)
+    dst[1] = _get_byte(6, hash)
+    dst[2] = _get_byte(5, hash)
+    dst[3] = _get_byte(4, hash)
+    dst[4] = _get_byte(3, hash)
+    dst[5] = _get_byte(2, hash)
+    dst[6] = _get_byte(1, hash)
+    dst[7] = _get_byte(0, hash)
+}
+
+_compress :: #force_inline proc "contextless" (ctx: ^Context) {
+    ctx.v0 += ctx.v1
+    ctx.v1  = util.ROTL64(ctx.v1, 13)
+    ctx.v1 ~= ctx.v0
+    ctx.v0  = util.ROTL64(ctx.v0, 32)
+    ctx.v2 += ctx.v3
+    ctx.v3  = util.ROTL64(ctx.v3, 16)
+    ctx.v3 ~= ctx.v2
+    ctx.v0 += ctx.v3
+    ctx.v3  = util.ROTL64(ctx.v3, 21)
+    ctx.v3 ~= ctx.v0
+    ctx.v2 += ctx.v1
+    ctx.v1  = util.ROTL64(ctx.v1, 17)
+    ctx.v1 ~= ctx.v2
+    ctx.v2  = util.ROTL64(ctx.v2, 32)
+}

+ 1 - 1
core/dynlib/lib_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package dynlib
 
 import "core:os"

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 189 - 179
core/fmt/fmt.odin


+ 1 - 1
core/image/png/example.odin

@@ -207,7 +207,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
 	}
 
 	mode: int = 0
-	when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+	when ODIN_OS == .Linux || ODIN_OS == .Darwin {
 		// NOTE(justasd): 644 (owner read, write; group read; others read)
 		mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
 	}

+ 1 - 1
core/image/png/helpers.odin

@@ -443,7 +443,7 @@ when false {
 		}
 
 		mode: int = 0
-		when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+		when ODIN_OS == .Linux || ODIN_OS == .Darwin {
 			// NOTE(justasd): 644 (owner read, write; group read; others read)
 			mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
 		}

+ 734 - 0
core/math/noise/internal.odin

@@ -0,0 +1,734 @@
+/*
+	OpenSimplex2 noise implementation.
+
+	Ported from https://github.com/KdotJPG/OpenSimplex2.
+	Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+//+private
+package math_noise
+
+/*
+	Private implementation details follow.
+*/
+
+PRIME_X                  :: i64(0x5205402B9270C86F)
+PRIME_Y                  :: i64(0x598CD327003817B5)
+PRIME_Z                  :: i64(0x5BCC226E9FA0BACB)
+PRIME_W                  :: i64(0x56CC5227E58F554B)
+
+HASH_MULTIPLIER          :: i64(0x53A3F72DEEC546F5)
+SEED_FLIP_3D             :: i64(-0x52D547B2E96ED629)
+SEED_OFFSET_4D           :: i64(0xE83DC3E0DA7164D)
+
+ROOT_2_OVER_2            :: f64(0.7071067811865476)
+SKEW_2D                  :: f64(0.366025403784439)
+UNSKEW_2D                :: f64(-0.21132486540518713)
+ROOT_3_OVER_3            :: f64(0.577350269189626)
+
+FALLBACK_ROTATE_3D       :: f64(2.0) / f64(3.0)
+ROTATE_3D_ORTHOGONALIZER :: f64(UNSKEW_2D)
+
+SKEW_4D                  :: f32(0hbe0d8369)
+UNSKEW_4D                :: f32(0.309016994374947)
+LATTICE_STEP_4D          :: f32(0.2)
+
+N_GRADS_2D_EXPONENT      :: 7
+N_GRADS_3D_EXPONENT      :: 8
+N_GRADS_4D_EXPONENT      :: 9
+N_GRADS_2D               :: 1 << N_GRADS_2D_EXPONENT
+N_GRADS_3D               :: 1 << N_GRADS_3D_EXPONENT
+N_GRADS_4D               :: 1 << N_GRADS_4D_EXPONENT
+
+NORMALIZER_2D            :: f64(0.01001634121365712)
+NORMALIZER_3D            :: f64(0.07969837668935331)
+NORMALIZER_4D            :: f64(0.0220065933241897)
+RSQUARED_2D              :: f32(0.5)
+RSQUARED_3D              :: f32(0.6)
+RSQUARED_4D              :: f32(0.6)
+
+GRADIENTS_2D := [N_GRADS_2D * 2]f32{
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+}
+
+GRADIENTS_3D := [N_GRADS_3D * 4]f32{
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+}
+
+GRADIENTS_4D := [N_GRADS_4D * 4]f32{
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+}
+
+/*
+	2D Simplex noise base.
+*/
+_internal_noise_2d_unskewed_base :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Get base points and offsets.
+	base := [2]i64{fast_floor(coord.x), fast_floor(coord.y)}
+	i    := [2]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y))}
+
+	// Prime pre-multiplication for hash.
+	bp := base * [2]i64{PRIME_X, PRIME_Y}
+
+	// Unskew.
+	t  := f32(i.x + i.y) * f32(UNSKEW_2D)
+	d0 := i + [2]f32{t, t}
+
+	// First vertex.
+	a0 := RSQUARED_2D - d0.x * d0.x - d0.y * d0.y
+	if a0 > 0 {
+		value = (a0 * a0) * (a0 * a0) * grad(seed, [2]i64{bp.x, bp.y}, d0)
+	}
+
+	// Second vertex.
+	a1 := f32(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + f32(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0
+	if a1 > 0 {
+		d1 := d0 - [2]f32{f32(1 + 2 * UNSKEW_2D), f32(1 + 2 * UNSKEW_2D)}
+		value += (a1 * a1) * (a1 * a1) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y + PRIME_Y}, d1)
+	}
+
+	// Third vertex.
+	if d0.y > d0.x {
+		d2 := d0 - [2]f32{f32(UNSKEW_2D), f32(UNSKEW_2D + 1)}
+		a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+		if(a2 > 0) {
+			value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x, bp.y + PRIME_Y}, d2)
+		}
+	} else {
+		d2 := d0 - [2]f32{f32(UNSKEW_2D + 1), f32(UNSKEW_2D)}
+		a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+		if(a2 > 0) {
+			value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y}, d2)
+		}
+	}
+
+	return
+}
+
+
+/*
+	Generate overlapping cubic lattices for 3D OpenSimplex2 noise.
+*/
+_internal_noise_3d_unrotated_base :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	seed := seed
+	// Get base points and offsets.
+	// xr, yr, zr := coord.x, coord.y, coord.z
+
+	rb := [3]i64{fast_round(coord.x), fast_round(coord.y), fast_round(coord.z)}
+	ri := [3]f32{f32(coord.x - f64(rb.x)), f32(coord.y - f64(rb.y)), f32(coord.z - f64(rb.z))}
+
+	// -1 if positive, 1 if negative.
+	i_sign := [3]i64{i64(-1.0 - ri.x) | 1, i64(-1.0 - ri.y) | 1, i64(-1.0 - ri.z) | 1}
+	f_sign := [3]f32{f32(i_sign.x), f32(i_sign.y), f32(i_sign.z)}
+
+	// Compute absolute values, using the above as a shortcut. This was faster in my tests for some reason.
+	a0 := f_sign * -ri
+
+	// Prime pre-multiplication for hash.
+	rbp := rb * [3]i64{PRIME_X, PRIME_Y, PRIME_Z}
+
+	// Loop: Pick an edge on each lattice copy.
+	a := (RSQUARED_3D - ri.x * ri.x) - (ri.y * ri.y + ri.z * ri.z)
+
+	l := 0
+	for {
+		defer l += 1
+		
+		// Closest point on cube.
+		if a > 0 {
+			a2 := a * a; a4 := a2 * a2
+			value += a4 * grad(seed, rbp, ri)
+		}
+
+		// Second-closest point.
+		if a0.x >= a0.y && a0.x >= a0.z {
+			b := a + a0.x + a0.x
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x - i_sign.x * PRIME_X, rbp.y, rbp.z}, [3]f32{ri.x + f_sign.x, ri.y, ri.z})
+			}
+		} else if a0.y > a0.x && a0.y >= a0.z {
+			b := a + a0.y + a0.y
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x, rbp.y - i_sign.y * PRIME_Y, rbp.z}, [3]f32{ri.x, ri.y + f_sign.y, ri.z})
+			}
+		} else {
+			b := a + a0.z + a0.z
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x, rbp.y, rbp.z - i_sign.z * PRIME_Z}, [3]f32{ri.x, ri.y, ri.z + f_sign.z})
+			}
+		}
+
+		// Break from loop if we're done, skipping updates below.
+		if l == 1 {
+			break
+		}
+
+		// Update absolute value.
+		a0 = 0.5 - a0
+
+		// Update relative coordinate.
+		ri = a0 * f_sign
+
+		// Update falloff.
+		a += (0.75 - a0.x) - (a0.y + a0.z)
+
+		// Update prime for hash.
+		rbp += [3]i64{i_sign.x >> 1, i_sign.y >> 1, i_sign.z >> 1} & {PRIME_X, PRIME_Y, PRIME_Z}
+
+		// Update the reverse sign indicators.
+		i_sign = -i_sign
+		f_sign = -f_sign
+
+		// And finally update the seed for the other lattice copy.
+		seed ~= SEED_FLIP_3D
+	}
+
+	return value
+}
+
+/*
+	4D OpenSimplex2 noise base.
+*/
+_internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	seed := seed
+
+	// Get base points and offsets
+	base := [4]i64{fast_floor(coord.x), fast_floor(coord.y), fast_floor(coord.z), fast_floor(coord.w)}
+	si   := [4]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y)), f32(coord.z - f64(base.z)), f32(coord.w - f64(base.w))}
+
+	// Determine which lattice we can be confident has a contributing point its corresponding cell's base simplex.
+	// We only look at the spaces between the diagonal planes. This proved effective in all of my tests.
+	si_sum := (si.x + si.y) + (si.z + si.w)
+	starting_lattice := i64(si_sum * 1.25)
+
+	// Offset for seed based on first lattice copy.
+	seed += starting_lattice * SEED_OFFSET_4D
+
+	// Offset for lattice point relative positions (skewed)
+	starting_lattice_offset := f32(starting_lattice) * -LATTICE_STEP_4D
+	si += starting_lattice_offset
+
+	// Prep for vertex contributions.
+	ssi := (si_sum + starting_lattice_offset * 4) * UNSKEW_4D
+				
+	// Prime pre-multiplication for hash.
+	svp := base * [4]i64{PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+
+	// Five points to add, total, from five copies of the A4 lattice.
+	for i : i64 = 0; ; i += 1 {
+		
+		// Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
+		score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
+		if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
+			svp.x += PRIME_X
+			si.x -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
+			svp.y += PRIME_Y
+			si.y -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
+			svp.z += PRIME_Z
+			si.z -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
+			svp.w += PRIME_W
+			si.w -= 1
+			ssi -= UNSKEW_4D
+		}
+
+		// gradient contribution with falloff.
+		d := si + ssi
+		a := (d.x * d.x + d.y * d.y) + (d.z * d.z + d.w * d.w)
+
+		if a < RSQUARED_4D {
+			a -= RSQUARED_4D
+			a *= a; a4 := a * a
+			value += a4 * grad(seed, svp, d)
+		}
+
+		// Break from loop if we're done, skipping updates below.
+		if i == 4 {
+			break
+		}
+
+		// Update for next lattice copy shifted down by <-0.2, -0.2, -0.2, -0.2>.
+		si   += LATTICE_STEP_4D
+		ssi  += LATTICE_STEP_4D * 4 * UNSKEW_4D
+		seed -= SEED_OFFSET_4D
+
+		// Because we don't always start on the same lattice copy, there's a special reset case.
+		if i == starting_lattice {
+			svp  -= {PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+			seed += SEED_OFFSET_4D * 5
+		}
+	}
+	return
+}
+
+/*
+	Utility functions
+*/
+@(optimization_mode="speed")
+grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
+	hash := seed ~ svp.x ~ svp.y
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_2D_EXPONENT + 1)
+
+	gi   := hash & ((N_GRADS_2D - 1) << 1)
+	return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
+}
+
+@(optimization_mode="speed")
+grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
+	hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_3D_EXPONENT + 2)
+
+	gi   := hash & ((N_GRADS_3D - 1) << 2)
+	return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
+}
+
+@(optimization_mode="speed")
+grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
+	hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_4D_EXPONENT + 2)
+
+	gi := hash & ((N_GRADS_4D - 1) << 2)
+	return (GRADIENTS_4D[gi] * delta.x + GRADIENTS_4D[gi | 1] * delta.y) + (GRADIENTS_4D[gi | 2] * delta.z + GRADIENTS_4D[gi | 3] * delta.w)
+}
+
+grad :: proc {grad_2d, grad_3d, grad_4d}
+
+@(optimization_mode="speed")
+fast_floor :: proc(x: f64) -> (floored: i64) {
+	xi := i64(x)
+	return x < f64(xi) ? xi - 1 : xi
+}
+
+@(optimization_mode="speed")
+fast_round :: proc(x: f64) -> (rounded: i64) {
+	return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
+}

+ 171 - 0
core/math/noise/opensimplex2.odin

@@ -0,0 +1,171 @@
+/*
+	OpenSimplex2 noise implementation.
+
+	Ported from https://github.com/KdotJPG/OpenSimplex2.
+	Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+package math_noise
+
+/*
+	Input coordinate vectors
+*/
+Vec2 :: [2]f64
+Vec3 :: [3]f64
+Vec4 :: [4]f64
+
+/*
+	Noise Evaluators
+*/
+
+/*
+	2D Simplex noise, standard lattice orientation.
+*/
+noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Get points for A2* lattice
+	skew   := SKEW_2D * (coord.x + coord.y)
+	skewed := coord + skew
+
+	return _internal_noise_2d_unskewed_base(seed, skewed)
+}
+
+/*
+	2D Simplex noise, with Y pointing down the main diagonal.
+	Might be better for a 2D sandbox style game, where Y is vertical.
+	Probably slightly less optimal for heightmaps or continent maps,
+	unless your map is centered around an equator. It's a subtle
+	difference, but the option is here to make it an easy choice.
+*/
+noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Skew transform and rotation baked into one.
+	xx := coord.x * ROOT_2_OVER_2
+	yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D))
+	return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx})
+}
+
+
+/*
+	3D OpenSimplex2 noise, with better visual isotropy in (X, Y).
+	Recommended for 3D terrain and time-varied animations.
+	The Z coordinate should always be the "different" coordinate in whatever your use case is.
+	If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`.
+	If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`.
+	For a time varied animation, call `noise_3d_improve_xz(x, y, T)`.
+*/
+noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
+		and the planes formed by XY are moved far out of alignment with the cube faces.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	xy := coord.x + coord.y
+	s2 := xy * ROTATE_3D_ORTHOGONALIZER
+	zz := coord.z * ROOT_3_OVER_3
+
+	r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz}
+
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+	3D OpenSimplex2 noise, with better visual isotropy in (X, Z).
+	Recommended for 3D terrain and time-varied animations.
+	The Y coordinate should always be the "different" coordinate in whatever your use case is.
+	If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`.
+	If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`.
+	For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`.
+*/
+noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
+		and the planes formed by XZ are moved far out of alignment with the cube faces.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	xz := coord.x + coord.z
+	s2 := xz * ROTATE_3D_ORTHOGONALIZER
+	yy := coord.y * ROOT_3_OVER_3
+
+	r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy}
+	
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+	3D OpenSimplex2 noise, fallback rotation option
+	Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate.
+	They have less diagonal bias. This function's best use is as a fallback.
+*/
+noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices via rotation, to produce a familiar look.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	bias   := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z)
+	biased := bias - coord
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, biased)
+}
+
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	in a space where Z is vertical.
+*/
+noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xy := coord.x + coord.y
+	s2 := xy * -0.21132486540518699998
+	zz := coord.z * 0.28867513459481294226
+	ww := coord.w * 0.2236067977499788
+
+	xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2)
+	zr : f64 = xy * -0.57735026918962599998 + (zz + ww)
+	wr : f64 = coord.z * -0.866025403784439 + ww
+
+	return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	in a space where Y is vertical.
+*/
+noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xz := coord.x + coord.z
+	s2 := xz * -0.21132486540518699998
+	yy := coord.y * 0.28867513459481294226
+	ww := coord.w * 0.2236067977499788
+
+	xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2)
+	yr := xz * -0.57735026918962599998 + (yy + ww)
+	wr := coord.y * -0.866025403784439 + ww
+
+	return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	where there isn't a clear distinction between horizontal and vertical
+*/
+noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xyz := coord.x + coord.y + coord.z
+	ww  := coord.w * 0.2236067977499788
+	s2  := xyz * -0.16666666666666666 + ww
+
+	skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww}
+	return _internal_noise_4d_unskewed_base(seed, skewed)
+}
+
+/*
+	4D OpenSimplex2 noise, fallback lattice orientation.
+*/
+noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	// Get points for A4 lattice
+	skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w)
+	return _internal_noise_4d_unskewed_base(seed, coord + skew)
+}

+ 34 - 0
core/mem/doc.odin

@@ -0,0 +1,34 @@
+/*
+package mem implements various types of allocators.
+
+
+An example of how to use the `Tracking_Allocator` to track subsequent allocations
+in your program and report leaks and bad frees:
+
+```odin
+package foo
+
+import "core:mem"
+import "core:fmt"
+
+_main :: proc() {
+	do stuff
+}
+
+main :: proc() {
+	track: mem.Tracking_Allocator
+	mem.tracking_allocator_init(&track, context.allocator)
+	context.allocator = mem.tracking_allocator(&track)
+
+	_main()
+
+	for _, leak in track.allocation_map {
+		fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
+	}
+	for bad_free in track.bad_free_array {
+		fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
+	}
+}
+```
+*/
+package mem

+ 35 - 19
core/mem/virtual/virtual.odin

@@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096)
 
 Allocator_Error :: mem.Allocator_Error
 
-reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	return _reserve(size)
 }
 
-commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	return _commit(data, size)
 }
 
-reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	data = reserve(size) or_return
 	commit(raw_data(data), size) or_return
 	return
 }
 
-decommit :: proc(data: rawptr, size: uint) {
+decommit :: proc "contextless" (data: rawptr, size: uint) {
 	_decommit(data, size)
 }
 
-release :: proc(data: rawptr, size: uint) {
+release :: proc "contextless" (data: rawptr, size: uint) {
 	_release(data, size)
 }
 
@@ -36,7 +36,7 @@ Protect_Flag :: enum u32 {
 Protect_Flags :: distinct bit_set[Protect_Flag; u32]
 Protect_No_Access :: Protect_Flags{}
 
-protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	return _protect(data, size, flags)
 }
 
@@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 	pmblock := platform_memory_alloc(0, total_size) or_return
 	
 	pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
-	commit(pmblock.block.base, committed) or_return
+	commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
+	assert(commit_err == nil)
+
 	// Should be zeroed
 	assert(pmblock.block.used == 0)
 	assert(pmblock.block.prev == nil)	
-	if (do_protection) {
+	if do_protection {
 		protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
 	}
 	
@@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 }
 
 alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
-	calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
+	calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
 		alignment_offset := uint(0)
 		ptr := uintptr(block.base[block.used:])
 		mask := alignment-1
@@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
 		return alignment_offset
 		
 	}
-	
+	do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
+		if block.committed - block.used < size {
+			pmblock := (^Platform_Memory_Block)(block)
+			base_offset := uint(uintptr(block) - uintptr(pmblock))
+			platform_total_commit := base_offset + block.used + size
+
+			assert(pmblock.committed <= pmblock.reserved)
+			assert(pmblock.committed < platform_total_commit)
+
+			platform_memory_commit(pmblock, platform_total_commit) or_return
+
+			pmblock.committed = platform_total_commit
+			block.committed = pmblock.committed - base_offset
+		}
+		return nil
+	}
+
+
 	alignment_offset := calc_alignment_offset(block, uintptr(alignment))
-	
 	size := uint(min_size) + alignment_offset
-	
+
 	if block.used + size > block.reserved {
 		err = .Out_Of_Memory
 		return
 	}
-	
-	ptr := block.base[block.used:]
-	ptr = ptr[alignment_offset:]
-	
+	assert(block.committed <= block.reserved)
+	do_commit_if_necessary(block, size) or_return
+
+	data = block.base[block.used+alignment_offset:][:min_size]
 	block.used += size
-	assert(block.used <= block.reserved)
-	
-	return ptr[:min_size], nil	
+	return
 }
 
 

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

@@ -58,7 +58,7 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
 }
 
 
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	MAP_FAILED := rawptr(~uintptr(0))
 	result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
 	if result == MAP_FAILED {
@@ -67,7 +67,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
 	return ([^]byte)(result)[:size], nil
 }
 
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	result := mprotect(data, size, PROT_READ|PROT_WRITE)
 	if result != 0 {
 		// TODO(bill): Handle error value correctly
@@ -75,14 +75,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
 	}
 	return nil
 }
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
 	mprotect(data, size, PROT_NONE)
 	madvise(data, size, MADV_FREE)
 }
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
 	munmap(data, size)
 }
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	pflags: c.int
 	pflags = PROT_NONE
 	if .Read    in flags { pflags |= PROT_READ  }

+ 21 - 5
core/mem/virtual/virtual_platform.odin

@@ -4,12 +4,13 @@ package mem_virtual
 import sync "core:sync/sync2"
 
 Platform_Memory_Block :: struct {
-	block:    Memory_Block,
-	reserved: uint,
+	block:      Memory_Block,
+	committed:  uint,
+	reserved:   uint,
 	prev, next: ^Platform_Memory_Block,
 } 
 
-platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
+platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
 	to_commit, to_reserve := to_commit, to_reserve
 	to_reserve = max(to_commit, to_reserve)
 	
@@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_
 	commit(raw_data(data), to_commit)
 	
 	block = (^Platform_Memory_Block)(raw_data(data))
-	block.reserved = to_reserve
+	block.committed = to_commit
+	block.reserved  = to_reserve
 	return
 }
 
 
-platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
 	if block != nil {
 		release(block, block.reserved)
 	}
@@ -52,3 +54,17 @@ platform_memory_init :: proc() {
 		global_platform_memory_block_sentinel_set = true
 	}
 }
+
+platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
+	if to_commit < block.committed {
+		return nil
+	}
+	if to_commit > block.reserved {
+		return .Out_Of_Memory
+	}
+
+
+	commit(block, to_commit) or_return
+	block.committed = to_commit
+	return nil
+}

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

@@ -62,7 +62,7 @@ foreign Kernel32 {
 }
 
 
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
 	if result == nil {
 		err = .Out_Of_Memory
@@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
 	return
 }
 
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
 	if result == nil {
 		switch err := GetLastError(); err {
@@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
 	}
 	return nil
 }
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
 	VirtualFree(data, size, MEM_DECOMMIT)
 }
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
 	VirtualFree(data, 0, MEM_RELEASE)
 }
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	pflags: u32
 	pflags = PAGE_NOACCESS
 	switch flags {

+ 14 - 2
core/odin/parser/parser.odin

@@ -428,9 +428,21 @@ expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
 		str := tokenizer.token_to_string(token)
 		error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str)
 	}
-	return expect_token(p, .Close_Brace)
+	expect_brace := expect_token(p, .Close_Brace)
+
+	if expect_brace.kind != .Close_Brace {
+		for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) {
+			advance_token(p)
+		}
+		return p.curr_tok
+	} 
+
+	return expect_brace
 }
 
+is_non_inserted_semicolon :: proc(tok: tokenizer.Token) -> bool {
+	return tok.kind == .Semicolon && tok.text != "\n"
+}
 
 is_blank_ident :: proc{
 	is_blank_ident_string,
@@ -1312,7 +1324,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		}
 
 		results: [dynamic]^ast.Expr
-		for p.curr_tok.kind != .Semicolon {
+		for p.curr_tok.kind != .Semicolon && p.curr_tok.kind != .Close_Brace {
 			result := parse_expr(p, false)
 			append(&results, result)
 			if p.curr_tok.kind != .Comma ||

+ 71 - 0
core/os/dir_openbsd.odin

@@ -0,0 +1,71 @@
+package os
+
+import "core:strings"
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+	dirp: Dir
+	dirp, err = _fdopendir(fd)
+	if err != ERROR_NONE {
+		return
+	}
+
+	defer _closedir(dirp)
+
+	// XXX OpenBSD
+	dirpath: string
+	dirpath, err = absolute_path_from_handle(fd)
+
+	if err != ERROR_NONE {
+		return
+	}
+
+	defer delete(dirpath)
+
+	n := n
+	size := n
+	if n <= 0 {
+		n = -1
+		size = 100
+	}
+
+	dfi := make([dynamic]File_Info, 0, size, allocator)
+
+	for {
+		entry: Dirent
+		end_of_stream: bool
+		entry, err, end_of_stream = _readdir(dirp)
+		if err != ERROR_NONE {
+			for fi_ in dfi {
+				file_info_delete(fi_, allocator)
+			}
+			delete(dfi)
+			return
+		} else if end_of_stream {
+			break
+		}
+
+		fi_: File_Info
+		filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+		if filename == "." || filename == ".." {
+			continue
+		}
+
+		fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
+		defer delete(fullpath, context.temp_allocator)
+
+		fi_, err = stat(fullpath, allocator)
+		if err != ERROR_NONE {
+			for fi__ in dfi {
+				file_info_delete(fi__, allocator)
+			}
+			delete(dfi)
+			return
+		}
+
+		append(&dfi, fi_)
+	}
+
+	return dfi[:], ERROR_NONE
+}

+ 18 - 7
core/os/file_windows.odin

@@ -106,19 +106,23 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
 	BUF_SIZE :: 386
 	buf16: [BUF_SIZE]u16
 	buf8: [4*BUF_SIZE]u8
-	
+
 	for n < len(b) && err == 0 {
-		max_read := u32(min(BUF_SIZE, len(b)/4))
-		
+		min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
+		max_read := u32(min(BUF_SIZE, min_read))
+		if max_read == 0 {
+			break
+		}
+
 		single_read_length: u32
 		ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
 		if !ok {
 			err = Errno(win32.GetLastError())
 		}
-		
+
 		buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
 		src := buf8[:buf8_len]
-		
+
 		ctrl_z := false
 		for i := 0; i < len(src) && n+i < len(b); i += 1 {
 			x := src[i]
@@ -129,9 +133,16 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
 			b[n] = x
 			n += 1
 		}
-		if ctrl_z || single_read_length < len(buf16) {
+		if ctrl_z || single_read_length < max_read {
 			break
 		}
+
+		// NOTE(bill): if the last two values were a newline, then it is expected that
+		// this is the end of the input
+		if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
+			break
+		}
+
 	}
 	
 	return
@@ -399,7 +410,7 @@ is_abs :: proc(path: string) -> bool {
 	if len(path) > 0 && path[0] == '/' {
 		return true
 	}
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
 		if len(path) > 2 {
 			switch path[0] {
 			case 'A'..='Z', 'a'..='z':

+ 1 - 1
core/os/os.odin

@@ -139,7 +139,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
 	}
 
 	mode: int = 0
-	when OS == "linux" || OS == "darwin" {
+	when OS == .Linux || OS == .Darwin {
 		// NOTE(justasd): 644 (owner read, write; group read; others read)
 		mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
 	}

+ 8 - 8
core/os/os2/user.odin

@@ -3,13 +3,13 @@ package os2
 import "core:strings"
 
 user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
-	switch ODIN_OS {
-	case "windows":
+	#partial switch ODIN_OS {
+	case .Windows:
 		dir = get_env("LocalAppData")
 		if dir != "" {
 			dir = strings.clone(dir, allocator)
 		}
-	case "darwin":
+	case .Darwin:
 		dir = get_env("HOME")
 		if dir != "" {
 			dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
@@ -29,13 +29,13 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin
 }
 
 user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
-	switch ODIN_OS {
-	case "windows":
+	#partial switch ODIN_OS {
+	case .Windows:
 		dir = get_env("AppData")
 		if dir != "" {
 			dir = strings.clone(dir, allocator)
 		}
-	case "darwin":
+	case .Darwin:
 		dir = get_env("HOME")
 		if dir != "" {
 			dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
@@ -56,8 +56,8 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi
 
 user_home_dir :: proc() -> (dir: string, is_defined: bool) {
 	env := "HOME"
-	switch ODIN_OS {
-	case "windows":
+	#partial switch ODIN_OS {
+	case .Windows:
 		env = "USERPROFILE"
 	}
 	if v := get_env(env); v != "" {

+ 13 - 4
core/os/os_darwin.odin

@@ -313,6 +313,7 @@ foreign libc {
 	@(link_name="getenv")   _unix_getenv   :: proc(cstring) -> cstring ---
 	@(link_name="getcwd")   _unix_getcwd   :: proc(buf: cstring, len: c.size_t) -> cstring ---
 	@(link_name="chdir")    _unix_chdir    :: proc(buf: cstring) -> c.int ---
+	@(link_name="mkdir")    _unix_mkdir    :: proc(buf: cstring, mode: u32) -> c.int ---
 	@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
 
 	@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
@@ -320,7 +321,7 @@ foreign libc {
 	@(link_name="exit")    _unix_exit :: proc(status: c.int) -> ! ---
 }
 
-when ODIN_ARCH != "arm64" {
+when ODIN_ARCH != .arm64 {
 	_unix_fdopendir :: proc {_unix_fdopendir_amd64}
 	_unix_readdir_r :: proc {_unix_readdir_r_amd64}
 } else {
@@ -344,14 +345,13 @@ get_last_error_string :: proc() -> string {
 }
 
 open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
-	cstr := strings.clone_to_cstring(path)
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 	handle := _unix_open(cstr, i32(flags), u16(mode))
-	delete(cstr)
 	if handle == -1 {
 		return INVALID_HANDLE, 1
 	}
 
-when  ODIN_OS == "darwin" && ODIN_ARCH == "arm64" {
+when  ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
 	if mode != 0 {
 		err := fchmod(handle, cast(u16)mode)
 		if err != 0 {
@@ -670,6 +670,15 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
 	return ERROR_NONE
 }
 
+make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_mkdir(path_cstr, mode)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
 exit :: proc "contextless" (code: int) -> ! {
 	_unix_exit(i32(code))
 }

+ 280 - 287
core/os/os_freebsd.odin

@@ -7,149 +7,149 @@ import "core:runtime"
 import "core:strings"
 import "core:c"
 
-Handle :: distinct i32;
-File_Time :: distinct u64;
-Errno :: distinct i32;
-Syscall :: distinct i32;
-
-INVALID_HANDLE :: ~Handle(0);
-
-ERROR_NONE:      Errno : 0;
-EPERM:           Errno : 1;
-ENOENT:          Errno : 2;
-ESRCH:           Errno : 3;
-EINTR:           Errno : 4;
-EIO:             Errno : 5;
-ENXIO:           Errno : 6;
-E2BIG:           Errno : 7;
-ENOEXEC:         Errno : 8;
-EBADF:           Errno : 9;
-ECHILD:          Errno : 10;
-EBEADLK:         Errno : 11;
-ENOMEM:          Errno : 12;
-EACCESS:         Errno : 13;
-EFAULT:          Errno : 14;
-ENOTBLK:         Errno : 15;
-EBUSY:           Errno : 16;
-EEXIST:          Errno : 17;
-EXDEV:           Errno : 18;
-ENODEV:          Errno : 19;
-ENOTDIR:         Errno : 20;
-EISDIR:          Errno : 21;
-EINVAL:          Errno : 22;
-ENFILE:          Errno : 23;
-EMFILE:          Errno : 24;
-ENOTTY:          Errno : 25;
-ETXTBSY:         Errno : 26;
-EFBIG:           Errno : 27;
-ENOSPC:          Errno : 28;
-ESPIPE:          Errno : 29;
-EROFS:           Errno : 30;
-EMLINK:          Errno : 31;
-EPIPE:           Errno : 32;
-EDOM:            Errno : 33;
-ERANGE:          Errno : 34; /* Result too large */
-EAGAIN:          Errno : 35;
-EINPROGRESS:     Errno : 36;
-EALREADY:        Errno : 37;
-ENOTSOCK:        Errno : 38;
-EDESTADDRREQ:    Errno : 39;
-EMSGSIZE:        Errno : 40;
-EPROTOTYPE:      Errno : 41;
-ENOPROTOOPT:     Errno : 42;
-EPROTONOSUPPORT: Errno : 43;
-ESOCKTNOSUPPORT: Errno : 44;
-EOPNOTSUPP:      Errno : 45;
-EPFNOSUPPORT:    Errno : 46;
-EAFNOSUPPORT:    Errno : 47;
-EADDRINUSE:      Errno : 48;
-EADDRNOTAVAIL:   Errno : 49;
-ENETDOWN:        Errno : 50;
-ENETUNREACH:     Errno : 51;
-ENETRESET:       Errno : 52;
-ECONNABORTED:    Errno : 53;
-ECONNRESET:      Errno : 54;
-ENOBUFS:         Errno : 55;
-EISCONN:         Errno : 56;
-ENOTCONN:        Errno : 57;
-ESHUTDOWN:       Errno : 58;
-ETIMEDOUT:       Errno : 60;
-ECONNREFUSED:    Errno : 61;
-ELOOP:           Errno : 62;
-ENAMETOOLING:    Errno : 63;
-EHOSTDOWN:       Errno : 64;
-EHOSTUNREACH:    Errno : 65;
-ENOTEMPTY:       Errno : 66;
-EPROCLIM:        Errno : 67;
-EUSERS:          Errno : 68;
-EDQUOT:          Errno : 69;
-ESTALE:          Errno : 70;
-EBADRPC:         Errno : 72;
-ERPCMISMATCH:    Errno : 73;
-EPROGUNAVAIL:    Errno : 74;
-EPROGMISMATCH:   Errno : 75;
-EPROCUNAVAIL:    Errno : 76;
-ENOLCK:          Errno : 77;
-ENOSYS:          Errno : 78;
-EFTYPE:          Errno : 79;
-EAUTH:           Errno : 80;
-ENEEDAUTH:       Errno : 81;
-EIDRM:           Errno : 82;
-ENOMSG:          Errno : 83;
-EOVERFLOW:       Errno : 84;
-ECANCELED:       Errno : 85;
-EILSEQ:          Errno : 86;
-ENOATTR:         Errno : 87;
-EDOOFUS:         Errno : 88;
-EBADMSG:         Errno : 89;
-EMULTIHOP:       Errno : 90;
-ENOLINK:         Errno : 91;
-EPROTO:          Errno : 92;
-ENOTCAPABLE:     Errno : 93;
-ECAPMODE:        Errno : 94;
-ENOTRECOVERABLE: Errno : 95;
-EOWNERDEAD:      Errno : 96;
-
-O_RDONLY   :: 0x00000;
-O_WRONLY   :: 0x00001;
-O_RDWR     :: 0x00002;
-O_CREATE   :: 0x00040;
-O_EXCL     :: 0x00080;
-O_NOCTTY   :: 0x00100;
-O_TRUNC    :: 0x00200;
-O_NONBLOCK :: 0x00800;
-O_APPEND   :: 0x00400;
-O_SYNC     :: 0x01000;
-O_ASYNC    :: 0x02000;
-O_CLOEXEC  :: 0x80000;
-
-
-SEEK_SET   :: 0;
-SEEK_CUR   :: 1;
-SEEK_END   :: 2;
-SEEK_DATA  :: 3;
-SEEK_HOLE  :: 4;
-SEEK_MAX   :: SEEK_HOLE;
+Handle :: distinct i32
+File_Time :: distinct u64
+Errno :: distinct i32
+Syscall :: distinct i32
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE:      Errno : 0
+EPERM:           Errno : 1
+ENOENT:          Errno : 2
+ESRCH:           Errno : 3
+EINTR:           Errno : 4
+EIO:             Errno : 5
+ENXIO:           Errno : 6
+E2BIG:           Errno : 7
+ENOEXEC:         Errno : 8
+EBADF:           Errno : 9
+ECHILD:          Errno : 10
+EBEADLK:         Errno : 11
+ENOMEM:          Errno : 12
+EACCESS:         Errno : 13
+EFAULT:          Errno : 14
+ENOTBLK:         Errno : 15
+EBUSY:           Errno : 16
+EEXIST:          Errno : 17
+EXDEV:           Errno : 18
+ENODEV:          Errno : 19
+ENOTDIR:         Errno : 20
+EISDIR:          Errno : 21
+EINVAL:          Errno : 22
+ENFILE:          Errno : 23
+EMFILE:          Errno : 24
+ENOTTY:          Errno : 25
+ETXTBSY:         Errno : 26
+EFBIG:           Errno : 27
+ENOSPC:          Errno : 28
+ESPIPE:          Errno : 29
+EROFS:           Errno : 30
+EMLINK:          Errno : 31
+EPIPE:           Errno : 32
+EDOM:            Errno : 33
+ERANGE:          Errno : 34 /* Result too large */
+EAGAIN:          Errno : 35
+EINPROGRESS:     Errno : 36
+EALREADY:        Errno : 37
+ENOTSOCK:        Errno : 38
+EDESTADDRREQ:    Errno : 39
+EMSGSIZE:        Errno : 40
+EPROTOTYPE:      Errno : 41
+ENOPROTOOPT:     Errno : 42
+EPROTONOSUPPORT: Errno : 43
+ESOCKTNOSUPPORT: Errno : 44
+EOPNOTSUPP:      Errno : 45
+EPFNOSUPPORT:    Errno : 46
+EAFNOSUPPORT:    Errno : 47
+EADDRINUSE:      Errno : 48
+EADDRNOTAVAIL:   Errno : 49
+ENETDOWN:        Errno : 50
+ENETUNREACH:     Errno : 51
+ENETRESET:       Errno : 52
+ECONNABORTED:    Errno : 53
+ECONNRESET:      Errno : 54
+ENOBUFS:         Errno : 55
+EISCONN:         Errno : 56
+ENOTCONN:        Errno : 57
+ESHUTDOWN:       Errno : 58
+ETIMEDOUT:       Errno : 60
+ECONNREFUSED:    Errno : 61
+ELOOP:           Errno : 62
+ENAMETOOLING:    Errno : 63
+EHOSTDOWN:       Errno : 64
+EHOSTUNREACH:    Errno : 65
+ENOTEMPTY:       Errno : 66
+EPROCLIM:        Errno : 67
+EUSERS:          Errno : 68
+EDQUOT:          Errno : 69
+ESTALE:          Errno : 70
+EBADRPC:         Errno : 72
+ERPCMISMATCH:    Errno : 73
+EPROGUNAVAIL:    Errno : 74
+EPROGMISMATCH:   Errno : 75
+EPROCUNAVAIL:    Errno : 76
+ENOLCK:          Errno : 77
+ENOSYS:          Errno : 78
+EFTYPE:          Errno : 79
+EAUTH:           Errno : 80
+ENEEDAUTH:       Errno : 81
+EIDRM:           Errno : 82
+ENOMSG:          Errno : 83
+EOVERFLOW:       Errno : 84
+ECANCELED:       Errno : 85
+EILSEQ:          Errno : 86
+ENOATTR:         Errno : 87
+EDOOFUS:         Errno : 88
+EBADMSG:         Errno : 89
+EMULTIHOP:       Errno : 90
+ENOLINK:         Errno : 91
+EPROTO:          Errno : 92
+ENOTCAPABLE:     Errno : 93
+ECAPMODE:        Errno : 94
+ENOTRECOVERABLE: Errno : 95
+EOWNERDEAD:      Errno : 96
+
+O_RDONLY   :: 0x00000
+O_WRONLY   :: 0x00001
+O_RDWR     :: 0x00002
+O_CREATE   :: 0x00040
+O_EXCL     :: 0x00080
+O_NOCTTY   :: 0x00100
+O_TRUNC    :: 0x00200
+O_NONBLOCK :: 0x00800
+O_APPEND   :: 0x00400
+O_SYNC     :: 0x01000
+O_ASYNC    :: 0x02000
+O_CLOEXEC  :: 0x80000
+
+
+SEEK_SET   :: 0
+SEEK_CUR   :: 1
+SEEK_END   :: 2
+SEEK_DATA  :: 3
+SEEK_HOLE  :: 4
+SEEK_MAX   :: SEEK_HOLE
 
 // NOTE: These are OS specific!
 // Do not mix these up!
-RTLD_LAZY         :: 0x001;
-RTLD_NOW          :: 0x002;
-//RTLD_BINDING_MASK :: 0x3; // Called MODEMASK in dlfcn.h
-RTLD_GLOBAL       :: 0x100;
-RTLD_LOCAL        :: 0x000;
-RTLD_TRACE        :: 0x200;
-RTLD_NODELETE     :: 0x01000;
-RTLD_NOLOAD       :: 0x02000;
+RTLD_LAZY         :: 0x001
+RTLD_NOW          :: 0x002
+//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h
+RTLD_GLOBAL       :: 0x100
+RTLD_LOCAL        :: 0x000
+RTLD_TRACE        :: 0x200
+RTLD_NODELETE     :: 0x01000
+RTLD_NOLOAD       :: 0x02000
 
-args := _alloc_command_line_arguments();
+args := _alloc_command_line_arguments()
 
 Unix_File_Time :: struct {
 	seconds: i64,
 	nanoseconds: c.long,
 }
 
-pid_t :: u32;
+pid_t :: u32
 
 OS_Stat :: struct {
 	device_id: u64,
@@ -177,297 +177,290 @@ OS_Stat :: struct {
 }
 
 // File type
-S_IFMT   :: 0o170000; // Type of file mask
-S_IFIFO  :: 0o010000; // Named pipe (fifo)
-S_IFCHR  :: 0o020000; // Character special
-S_IFDIR  :: 0o040000; // Directory
-S_IFBLK  :: 0o060000; // Block special
-S_IFREG  :: 0o100000; // Regular
-S_IFLNK  :: 0o120000; // Symbolic link
-S_IFSOCK :: 0o140000; // Socket
-//S_ISVTX  :: 0o001000; // Save swapped text even after use
+S_IFMT   :: 0o170000 // Type of file mask
+S_IFIFO  :: 0o010000 // Named pipe (fifo)
+S_IFCHR  :: 0o020000 // Character special
+S_IFDIR  :: 0o040000 // Directory
+S_IFBLK  :: 0o060000 // Block special
+S_IFREG  :: 0o100000 // Regular
+S_IFLNK  :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+//S_ISVTX  :: 0o001000 // Save swapped text even after use
 
 // File mode
 // Read, write, execute/search by owner
-S_IRWXU :: 0o0700; // RWX mask for owner
-S_IRUSR :: 0o0400; // R for owner
-S_IWUSR :: 0o0200; // W for owner
-S_IXUSR :: 0o0100; // X for owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
 
 	// Read, write, execute/search by group
-S_IRWXG :: 0o0070; // RWX mask for group
-S_IRGRP :: 0o0040; // R for group
-S_IWGRP :: 0o0020; // W for group
-S_IXGRP :: 0o0010; // X for group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
 
 	// Read, write, execute/search by others
-S_IRWXO :: 0o0007; // RWX mask for other
-S_IROTH :: 0o0004; // R for other
-S_IWOTH :: 0o0002; // W for other
-S_IXOTH :: 0o0001; // X for other
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
 
-S_ISUID :: 0o4000; // Set user id on execution
-S_ISGID :: 0o2000; // Set group id on execution
-S_ISVTX :: 0o1000; // Directory restrcted delete
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISVTX :: 0o1000 // Directory restrcted delete
 
 
-S_ISLNK  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
-S_ISREG  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
-S_ISDIR  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
-S_ISCHR  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
-S_ISBLK  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
-S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
-S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
+S_ISLNK  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK
+S_ISREG  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG
+S_ISDIR  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR
+S_ISCHR  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR
+S_ISBLK  :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK
+S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO
+S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK
 
-F_OK :: 0; // Test for file existance
-X_OK :: 1; // Test for execute permission
-W_OK :: 2; // Test for write permission
-R_OK :: 4; // Test for read permission
+F_OK :: 0 // Test for file existance
+X_OK :: 1 // Test for execute permission
+W_OK :: 2 // Test for write permission
+R_OK :: 4 // Test for read permission
 
 foreign libc {
-	@(link_name="__error") __errno_location :: proc() -> ^int ---;
-	@(link_name="syscall")          syscall          :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
-
-	@(link_name="open")             _unix_open       :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
-	@(link_name="close")            _unix_close         :: proc(fd: Handle) -> c.int ---;
-	@(link_name="read")             _unix_read          :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
-	@(link_name="write")            _unix_write         :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
-	@(link_name="lseek64")          _unix_seek          :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
-	@(link_name="gettid")           _unix_gettid        :: proc() -> u64 ---;
-	@(link_name="getpagesize")      _unix_getpagesize   :: proc() -> c.int ---;
-	@(link_name="stat64")           _unix_stat          :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---;
-	@(link_name="fstat")            _unix_fstat         :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---;
-	@(link_name="access")           _unix_access        :: proc(path: cstring, mask: c.int) -> c.int ---;
-
-	@(link_name="malloc")           _unix_malloc        :: proc(size: c.size_t) -> rawptr ---;
-	@(link_name="calloc")           _unix_calloc        :: proc(num, size: c.size_t) -> rawptr ---;
-	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---;
-	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
-	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---;
-	@(link_name="getcwd")           _unix_getcwd        :: proc(buf: cstring, len: c.size_t) -> cstring ---;
-	@(link_name="chdir")            _unix_chdir         :: proc(buf: cstring) -> c.int ---;
-
-	@(link_name="exit")             _unix_exit          :: proc(status: c.int) -> ! ---;
+	@(link_name="__error") __errno_location :: proc() -> ^int ---
+	@(link_name="syscall")          syscall          :: proc(number: Syscall, #c_vararg args: ..any) -> int ---
+
+	@(link_name="open")             _unix_open       :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+	@(link_name="close")            _unix_close         :: proc(fd: Handle) -> c.int ---
+	@(link_name="read")             _unix_read          :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="write")            _unix_write         :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="lseek64")          _unix_seek          :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
+	@(link_name="gettid")           _unix_gettid        :: proc() -> u64 ---
+	@(link_name="getpagesize")      _unix_getpagesize   :: proc() -> c.int ---
+	@(link_name="stat64")           _unix_stat          :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
+	@(link_name="fstat")            _unix_fstat         :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
+	@(link_name="access")           _unix_access        :: proc(path: cstring, mask: c.int) -> c.int ---
+
+	@(link_name="malloc")           _unix_malloc        :: proc(size: c.size_t) -> rawptr ---
+	@(link_name="calloc")           _unix_calloc        :: proc(num, size: c.size_t) -> rawptr ---
+	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---
+	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
+	@(link_name="getcwd")           _unix_getcwd        :: proc(buf: cstring, len: c.size_t) -> cstring ---
+	@(link_name="chdir")            _unix_chdir         :: proc(buf: cstring) -> c.int ---
+
+	@(link_name="exit")             _unix_exit          :: proc(status: c.int) -> ! ---
 }
 foreign dl {
-	@(link_name="dlopen")           _unix_dlopen        :: proc(filename: cstring, flags: c.int) -> rawptr ---;
-	@(link_name="dlsym")            _unix_dlsym         :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
-	@(link_name="dlclose")          _unix_dlclose       :: proc(handle: rawptr) -> c.int ---;
-	@(link_name="dlerror")          _unix_dlerror       :: proc() -> cstring ---;
+	@(link_name="dlopen")           _unix_dlopen        :: proc(filename: cstring, flags: c.int) -> rawptr ---
+	@(link_name="dlsym")            _unix_dlsym         :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+	@(link_name="dlclose")          _unix_dlclose       :: proc(handle: rawptr) -> c.int ---
+	@(link_name="dlerror")          _unix_dlerror       :: proc() -> cstring ---
 
-	@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---;
+	@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
 }
 
 is_path_separator :: proc(r: rune) -> bool {
-	return r == '/';
+	return r == '/'
 }
 
 get_last_error :: proc() -> int {
-	return __errno_location()^;
+	return __errno_location()^
 }
 
 open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
-	cstr := strings.clone_to_cstring(path);
-	handle := _unix_open(cstr, c.int(flags), c.int(mode));
-	delete(cstr);
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	handle := _unix_open(cstr, c.int(flags), c.int(mode))
 	if handle == -1 {
-		return INVALID_HANDLE, Errno(get_last_error());
+		return INVALID_HANDLE, Errno(get_last_error())
 	}
-	return handle, ERROR_NONE;
+	return handle, ERROR_NONE
 }
 
 close :: proc(fd: Handle) -> Errno {
-	result := _unix_close(fd);
+	result := _unix_close(fd)
 	if result == -1 {
-		return Errno(get_last_error());
+		return Errno(get_last_error())
 	}
-	return ERROR_NONE;
+	return ERROR_NONE
 }
 
 read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
-	bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
+	bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
 	if bytes_read == -1 {
-		return -1, Errno(get_last_error());
+		return -1, Errno(get_last_error())
 	}
-	return int(bytes_read), ERROR_NONE;
+	return int(bytes_read), ERROR_NONE
 }
 
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	if len(data) == 0 {
-		return 0, ERROR_NONE;
+		return 0, ERROR_NONE
 	}
-	bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
+	bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
 	if bytes_written == -1 {
-		return -1, Errno(get_last_error());
+		return -1, Errno(get_last_error())
 	}
-	return int(bytes_written), ERROR_NONE;
+	return int(bytes_written), ERROR_NONE
 }
 
 seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
-	res := _unix_seek(fd, offset, c.int(whence));
+	res := _unix_seek(fd, offset, c.int(whence))
 	if res == -1 {
-		return -1, Errno(get_last_error());
+		return -1, Errno(get_last_error())
 	}
-	return res, ERROR_NONE;
+	return res, ERROR_NONE
 }
 
 file_size :: proc(fd: Handle) -> (i64, Errno) {
-	s, err := fstat(fd);
+	s, err := fstat(fd)
 	if err != ERROR_NONE {
-		return -1, err;
+		return -1, err
 	}
-	return s.size, ERROR_NONE;
+	return s.size, ERROR_NONE
 }
 
-stdin: Handle = 0;
-stdout: Handle = 1;
-stderr: Handle = 2;
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
 
 last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
-	s, err := fstat(fd);
+	s, err := fstat(fd)
 	if err != ERROR_NONE {
-		return 0, err;
+		return 0, err
 	}
-	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
-	return File_Time(modified), ERROR_NONE;
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
 }
 
 last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
-	s, err := stat(name);
+	s, err := stat(name)
 	if err != ERROR_NONE {
-		return 0, err;
+		return 0, err
 	}
-	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
-	return File_Time(modified), ERROR_NONE;
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
 }
 
 stat :: proc(path: string) -> (OS_Stat, Errno) {
-	cstr := strings.clone_to_cstring(path);
-	defer delete(cstr);
-
-	s: OS_Stat;
-	result := _unix_stat(cstr, &s);
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	s: OS_Stat
+	result := _unix_stat(cstr, &s)
 	if result == -1 {
-		return s, Errno(get_last_error());
+		return s, Errno(get_last_error())
 	}
-	return s, ERROR_NONE;
+	return s, ERROR_NONE
 }
 
 fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
-	s: OS_Stat;
-	result := _unix_fstat(fd, &s);
+	s: OS_Stat
+	result := _unix_fstat(fd, &s)
 	if result == -1 {
-		return s, Errno(get_last_error());
+		return s, Errno(get_last_error())
 	}
-	return s, ERROR_NONE;
+	return s, ERROR_NONE
 }
 
 access :: proc(path: string, mask: int) -> (bool, Errno) {
-	cstr := strings.clone_to_cstring(path);
-	defer delete(cstr);
-	result := _unix_access(cstr, c.int(mask));
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	result := _unix_access(cstr, c.int(mask))
 	if result == -1 {
-		return false, Errno(get_last_error());
+		return false, Errno(get_last_error())
 	}
-	return true, ERROR_NONE;
+	return true, ERROR_NONE
 }
 
 heap_alloc :: proc(size: int) -> rawptr {
-	assert(size >= 0);
-	return _unix_calloc(1, c.size_t(size));
+	assert(size >= 0)
+	return _unix_calloc(1, c.size_t(size))
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
 	// POSIX platforms. Ensure your caller takes this into account.
-	return _unix_realloc(ptr, c.size_t(new_size));
+	return _unix_realloc(ptr, c.size_t(new_size))
 }
 
 heap_free :: proc(ptr: rawptr) {
-	_unix_free(ptr);
+	_unix_free(ptr)
 }
 
 getenv :: proc(name: string) -> (string, bool) {
-	path_str := strings.clone_to_cstring(name);
-	defer delete(path_str);
-	cstr := _unix_getenv(path_str);
+	path_str := strings.clone_to_cstring(name, context.temp_allocator)
+	cstr := _unix_getenv(path_str)
 	if cstr == nil {
-		return "", false;
+		return "", false
 	}
-	return string(cstr), true;
+	return string(cstr), true
 }
 
 get_current_directory :: proc() -> string {
 	// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
 	// an authoritative value for it across all systems.
 	// The largest value I could find was 4096, so might as well use the page size.
-	page_size := get_page_size();
-	buf := make([dynamic]u8, page_size);
+	page_size := get_page_size()
+	buf := make([dynamic]u8, page_size)
 	#no_bounds_check for {
-		cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)));
+		cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
 		if cwd != nil {
-			return string(cwd);
+			return string(cwd)
 		}
 		if Errno(get_last_error()) != ERANGE {
-			return "";
+			return ""
 		}
-		resize(&buf, len(buf)+page_size);
+		resize(&buf, len(buf)+page_size)
 	}
-	unreachable();
+	unreachable()
 }
 
 set_current_directory :: proc(path: string) -> (err: Errno) {
-	cstr := strings.clone_to_cstring(path, context.temp_allocator);
-	res := _unix_chdir(cstr);
-	if res == -1 do return Errno(get_last_error());
-	return ERROR_NONE;
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_chdir(cstr)
+	if res == -1 do return Errno(get_last_error())
+	return ERROR_NONE
 }
 
 exit :: proc "contextless" (code: int) -> ! {
-	_unix_exit(c.int(code));
+	_unix_exit(c.int(code))
 }
 
 current_thread_id :: proc "contextless" () -> int {
-	return cast(int) pthread_getthreadid_np();
+	return cast(int) pthread_getthreadid_np()
 }
 
 dlopen :: proc(filename: string, flags: int) -> rawptr {
-	cstr := strings.clone_to_cstring(filename);
-	defer delete(cstr);
-	handle := _unix_dlopen(cstr, c.int(flags));
-	return handle;
+	cstr := strings.clone_to_cstring(filename, context.temp_allocator)
+	handle := _unix_dlopen(cstr, c.int(flags))
+	return handle
 }
 dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
-	assert(handle != nil);
-	cstr := strings.clone_to_cstring(symbol);
-	defer delete(cstr);
-	proc_handle := _unix_dlsym(handle, cstr);
-	return proc_handle;
+	assert(handle != nil)
+	cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+	proc_handle := _unix_dlsym(handle, cstr)
+	return proc_handle
 }
 dlclose :: proc(handle: rawptr) -> bool {
-	assert(handle != nil);
-	return _unix_dlclose(handle) == 0;
+	assert(handle != nil)
+	return _unix_dlclose(handle) == 0
 }
 dlerror :: proc() -> string {
-	return string(_unix_dlerror());
+	return string(_unix_dlerror())
 }
 
 get_page_size :: proc() -> int {
 	// NOTE(tetra): The page size never changes, so why do anything complicated
 	// if we don't have to.
-	@static page_size := -1;
-	if page_size != -1 do return page_size;
+	@static page_size := -1
+	if page_size != -1 do return page_size
 
-	page_size = int(_unix_getpagesize());
-	return page_size;
+	page_size = int(_unix_getpagesize())
+	return page_size
 }
 
 
 _alloc_command_line_arguments :: proc() -> []string {
-	res := make([]string, len(runtime.args__));
+	res := make([]string, len(runtime.args__))
 	for arg, i in runtime.args__ {
-		res[i] = string(arg);
+		res[i] = string(arg)
 	}
-	return res;
+	return res
 }
 

+ 63 - 39
core/os/os_linux.odin

@@ -11,6 +11,7 @@ import "core:intrinsics"
 import "core:sys/unix"
 
 Handle    :: distinct i32
+Pid       :: distinct i32
 File_Time :: distinct u64
 Errno     :: distinct i32
 
@@ -150,6 +151,8 @@ ERFKILL: 		Errno : 132	/* Operation not possible due to RF-kill */
 
 EHWPOISON: 		Errno : 133	/* Memory page has hardware error */
 
+ADDR_NO_RANDOMIZE :: 0x40000
+
 O_RDONLY   :: 0x00000
 O_WRONLY   :: 0x00001
 O_RDWR     :: 0x00002
@@ -270,8 +273,17 @@ AT_FDCWD            :: -100
 AT_REMOVEDIR        :: uintptr(0x200)
 AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
 
+_unix_personality :: proc(persona: u64) -> int {
+	return int(intrinsics.syscall(unix.SYS_personality, uintptr(persona)))
+}
+
+_unix_fork :: proc() -> Pid {
+	res := int(intrinsics.syscall(unix.SYS_fork))
+	return -1 if res < 0 else Pid(res)
+}
+
 _unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
 	} else { // NOTE: arm64 does not have open
 		res := int(intrinsics.syscall(unix.SYS_openat, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode))))
@@ -292,7 +304,7 @@ _unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
 }
 
 _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
-	when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 		return i64(intrinsics.syscall(unix.SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
 	} else {
 		low := uintptr(offset & 0xFFFFFFFF)
@@ -304,9 +316,9 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
 }
 
 _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
-	when ODIN_ARCH == "amd64" {
+	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != "arm64" {
+	} else when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have stat
 		return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), 0))
@@ -314,7 +326,7 @@ _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
 }
 
 _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
-	when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
 		return int(intrinsics.syscall(unix.SYS_fstat, uintptr(fd), uintptr(stat)))
 	} else {
 		return int(intrinsics.syscall(unix.SYS_fstat64, uintptr(fd), uintptr(stat)))
@@ -322,9 +334,9 @@ _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
 }
 
 _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
-	when ODIN_ARCH == "amd64" {
+	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != "arm64" {
+	} else when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have any lstat
 		return int(intrinsics.syscall(unix.SYS_fstatat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
@@ -332,7 +344,7 @@ _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
 }
 
 _unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 	} else { // NOTE: arm64 does not have readlink
 		return int(intrinsics.syscall(unix.SYS_readlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
@@ -340,7 +352,7 @@ _unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
 }
 
 _unix_access :: proc(path: cstring, mask: int) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask)))
 	} else { // NOTE: arm64 does not have access
 		return int(intrinsics.syscall(unix.SYS_faccessat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mask)))
@@ -356,7 +368,7 @@ _unix_chdir :: proc(path: cstring) -> int {
 }
 
 _unix_rename :: proc(old, new: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
 	} else { // NOTE: arm64 does not have rename
 		return int(intrinsics.syscall(unix.SYS_renameat, uintptr(AT_FDCWD), uintptr(rawptr(old)), uintptr(rawptr(new))))
@@ -364,7 +376,7 @@ _unix_rename :: proc(old, new: cstring) -> int {
 }
 
 _unix_unlink :: proc(path: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have unlink
 		return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path), 0)))
@@ -372,7 +384,7 @@ _unix_unlink :: proc(path: cstring) -> int {
 }
 
 _unix_rmdir :: proc(path: cstring) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have rmdir
 		return int(intrinsics.syscall(unix.SYS_unlinkat, uintptr(AT_FDCWD), uintptr(rawptr(path)), AT_REMOVEDIR))
@@ -380,7 +392,7 @@ _unix_rmdir :: proc(path: cstring) -> int {
 }
 
 _unix_mkdir :: proc(path: cstring, mode: u32) -> int {
-	when ODIN_ARCH != "arm64" {
+	when ODIN_ARCH != .arm64 {
 		return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 	} else { // NOTE: arm64 does not have mkdir
 		return int(intrinsics.syscall(unix.SYS_mkdirat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode)))
@@ -431,10 +443,25 @@ get_last_error :: proc() -> int {
 	return __errno_location()^
 }
 
+personality :: proc(persona: u64) -> (Errno) {
+	res := _unix_personality(persona)
+	if res == -1 {
+		return _get_errno(res)
+	}
+	return ERROR_NONE
+}
+
+fork :: proc() -> (Pid, Errno) {
+	pid := _unix_fork()
+	if pid == -1 {
+		return -1, _get_errno(int(pid))
+	}
+	return pid, ERROR_NONE
+}
+
 open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
-	cstr := strings.clone_to_cstring(path)
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 	handle := _unix_open(cstr, flags, mode)
-	defer delete(cstr)
 	if handle < 0 {
 		return INVALID_HANDLE, _get_errno(int(handle))
 	}
@@ -473,11 +500,13 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 }
 
 file_size :: proc(fd: Handle) -> (i64, Errno) {
-	s, err := _fstat(fd)
-	if err != ERROR_NONE {
-		return 0, err
-	}
-	return max(s.size, 0), ERROR_NONE
+    // deliberately uninitialized; the syscall fills this buffer for us
+    s: OS_Stat = ---
+    result := _unix_fstat(fd, &s)
+    if result < 0 {
+        return 0, _get_errno(result)
+    }
+    return max(s.size, 0), ERROR_NONE
 }
 
 rename :: proc(old_path, new_path: string) -> Errno {
@@ -580,10 +609,10 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
 
 @private
 _stat :: proc(path: string) -> (OS_Stat, Errno) {
-	cstr := strings.clone_to_cstring(path)
-	defer delete(cstr)
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 
-	s: OS_Stat
+	// deliberately uninitialized; the syscall fills this buffer for us
+	s: OS_Stat = ---
 	result := _unix_stat(cstr, &s)
 	if result < 0 {
 		return s, _get_errno(result)
@@ -593,10 +622,10 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
 
 @private
 _lstat :: proc(path: string) -> (OS_Stat, Errno) {
-	cstr := strings.clone_to_cstring(path)
-	defer delete(cstr)
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 
-	s: OS_Stat
+	// deliberately uninitialized; the syscall fills this buffer for us
+	s: OS_Stat = ---
 	result := _unix_lstat(cstr, &s)
 	if result < 0 {
 		return s, _get_errno(result)
@@ -606,7 +635,8 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
 
 @private
 _fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
-	s: OS_Stat
+	// deliberately uninitialized; the syscall fills this buffer for us
+	s: OS_Stat = ---
 	result := _unix_fstat(fd, &s)
 	if result < 0 {
 		return s, _get_errno(result)
@@ -659,8 +689,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
 
 @private
 _readlink :: proc(path: string) -> (string, Errno) {
-	path_cstr := strings.clone_to_cstring(path)
-	defer delete(path_cstr)
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
 
 	bufsz : uint = 256
 	buf := make([]byte, bufsz)
@@ -696,8 +725,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
 		rel = "."
 	}
 
-	rel_cstr := strings.clone_to_cstring(rel)
-	defer delete(rel_cstr)
+	rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
 
 	path_ptr := _unix_realpath(rel_cstr, nil)
 	if path_ptr == nil {
@@ -712,8 +740,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
 }
 
 access :: proc(path: string, mask: int) -> (bool, Errno) {
-	cstr := strings.clone_to_cstring(path)
-	defer delete(cstr)
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
 	result := _unix_access(cstr, mask)
 	if result < 0 {
 		return false, _get_errno(result)
@@ -737,8 +764,7 @@ heap_free :: proc(ptr: rawptr) {
 }
 
 getenv :: proc(name: string) -> (string, bool) {
-	path_str := strings.clone_to_cstring(name)
-	defer delete(path_str)
+	path_str := strings.clone_to_cstring(name, context.temp_allocator)
 	cstr := _unix_getenv(path_str)
 	if cstr == nil {
 		return "", false
@@ -784,15 +810,13 @@ current_thread_id :: proc "contextless" () -> int {
 }
 
 dlopen :: proc(filename: string, flags: int) -> rawptr {
-	cstr := strings.clone_to_cstring(filename)
-	defer delete(cstr)
+	cstr := strings.clone_to_cstring(filename, context.temp_allocator)
 	handle := _unix_dlopen(cstr, c.int(flags))
 	return handle
 }
 dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
 	assert(handle != nil)
-	cstr := strings.clone_to_cstring(symbol)
-	defer delete(cstr)
+	cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
 	proc_handle := _unix_dlsym(handle, cstr)
 	return proc_handle
 }

+ 706 - 0
core/os/os_openbsd.odin

@@ -0,0 +1,706 @@
+package os
+
+foreign import libc "system:c"
+
+import "core:runtime"
+import "core:strings"
+import "core:c"
+
+Handle    :: distinct i32
+Pid       :: distinct i32
+File_Time :: distinct u64
+Errno     :: distinct i32
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE:	Errno: 0
+
+EPERM:		Errno: 1
+ENOENT:		Errno: 2
+ESRCH:		Errno: 3
+EINTR:		Errno: 4
+EIO:		Errno: 5
+ENXIO:		Errno: 6
+E2BIG:		Errno: 7
+ENOEXEC:	Errno: 8
+EBADF:		Errno: 9
+ECHILD:		Errno: 10
+EDEADLK:	Errno: 11
+ENOMEM:		Errno: 12
+EACCES:		Errno: 13
+EFAULT:		Errno: 14
+ENOTBLK:	Errno: 15
+EBUSY:		Errno: 16
+EEXIST:		Errno: 17
+EXDEV:		Errno: 18
+ENODEV:		Errno: 19
+ENOTDIR:	Errno: 20
+EISDIR:		Errno: 21
+EINVAL:		Errno: 22
+ENFILE:		Errno: 23
+EMFILE:		Errno: 24
+ENOTTY:		Errno: 25
+ETXTBSY:	Errno: 26
+EFBIG:		Errno: 27
+ENOSPC:		Errno: 28
+ESPIPE:		Errno: 29
+EROFS:		Errno: 30
+EMLINK:		Errno: 31
+EPIPE:		Errno: 32
+EDOM:		Errno: 33
+ERANGE:		Errno: 34
+EAGAIN:		Errno: 35
+EWOULDBLOCK:	Errno: EAGAIN
+EINPROGRESS:	Errno: 36
+EALREADY:	Errno: 37
+ENOTSOCK:	Errno: 38
+EDESTADDRREQ:	Errno: 39
+EMSGSIZE:	Errno: 40
+EPROTOTYPE:	Errno: 41
+ENOPROTOOPT:	Errno: 42
+EPROTONOSUPPORT: Errno: 43
+ESOCKTNOSUPPORT: Errno: 44
+EOPNOTSUPP:	Errno: 45
+EPFNOSUPPORT:	Errno: 46
+EAFNOSUPPORT:	Errno: 47
+EADDRINUSE:	Errno: 48
+EADDRNOTAVAIL:	Errno: 49
+ENETDOWN:	Errno: 50
+ENETUNREACH:	Errno: 51
+ENETRESET:	Errno: 52
+ECONNABORTED:	Errno: 53
+ECONNRESET:	Errno: 54
+ENOBUFS:	Errno: 55
+EISCONN:	Errno: 56
+ENOTCONN:	Errno: 57
+ESHUTDOWN:	Errno: 58
+ETOOMANYREFS:	Errno: 59
+ETIMEDOUT:	Errno: 60
+ECONNREFUSED:	Errno: 61
+ELOOP:		Errno: 62
+ENAMETOOLONG:	Errno: 63
+EHOSTDOWN:	Errno: 64
+EHOSTUNREACH:	Errno: 65
+ENOTEMPTY:	Errno: 66
+EPROCLIM:	Errno: 67
+EUSERS:		Errno: 68
+EDQUOT:		Errno: 69
+ESTALE:		Errno: 70
+EREMOTE:	Errno: 71
+EBADRPC:	Errno: 72
+ERPCMISMATCH:	Errno: 73
+EPROGUNAVAIL:	Errno: 74
+EPROGMISMATCH:	Errno: 75
+EPROCUNAVAIL:	Errno: 76
+ENOLCK:		Errno: 77
+ENOSYS:		Errno: 78
+EFTYPE:		Errno: 79
+EAUTH:		Errno: 80
+ENEEDAUTH:	Errno: 81
+EIPSEC:		Errno: 82
+ENOATTR:	Errno: 83
+EILSEQ:		Errno: 84
+ENOMEDIUM:	Errno: 85
+EMEDIUMTYPE:	Errno: 86
+EOVERFLOW:	Errno: 87
+ECANCELED:	Errno: 88
+EIDRM:		Errno: 89
+ENOMSG:		Errno: 90
+ENOTSUP:	Errno: 91
+EBADMSG:	Errno: 92
+ENOTRECOVERABLE: Errno: 93
+EOWNERDEAD:	Errno: 94
+EPROTO:		Errno: 95
+
+O_RDONLY   :: 0x00000
+O_WRONLY   :: 0x00001
+O_RDWR     :: 0x00002
+O_NONBLOCK :: 0x00004
+O_APPEND   :: 0x00008
+O_ASYNC    :: 0x00040
+O_SYNC     :: 0x00080
+O_CREATE   :: 0x00200
+O_TRUNC    :: 0x00400
+O_EXCL     :: 0x00800
+O_NOCTTY   :: 0x08000
+O_CLOEXEC  :: 0x10000
+
+SEEK_SET :: 0
+SEEK_CUR :: 1
+SEEK_END :: 2
+
+RTLD_LAZY     :: 0x001
+RTLD_NOW      :: 0x002
+RTLD_LOCAL    :: 0x000
+RTLD_GLOBAL   :: 0x100
+RTLD_TRACE    :: 0x200
+RTLD_NODELETE :: 0x400
+
+MAX_PATH :: 1024
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+pid_t     :: i32
+time_t    :: i64
+mode_t    :: u32
+dev_t     :: i32
+ino_t     :: u64
+nlink_t   :: u32
+uid_t     :: u32
+gid_t     :: u32
+off_t     :: i64
+blkcnt_t  :: u64
+blksize_t :: i32
+
+Unix_File_Time :: struct {
+	seconds:     time_t,
+	nanoseconds: c.long,
+}
+
+OS_Stat :: struct {
+	mode: mode_t,			// inode protection mode
+	device_id: dev_t,		// inode's device
+	serial: ino_t,			// inode's number
+	nlink: nlink_t,			// number of hard links
+	uid: uid_t,			// user ID of the file's owner
+	gid: gid_t,			// group ID of the file's group
+	rdev: dev_t,			// device type
+
+	last_access: Unix_File_Time,	// time of last access
+	modified: Unix_File_Time,	// time of last data modification
+	status_change: Unix_File_Time,	// time of last file status change
+
+	size: off_t,			// file size, in bytes
+	blocks: blkcnt_t,		// blocks allocated for file
+	block_size:	blksize_t,	// optimal blocksize for I/O
+
+	flags:		u32,		// user defined flags for file
+	gen:		u32,		// file generation number
+	birthtime:	Unix_File_Time,	// time of file creation
+}
+
+MAXNAMLEN :: 255
+
+// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above
+Dirent :: struct {
+	ino:      ino_t,	// file number of entry
+	off:      off_t,	// offset after this entry
+	reclen:   u16,		// length of this record
+	type:     u8,		// file type
+	namlen:   u8,		// length of string in name
+	_padding: [4]u8,
+	name:     [MAXNAMLEN + 1]byte, // name
+}
+
+Dir :: distinct rawptr // DIR*
+
+// File type
+S_IFMT   :: 0o170000 // Type of file mask
+S_IFIFO  :: 0o010000 // Named pipe (fifo)
+S_IFCHR  :: 0o020000 // Character special
+S_IFDIR  :: 0o040000 // Directory
+S_IFBLK  :: 0o060000 // Block special
+S_IFREG  :: 0o100000 // Regular
+S_IFLNK  :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+S_ISVTX  :: 0o001000 // Save swapped text even after use
+
+// File mode
+	// Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+	// Read, write, execute/search by group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
+
+	// Read, write, execute/search by others
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISTXT :: 0o1000 // Sticky bit
+
+S_ISLNK  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK  }
+S_ISREG  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG  }
+S_ISDIR  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR  }
+S_ISCHR  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR  }
+S_ISBLK  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK  }
+S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO  }
+S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+
+F_OK :: 0x00 // Test for file existance
+X_OK :: 0x01 // Test for execute permission
+W_OK :: 0x02 // Test for write permission
+R_OK :: 0x04 // Test for read permission
+
+AT_FDCWD            :: -100
+AT_EACCESS          :: 0x01
+AT_SYMLINK_NOFOLLOW :: 0x02
+AT_SYMLINK_FOLLOW   :: 0x04
+AT_REMOVEDIR        :: 0x08
+
+@(default_calling_convention="c")
+foreign libc {
+	@(link_name="__errno")	__errno		:: proc() -> ^int ---
+
+	@(link_name="fork")	_unix_fork	:: proc() -> pid_t ---
+	@(link_name="getthrid")	_unix_getthrid	:: proc() -> int ---
+
+	@(link_name="open")	_unix_open	:: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+	@(link_name="close")	_unix_close	:: proc(fd: Handle) -> c.int ---
+	@(link_name="read")	_unix_read	:: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="write")	_unix_write	:: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="lseek")	_unix_seek	:: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+	@(link_name="stat")	_unix_stat	:: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+	@(link_name="fstat")	_unix_fstat	:: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+	@(link_name="lstat")	_unix_lstat	:: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+	@(link_name="readlink")	_unix_readlink	:: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+	@(link_name="access")	_unix_access	:: proc(path: cstring, mask: c.int) -> c.int ---
+	@(link_name="getcwd")	_unix_getcwd	:: proc(buf: cstring, len: c.size_t) -> cstring ---
+	@(link_name="chdir")	_unix_chdir	:: proc(path: cstring) -> c.int ---
+	@(link_name="rename")	_unix_rename	:: proc(old, new: cstring) -> c.int ---
+	@(link_name="unlink")	_unix_unlink	:: proc(path: cstring) -> c.int ---
+	@(link_name="rmdir")	_unix_rmdir	:: proc(path: cstring) -> c.int ---
+	@(link_name="mkdir")	_unix_mkdir	:: proc(path: cstring, mode: mode_t) -> c.int ---
+
+	@(link_name="getpagesize") _unix_getpagesize :: 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) ---
+	@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+	@(link_name="malloc")	_unix_malloc	:: proc(size: c.size_t) -> rawptr ---
+	@(link_name="calloc")	_unix_calloc	:: proc(num, size: c.size_t) -> rawptr ---
+	@(link_name="free")	_unix_free	:: proc(ptr: rawptr) ---
+	@(link_name="realloc")	_unix_realloc	:: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+	@(link_name="getenv")	_unix_getenv	:: proc(cstring) -> cstring ---
+	@(link_name="realpath")	_unix_realpath	:: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+	@(link_name="exit")	_unix_exit	:: proc(status: c.int) -> ! ---
+
+	@(link_name="dlopen")	_unix_dlopen	:: proc(filename: cstring, flags: c.int) -> rawptr ---
+	@(link_name="dlsym")	_unix_dlsym	:: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+	@(link_name="dlclose")	_unix_dlclose	:: proc(handle: rawptr) -> c.int ---
+	@(link_name="dlerror")	_unix_dlerror	:: proc() -> cstring ---
+}
+
+is_path_separator :: proc(r: rune) -> bool {
+	return r == '/'
+}
+
+get_last_error :: proc() -> int {
+	return __errno()^
+}
+
+fork :: proc() -> (Pid, Errno) {
+	pid := _unix_fork()
+	if pid == -1 {
+		return Pid(-1), Errno(get_last_error())
+	}
+	return Pid(pid), ERROR_NONE
+}
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	handle := _unix_open(cstr, c.int(flags), c.int(mode))
+	if handle == -1 {
+		return INVALID_HANDLE, Errno(get_last_error())
+	}
+	return handle, ERROR_NONE
+}
+
+close :: proc(fd: Handle) -> Errno {
+	result := _unix_close(fd)
+	if result == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+	if bytes_read == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return int(bytes_read), ERROR_NONE
+}
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	if len(data) == 0 {
+		return 0, ERROR_NONE
+	}
+	bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
+	if bytes_written == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return int(bytes_written), ERROR_NONE
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+	res := _unix_seek(fd, offset, c.int(whence))
+	if res == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return res, ERROR_NONE
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return -1, err
+	}
+	return s.size, ERROR_NONE
+}
+
+rename :: proc(old_path, new_path: string) -> Errno {
+	old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
+	new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
+	res := _unix_rename(old_path_cstr, new_path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_unlink(path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_mkdir(path_cstr, mode)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_rmdir(path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin:  Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!                                                                                
+last_write_time :: proc(fd: Handle) -> File_Time {}                                                               
+last_write_time_by_name :: proc(name: string) -> File_Time {}                                                     
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return 0, err
+	}
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
+}
+
+last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
+	s, err := _stat(name)
+	if err != ERROR_NONE {
+		return 0, err
+	}
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
+}
+
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_stat(cstr, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_lstat(cstr, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_fstat(fd, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+	dirp := _unix_fdopendir(fd)
+	if dirp == cast(Dir)nil {
+		return nil, Errno(get_last_error())
+	}
+	return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+	rc := _unix_closedir(dirp)
+	if rc != 0 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+	_unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+	result: ^Dirent
+	rc := _unix_readdir_r(dirp, &entry, &result)
+
+	if rc != 0 {
+		err = Errno(get_last_error())
+		return
+	}
+	err = ERROR_NONE
+
+	if result == nil {
+		end_of_stream = true
+		return
+	}
+
+	return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	bufsz : uint = MAX_PATH
+	buf := make([]byte, MAX_PATH)
+	for {
+		rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+		if rc == -1 {
+			delete(buf)
+			return "", Errno(get_last_error())
+		} else if rc == int(bufsz) {
+			bufsz += MAX_PATH
+			delete(buf)
+			buf = make([]byte, bufsz)
+		} else {
+			return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+		}	
+	}
+	unreachable()
+}
+
+// XXX OpenBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+	return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+	rel := rel
+	if rel == "" {
+		rel = "."
+	}
+
+	rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+	path_ptr := _unix_realpath(rel_cstr, nil)
+	if path_ptr == nil {
+		return "", Errno(get_last_error())
+	}
+	defer _unix_free(path_ptr)
+
+	path_cstr := transmute(cstring)path_ptr
+	path = strings.clone( string(path_cstr) )
+
+	return path, ERROR_NONE
+}
+
+access :: proc(path: string, mask: int) -> (bool, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_access(cstr, c.int(mask))
+	if res == -1 {
+		return false, Errno(get_last_error())
+	}
+	return true, ERROR_NONE
+}
+
+heap_alloc :: proc(size: int) -> rawptr {
+	assert(size >= 0)
+	return _unix_calloc(1, c.size_t(size))
+}
+
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+	// POSIX platforms. Ensure your caller takes this into account.
+	return _unix_realloc(ptr, c.size_t(new_size))
+}
+
+heap_free :: proc(ptr: rawptr) {
+	_unix_free(ptr)
+}
+
+getenv :: proc(name: string) -> (string, bool) {
+	path_str := strings.clone_to_cstring(name, context.temp_allocator)
+	cstr := _unix_getenv(path_str)
+	if cstr == nil {
+		return "", false
+	}
+	return string(cstr), true
+}
+
+get_current_directory :: proc() -> string {
+	buf := make([dynamic]u8, MAX_PATH)
+	for {
+		cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)))
+		if cwd != nil {
+			return string(cwd)
+		}
+		if Errno(get_last_error()) != ERANGE {
+			return ""
+		}
+		resize(&buf, len(buf) + MAX_PATH)
+	}
+	unreachable()
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_chdir(cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+	_unix_exit(c.int(code))
+}
+
+current_thread_id :: proc "contextless" () -> int {
+	return _unix_getthrid()
+}
+
+dlopen :: proc(filename: string, flags: int) -> rawptr {
+	cstr := strings.clone_to_cstring(filename, context.temp_allocator)
+	handle := _unix_dlopen(cstr, c.int(flags))
+	return handle
+}
+dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
+	assert(handle != nil)
+	cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+	proc_handle := _unix_dlsym(handle, cstr)
+	return proc_handle
+}
+dlclose :: proc(handle: rawptr) -> bool {
+	assert(handle != nil)
+	return _unix_dlclose(handle) == 0
+}
+dlerror :: proc() -> string {
+	return string(_unix_dlerror())
+}
+
+get_page_size :: proc() -> int {
+	// NOTE(tetra): The page size never changes, so why do anything complicated
+	// if we don't have to.
+	@static page_size := -1
+	if page_size != -1 {
+		return page_size
+	}
+
+	page_size = int(_unix_getpagesize())
+	return page_size
+}
+
+
+_alloc_command_line_arguments :: proc() -> []string {
+	res := make([]string, len(runtime.args__))
+	for arg, i in runtime.args__ {
+		res[i] = string(arg)
+	}
+	return res
+}

+ 1 - 1
core/os/stat_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package os
 
 import "core:time"

+ 3 - 3
core/os/stream.odin

@@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{
 		return
 	},
 	impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+		when ODIN_OS == .Windows || ODIN_OS == .WASI {
 			fd := Handle(uintptr(s.stream_data))
 			os_err: Errno
 			n, os_err = read_at(fd, p, offset)
@@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{
 		return
 	},
 	impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+		when ODIN_OS == .Windows || ODIN_OS == .WASI {
 			fd := Handle(uintptr(s.stream_data))
 			os_err: Errno
 			n, os_err = write_at(fd, p, offset)
@@ -53,7 +53,7 @@ _file_stream_vtable := &io.Stream_VTable{
 		return sz
 	},
 	impl_flush = proc(s: io.Stream) -> io.Error {
-		when ODIN_OS == "windows" {
+		when ODIN_OS == .Windows {
 			fd := Handle(uintptr(s.stream_data))
 			flush(fd)
 		} else {

+ 6 - 7
core/path/filepath/match.odin

@@ -89,7 +89,7 @@ scan_chunk :: proc(pattern: string) -> (star: bool, chunk, rest: string) {
 	scan_loop: for i = 0; i < len(pattern); i += 1 {
 		switch pattern[i] {
 		case '\\':
-			when ODIN_OS != "windows" {
+			when ODIN_OS != .Windows {
 				if i+1 < len(pattern) {
 					i += 1
 				}
@@ -161,7 +161,7 @@ match_chunk :: proc(chunk, s: string) -> (rest: string, ok: bool, err: Match_Err
 			chunk = chunk[1:]
 
 		case '\\':
-			when ODIN_OS != "windows" {
+			when ODIN_OS != .Windows {
 				chunk = chunk[1:]
 				if len(chunk) == 0 {
 					err = .Syntax_Error
@@ -188,7 +188,7 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
 		return
 	}
 	chunk := chunk
-	if chunk[0] == '\\' && ODIN_OS != "windows" {
+	if chunk[0] == '\\' && ODIN_OS != .Windows {
 		chunk = chunk[1:]
 		if len(chunk) == 0 {
 			err = .Syntax_Error
@@ -227,11 +227,10 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
 		return m[:], .None
 	}
 
-	temp_buf: [8]byte
-
 	dir, file := split(pattern)
 	volume_len := 0
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
+		temp_buf: [8]byte
 		volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
 	} else {
 		dir = clean_glob_path(dir)
@@ -308,7 +307,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
 
 @(private)
 has_meta :: proc(path: string) -> bool {
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
 		CHARS :: `*?[`
 	} else {
 		CHARS :: `*?[\`

+ 5 - 4
core/path/filepath/path.odin

@@ -8,7 +8,7 @@ import "core:strings"
 is_separator :: proc(c: byte) -> bool {
 	switch c {
 	case '/':  return true
-	case '\\': return ODIN_OS == "windows"
+	case '\\': return ODIN_OS == .Windows
 	}
 	return false
 }
@@ -32,7 +32,7 @@ volume_name :: proc(path: string) -> string {
 }
 
 volume_name_len :: proc(path: string) -> int {
-	if ODIN_OS == "windows" {
+	if ODIN_OS == .Windows {
 		if len(path) < 2 {
 			return 0
 		}
@@ -284,13 +284,14 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
 }
 
 dir :: proc(path: string, allocator := context.allocator) -> string {
+        context.allocator = allocator
 	vol := volume_name(path)
 	i := len(path) - 1
 	for i >= len(vol) && !is_separator(path[i]) {
 		i -= 1
 	}
-	dir := clean(path[len(vol) : i+1], allocator)
-	defer delete(dir, allocator)
+	dir := clean(path[len(vol) : i+1])
+	defer delete(dir)
 	if dir == "." && len(vol) > 2 {
 		return strings.clone(vol)
 	}

+ 8 - 3
core/path/filepath/path_unix.odin

@@ -1,7 +1,7 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package filepath
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	foreign import libc "System.framework"
 } else {
 	foreign import libc "system:c"
@@ -54,11 +54,16 @@ foreign libc {
 	@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
 
 }
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	@(private)
 	foreign libc {
 		@(link_name="__error")          __error :: proc() -> ^i32 ---
 	}
+} else when ODIN_OS == .OpenBSD {
+	@(private)
+	foreign libc {
+		@(link_name="__errno")		__error :: proc() -> ^i32 ---
+	}
 } else {
 	@(private)
 	foreign libc {

+ 4 - 4
core/reflect/types.odin

@@ -334,11 +334,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
 
 
 
-write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
-	write_type(buf, type_info_of(id))
+write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+	return write_type_writer(strings.to_writer(buf), type_info_of(id))
 }
-write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
-	write_type(writer, type_info_of(id))
+write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+	return write_type_writer(writer, type_info_of(id), n_written)
 }
 
 write_typeid :: proc{

+ 35 - 2
core/runtime/core.odin

@@ -33,6 +33,11 @@ Calling_Convention :: enum u8 {
 
 	None        = 6,
 	Naked       = 7,
+
+	_           = 8, // reserved
+
+	Win64       = 9,
+	SysV        = 10,
 }
 
 Type_Info_Enum_Value :: distinct i64
@@ -346,7 +351,6 @@ Context :: struct {
 	assertion_failure_proc: Assertion_Failure_Proc,
 	logger:                 Logger,
 
-	user_data:  any,
 	user_ptr:   rawptr,
 	user_index: int,
 
@@ -387,6 +391,35 @@ Raw_Cstring :: struct {
 }
 
 
+/*
+	// Defined internally by the compiler
+	Odin_OS_Type :: enum int {
+		Unknown,
+		Windows,
+		Darwin,
+		Linux,
+		Essence,
+		FreeBSD,
+		WASI,
+		JS,
+		Freestanding,
+	}
+*/
+Odin_OS_Type :: type_of(ODIN_OS)
+
+/*
+	// Defined internally by the compiler
+	Odin_Arch_Type :: enum int {
+		Unknown,
+		amd64,
+		i386,
+		arm64,
+		wasm32,
+		wasm64,
+	}
+*/
+Odin_Arch_Type :: type_of(ODIN_ARCH)
+
 /*
 	// Defined internally by the compiler
 	Odin_Build_Mode_Type :: enum int {
@@ -540,7 +573,7 @@ __init_context :: proc "contextless" (c: ^Context) {
 }
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
-	when ODIN_OS == "freestanding" {
+	when ODIN_OS == .Freestanding {
 		// Do nothing
 	} else {
 		print_caller_location(loc)

+ 1 - 1
core/runtime/default_allocators_nil.odin

@@ -32,7 +32,7 @@ nil_allocator :: proc() -> Allocator {
 
 
 
-when ODIN_OS == "freestanding" {
+when ODIN_OS == .Freestanding {
 	default_allocator_proc :: nil_allocator_proc
 	default_allocator :: nil_allocator
 } 

+ 1 - 1
core/runtime/default_temporary_allocator.odin

@@ -3,7 +3,7 @@ package runtime
 DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
 
 
-when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 	Default_Temp_Allocator :: struct {}
 	
 	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}

+ 1 - 1
core/runtime/entry_unix.odin

@@ -1,5 +1,5 @@
 //+private
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package runtime
 
 import "core:intrinsics"

+ 1 - 1
core/runtime/entry_windows.odin

@@ -22,7 +22,7 @@ when ODIN_BUILD_MODE == .Dynamic {
 		return true
 	}
 } else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
-	when ODIN_ARCH == "i386" || ODIN_NO_CRT {
+	when ODIN_ARCH == .i386 || ODIN_NO_CRT {
 		@(link_name="mainCRTStartup", linkage="strong", require)
 		mainCRTStartup :: proc "stdcall" () -> i32 {
 			context = default_context()

+ 9 - 9
core/runtime/error_checks.odin

@@ -1,7 +1,7 @@
 package runtime
 
 bounds_trap :: proc "contextless" () -> ! {
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
 		windows_trap_array_bounds()
 	} else {
 		trap()
@@ -9,7 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
 }
 
 type_assertion_trap :: proc "contextless" () -> ! {
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
 		windows_trap_type_assertion()
 	} else {
 		trap()
@@ -25,7 +25,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
 		print_caller_location(Source_Code_Location{file, line, column, ""})
 		print_string(" Index ")
 		print_i64(i64(index))
-		print_string(" is out of bounds range 0:")
+		print_string(" is out of range 0..<")
 		print_i64(i64(count))
 		print_byte('\n')
 		bounds_trap()
@@ -35,11 +35,11 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
 
 slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
 	print_caller_location(Source_Code_Location{file, line, column, ""})
-	print_string(" Invalid slice indices: ")
+	print_string(" Invalid slice indices ")
 	print_i64(i64(lo))
 	print_string(":")
 	print_i64(i64(hi))
-	print_string(":")
+	print_string(" is out of range 0..<")
 	print_i64(i64(len))
 	print_byte('\n')
 	bounds_trap()
@@ -47,7 +47,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
 
 multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
 	print_caller_location(Source_Code_Location{file, line, column, ""})
-	print_string(" Invalid slice indices: ")
+	print_string(" Invalid slice indices ")
 	print_i64(i64(lo))
 	print_string(":")
 	print_i64(i64(hi))
@@ -83,11 +83,11 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
 	}
 	handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
 		print_caller_location(Source_Code_Location{file, line, column, ""})
-		print_string(" Invalid dynamic array values: ")
+		print_string(" Invalid dynamic array indices ")
 		print_i64(i64(low))
 		print_string(":")
 		print_i64(i64(high))
-		print_string(":")
+		print_string(" is out of range 0..<")
 		print_i64(i64(max))
 		print_byte('\n')
 		bounds_trap()
@@ -107,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
 		print_i64(i64(row_index))
 		print_string(", ")
 		print_i64(i64(column_index))
-		print_string(" is out of bounds range [0..<")
+		print_string(" is out of range [0..<")
 		print_i64(i64(row_count))
 		print_string(", 0..<")
 		print_i64(i64(column_count))

+ 1 - 1
core/runtime/internal.odin

@@ -3,7 +3,7 @@ package runtime
 import "core:intrinsics"
 
 @(private="file")
-IS_WASM :: ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64"
+IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64
 
 @(private)
 RUNTIME_LINKAGE :: "strong" when (

+ 2 - 2
core/runtime/procs.odin

@@ -1,6 +1,6 @@
 package runtime
 
-when ODIN_NO_CRT && ODIN_OS == "windows" {
+when ODIN_NO_CRT && ODIN_OS == .Windows {
 	foreign import lib "system:NtDll.lib"
 	
 	@(private="file")
@@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
 		RtlMoveMemory(dst, src, len)
 		return dst
 	}
-} else when ODIN_NO_CRT || (ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64") {
+} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64) {
 	@(link_name="memset", linkage="strong", require)
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 		if ptr != nil && len != 0 {

+ 21 - 0
core/runtime/procs_darwin.odin

@@ -0,0 +1,21 @@
+//+private
+package runtime
+
+foreign import "system:Foundation.framework"
+
+import "core:intrinsics"
+
+objc_id :: ^intrinsics.objc_object
+objc_Class :: ^intrinsics.objc_class
+objc_SEL :: ^intrinsics.objc_selector
+
+foreign Foundation {
+	objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
+	sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
+	objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
+
+	objc_msgSend        :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+	objc_msgSend_fpret  :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
+	objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
+	objc_msgSend_stret  :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+}

+ 1 - 0
core/strconv/strconv.odin

@@ -895,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
 	if s == `""` {
 		return "", false, true
 	}
+	s = s[1:len(s)-1]
 
 	if contains_rune(s, '\n') >= 0 {
 		return s, false, false

+ 31 - 47
core/strings/strings.odin

@@ -280,10 +280,29 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
 	return res[:i+1]
 }
 
+/*
+	Splits a string into parts, based on a separator.
+	Returned strings are substrings of 's'.
+	```
+	s := "aaa.bbb.ccc.ddd.eee" // 5 parts
+	ss := split(s, ".")
+	fmt.println(ss)            // [aaa, bbb, ccc, ddd, eee]
+	```
+*/
 split :: proc(s, sep: string, allocator := context.allocator) -> []string {
 	return _split(s, sep, 0, -1, allocator)
 }
 
+/*
+	Splits a string into a total of 'n' parts, based on a separator.
+	Returns fewer parts if there wasn't enough occurrences of the separator.
+	Returned strings are substrings of 's'.
+	```
+	s := "aaa.bbb.ccc.ddd.eee" // 5 parts present
+	ss := split_n(s, ".", 3)   // total of 3 wanted
+	fmt.println(ss)            // [aaa, bbb, ccc.ddd.eee]
+	```
+*/
 split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
 	return _split(s, sep, 0, n, allocator)
 }
@@ -298,13 +317,7 @@ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) ->
 
 
 @private
-_split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: string, ok: bool) {
-	s, n := s, n
-
-	if n == 0 {
-		return
-	}
-
+_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
 	if sep == "" {
 		res = s[:]
 		ok = true
@@ -312,44 +325,27 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: stri
 		return
 	}
 
-	if n < 0 {
-		n = count(s^, sep) + 1
-	}
-
-	n -= 1
-
-	i := 0
-	for ; i < n; i += 1 {
-		m := index(s^, sep)
-		if m < 0 {
-			break
-		}
+	m := index(s^, sep)
+	if m < 0 {
+		// not found
+		res = s[:]
+		ok = res != ""
+		s^ = s[len(s):]
+	} else {
 		res = s[:m+sep_save]
 		ok = true
 		s^ = s[m+len(sep):]
-		return
 	}
-	res = s[:]
-	ok = res != ""
-	s^ = s[len(s):]
 	return
 }
 
 
 split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
-	return _split_iterator(s, sep, 0, -1)
-}
-
-split_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
-	return _split_iterator(s, sep, 0, n)
+	return _split_iterator(s, sep, 0)
 }
 
 split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
-	return _split_iterator(s, sep, len(sep), -1)
-}
-
-split_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
-	return _split_iterator(s, sep, len(sep), n)
+	return _split_iterator(s, sep, len(sep))
 }
 
 
@@ -402,25 +398,13 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
 
 split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
 	sep :: "\n"
-	line = _split_iterator(s, sep, 0, -1) or_return
-	return _trim_cr(line), true
-}
-
-split_lines_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) {
-	sep :: "\n"
-	line = _split_iterator(s, sep, 0, n) or_return
+	line = _split_iterator(s, sep, 0) or_return
 	return _trim_cr(line), true
 }
 
 split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
 	sep :: "\n"
-	line = _split_iterator(s, sep, len(sep), -1) or_return
-	return _trim_cr(line), true
-}
-
-split_lines_after_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) {
-	sep :: "\n"
-	line = _split_iterator(s, sep, len(sep), n) or_return
+	line = _split_iterator(s, sep, len(sep)) or_return
 	return _trim_cr(line), true
 }
 

+ 1 - 1
core/sync/channel_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package sync
 
 import "core:time"

+ 78 - 0
core/sync/sync2/futex_openbsd.odin

@@ -0,0 +1,78 @@
+//+private
+//+build openbsd
+package sync2
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+FUTEX_WAIT :: 1
+FUTEX_WAKE :: 2
+
+FUTEX_PRIVATE_FLAG :: 128
+
+FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
+FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+
+foreign import libc "system:c"
+
+foreign libc {
+	@(link_name="futex")
+	_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+	res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
+
+	if res != -1 {
+		return true
+	}
+
+	if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+		return false
+	}
+
+	panic("futex_wait failure")
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+	if duration <= 0 {
+		return false
+	}
+
+	timespec_t :: struct {
+		tv_sec:  c.long,
+		tv_nsec: c.long,
+        }
+
+	res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, &timespec_t{
+		tv_sec  = (c.long)(duration/1e9),
+		tv_nsec = (c.long)(duration%1e9),
+	})
+
+	if res != -1 {
+		return true
+	}
+
+	if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+		return false
+	}
+
+	panic("futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
+
+	if res == -1 {
+		panic("futex_wake_single failure")
+	}
+}
+
+_futex_broadcast :: proc(f: ^Futex)  {
+	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
+
+	if res == -1 {
+		panic("_futex_wake_all failure")
+	}
+}

+ 1 - 1
core/sync/sync2/primitives.odin

@@ -11,7 +11,7 @@ current_thread_id :: proc "contextless" () -> int {
 //
 // A Mutex must not be copied after first use
 Mutex :: struct {
-	impl: _Mutex,
+	impl: _Mutex `This is a tag`,
 }
 
 // mutex_lock locks m

+ 1 - 1
core/sync/sync2/primitives_internal.odin

@@ -93,7 +93,7 @@ when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
 }
 
 
-when ODIN_OS != "windows" {
+when ODIN_OS != .Windows {
 	RW_Mutex_State :: distinct uint
 	RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2
 	RW_Mutex_State_Is_Writing :: RW_Mutex_State(1)

+ 9 - 0
core/sync/sync2/primitives_openbsd.odin

@@ -0,0 +1,9 @@
+//+build openbsd
+//+private
+package sync2
+
+import "core:os"
+
+_current_thread_id :: proc "contextless" () -> int {
+	return os.current_thread_id()
+}

+ 1 - 1
core/sync/sync2/primitives_pthreads.odin

@@ -1,4 +1,4 @@
-//+build linux, freebsd
+//+build linux, freebsd, openbsd
 //+private
 package sync2
 

+ 36 - 0
core/sync/sync_openbsd.odin

@@ -0,0 +1,36 @@
+package sync
+
+import "core:sys/unix"
+import "core:os"
+
+current_thread_id :: proc "contextless" () -> int {
+	return os.current_thread_id()
+}
+
+// The Darwin docs say it best:
+// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
+// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens, 
+// but when there are none left, a thread must wait until another thread returns one.
+Semaphore :: struct #align 16 {
+	handle: unix.sem_t,
+}
+
+semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
+	assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
+}
+
+semaphore_destroy :: proc(s: ^Semaphore) {
+	assert(unix.sem_destroy(&s.handle) == 0)
+	s.handle = {}
+}
+
+semaphore_post :: proc(s: ^Semaphore, count := 1) {
+    // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
+    for in 0..<count {
+	    assert(unix.sem_post(&s.handle) == 0)
+    }
+}
+
+semaphore_wait_for :: proc(s: ^Semaphore) {
+	assert(unix.sem_wait(&s.handle) == 0)
+}

+ 1 - 1
core/sync/sync_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package sync
 
 import "core:sys/unix"

+ 168 - 0
core/sys/darwin/xnu_system_call_helpers.odin

@@ -0,0 +1,168 @@
+package darwin
+
+import "core:strings"
+import "core:c"
+
+// this package uses the sys prefix for the proc names to indicate that these aren't native syscalls but directly call such
+sys_write_string ::  proc (fd: c.int, message: string) -> bool {
+	return syscall_write(fd, strings.ptr_from_string(message), cast(u64)len(message))
+}
+
+Offset_From :: enum c.int {
+	SEEK_SET  = 0,  // the offset is set to offset bytes.
+	SEEK_CUR  = 1,  // the offset is set to its current location plus offset bytes.
+   	SEEK_END  = 2,  // the offset is set to the size of the file plus offset bytes.
+   	SEEK_HOLE = 3,  //  the offset is set to the start of the next hole greater than or equal to the supplied offset.
+ 	SEEK_DATA = 4,  //  the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
+}
+
+Open_Flags_Enum :: enum u8 {
+	RDONLY, /* open for reading only */
+	WRONLY, /* open for writing only */
+	RDWR, /* open for reading and writing */
+
+	NONBLOCK, /* no delay */
+	APPEND, /* set append mode */
+	CREAT, /* create if nonexistant */
+	TRUNC, /* truncate to zero length */
+	EXCL, /* error if already exists */
+	SHLOCK, /* open with shared file lock */
+	EXLOCK, /* open with exclusive file lock */
+	DIRECTORY, /* restrict open to only directories */
+	NOFOLLOW, /* don't follow symlinks */
+	SYMLINK, /* allow open of a symlink */
+	EVTONLY, /* descriptor requested for event notifications only */
+	CLOEXEC, /* causes the descriptor to be closed if you use any of the exec like functions */
+	NOFOLLOW_ANY, /* no symlinks allowed in path */
+}
+Open_Flags :: bit_set[Open_Flags_Enum; u16]
+
+Permission_Enum :: enum u8 {
+	/* For owner */
+	PERMISSION_OWNER_READ, /* R for owner */
+	PERMISSION_OWNER_WRITE, /* W for owner */
+	PERMISSION_OWNER_EXECUTE, /* X for owner */
+	//IRWXU, /* RWX mask for owner */
+	
+	/* For group */
+	PERMISSION_GROUP_READ, /* R for group */
+	PERMISSION_GROUP_WRITE, /* W for group */
+	PERMISSION_GROUP_EXECUTE, /* X for group */
+	//IRWXG, /* RWX mask for group */
+	
+	/* For other */
+	PERMISSION_OTHER_READ, /* R for other */
+	PERMISSION_OTHER_WRITE, /* W for other */
+	PERMISSION_OTHER_EXECUTE, /* X for other */
+	//IRWXO, /* RWX mask for other */
+	
+	/* Special */
+	PERMISSION_SET_USER_ON_EXECUTION, /* set user id on execution */
+	PERMISSION_SET_GROUP_ON_EXECUTION, /* set group id on execution */
+
+	/* ?? */
+	PERMISSION_ISVTX, /* save swapped text even after use */
+}
+Permission :: bit_set[Permission_Enum; u16]
+
+PERMISSION_NONE_NONE :: Permission{}
+PERMISSION_OWNER_ALL :: Permission{.PERMISSION_OWNER_READ, .PERMISSION_OWNER_WRITE, .PERMISSION_OWNER_EXECUTE}
+PERMISSION_GROUP_ALL :: Permission{.PERMISSION_GROUP_READ, .PERMISSION_GROUP_WRITE, .PERMISSION_GROUP_EXECUTE}
+PERMISSION_OTHER_ALL :: Permission{.PERMISSION_OTHER_READ, .PERMISSION_OTHER_WRITE, .PERMISSION_OTHER_EXECUTE}
+PERMISSION_ALL_ALL   :: PERMISSION_OWNER_ALL | PERMISSION_GROUP_ALL | PERMISSION_OTHER_ALL
+
+_sys_permission_mode :: #force_inline proc (mode: Permission) -> u32 {
+	cflags: u32 = 0
+
+	cflags |= PERMISSION_MASK_IRUSR * u32(Permission.PERMISSION_OWNER_READ in mode)
+	cflags |= PERMISSION_MASK_IWUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+	cflags |= PERMISSION_MASK_IXUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+	cflags |= PERMISSION_MASK_IRGRP * u32(Permission.PERMISSION_GROUP_READ in mode)
+	cflags |= PERMISSION_MASK_IWGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+	cflags |= PERMISSION_MASK_IXGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+	cflags |= PERMISSION_MASK_IROTH * u32(Permission.PERMISSION_OTHER_READ in mode)
+	cflags |= PERMISSION_MASK_IWOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+	cflags |= PERMISSION_MASK_IXOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+
+	return cflags
+}
+
+sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
+	
+	cmode: u32 = 0
+	cflags: u32 = 0
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+
+	cflags = _sys_permission_mode(mode)
+
+	cmode |= OPEN_FLAG_RDONLY       * u32(Open_Flags.RDONLY in oflag)
+	cmode |= OPEN_FLAG_WRONLY       * u32(Open_Flags.WRONLY in oflag)
+	cmode |= OPEN_FLAG_RDWR         * u32(Open_Flags.RDWR in oflag)
+	cmode |= OPEN_FLAG_NONBLOCK     * u32(Open_Flags.NONBLOCK in oflag)
+	cmode |= OPEN_FLAG_CREAT        * u32(Open_Flags.CREAT in oflag)
+	cmode |= OPEN_FLAG_APPEND       * u32(Open_Flags.APPEND in oflag)
+	cmode |= OPEN_FLAG_TRUNC        * u32(Open_Flags.TRUNC in oflag)
+	cmode |= OPEN_FLAG_EXCL         * u32(Open_Flags.EXCL in oflag)
+	cmode |= OPEN_FLAG_SHLOCK       * u32(Open_Flags.SHLOCK in oflag)
+	cmode |= OPEN_FLAG_EXLOCK       * u32(Open_Flags.EXLOCK in oflag)
+	cmode |= OPEN_FLAG_DIRECTORY    * u32(Open_Flags.DIRECTORY in oflag)
+	cmode |= OPEN_FLAG_NOFOLLOW     * u32(Open_Flags.NOFOLLOW in oflag)
+	cmode |= OPEN_FLAG_SYMLINK      * u32(Open_Flags.SYMLINK in oflag)
+	cmode |= OPEN_FLAG_EVTONLY      * u32(Open_Flags.EVTONLY in oflag)
+	cmode |= OPEN_FLAG_CLOEXEC      * u32(Open_Flags.CLOEXEC in oflag)
+	cmode |= OPEN_FLAG_NOFOLLOW_ANY * u32(Open_Flags.NOFOLLOW_ANY in oflag)
+	
+	result := syscall_open(cpath, cmode, cflags)
+	state  := result != -1
+
+	if state && cflags != 0 {
+		state = (syscall_fchmod(result, cflags) != -1)
+	}
+
+	return result * cast(c.int)state, state
+}
+
+sys_mkdir :: proc(path: string, mode: Permission) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cflags := _sys_permission_mode(mode)
+	return syscall_mkdir(cpath, cflags) != -1
+}
+
+sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cflags := _sys_permission_mode(mode)
+	return syscall_mkdir_at(fd, cpath, cflags) != -1
+}
+
+sys_rmdir :: proc(path: string, mode: Permission) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cflags := _sys_permission_mode(mode)
+	return syscall_rmdir(cpath, cflags) != -1
+}
+
+sys_rename :: proc(path: string, new_path: string) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
+	return syscall_rename(cpath, cnpath) != -1
+}
+
+sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
+	return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
+}
+
+sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
+	return syscall_lseek(fd, offset, cast(c.int)whence)
+}
+
+sys_chmod :: proc(path: string, mode: Permission) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	cmode := _sys_permission_mode(mode)
+	return syscall_chmod(cpath, cmode) != -1
+}
+
+sys_lstat :: proc(path: string, status: ^stat) -> bool {
+	cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+	return syscall_lstat(cpath, status) != -1
+}

+ 558 - 0
core/sys/darwin/xnu_system_call_numbers.odin

@@ -0,0 +1,558 @@
+package darwin
+
+unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
+    return uintptr(number) + uintptr(0x2000000)
+}
+
+System_Call_Number :: enum uintptr {
+    /* 0 syscall */
+    exit = 1,
+    fork = 2,
+    read = 3,
+    write = 4,
+    open = 5,
+    close = 6,
+    wait4 = 7,
+    /* 8  old creat */
+    link = 9,
+    unlink = 10,
+    /* 11  old execv */
+    chdir = 12,
+    fchdir = 13,
+    mknod = 14,
+    chmod = 15,
+    chown = 16,
+    /* 17  old break */
+    getfsstat = 18,
+    /* 19  old lseek */
+    getpid = 20,
+    /* 21  old mount */
+    /* 22  old umount */
+    setuid = 23,
+    getuid = 24,
+    geteuid = 25,
+    ptrace = 26,
+    recvmsg = 27,
+    sendmsg = 28,
+    recvfrom = 29,
+    accept = 30,
+    getpeername = 31,
+    getsockname = 32,
+    access = 33,
+    chflags = 34,
+    fchflags = 35,
+    sync = 36,
+    kill = 37,
+    /* 38  old stat */
+    getppid = 39,
+    /* 40  old lstat */
+    dup = 41,
+    pipe = 42,
+    getegid = 43,
+    /* 44  old profil */
+    /* 45  old ktrace */
+    sigaction = 46,
+    getgid = 47,
+    sigprocmask = 48,
+    getlogin = 49,
+    setlogin = 50,
+    acct = 51,
+    sigpending = 52,
+    sigaltstack = 53,
+    ioctl = 54,
+    reboot = 55,
+    revoke = 56,
+    symlink = 57,
+    readlink = 58,
+    execve = 59,
+    umask = 60,
+    chroot = 61,
+    /* 62  old fstat */
+    /* 63  used internally and reserved */
+    /* getpagesize = 64, invalid */
+    msync = 65,
+    vfork = 66,
+    /* 67  old vread */
+    /* 68  old vwrite */
+    /* 69  old sbrk */
+    /* 70  old sstk */
+    /* 71  old mmap */
+    /* 72  old vadvise */
+    munmap = 73,
+    mprotect = 74,
+    madvise = 75,
+    /* 76  old vhangup */
+    /* 77  old vlimit */
+    mincore = 78,
+    getgroups = 79,
+    setgroups = 80,
+    getpgrp = 81,
+    setpgid = 82,
+    setitimer = 83,
+    /* 84  old wait */
+    swapon = 85,
+    getitimer = 86,
+    /* 87  old gethostname */
+    /* 88  old sethostname */
+    getdtablesize = 89,
+    dup2 = 90,
+    /* 91  old getdopt */
+    fcntl = 92,
+    select = 93,
+    /* 94  old setdopt */
+    fsync = 95,
+    setpriority = 96,
+    socket = 97,
+    connect = 98,
+    /* 99  old accept */
+    getpriority = 100,
+    /* 101  old send */
+    /* 102  old recv */
+    /* 103  old sigreturn */
+    bind = 104,
+    setsockopt = 105,
+    listen = 106,
+    /* 107  old vtimes */
+    /* 108  old sigvec */
+    /* 109  old sigblock */
+    /* 110  old sigsetmask */
+    sigsuspend = 111,
+    /* 112  old sigstack */
+    /* 113  old recvmsg */
+    /* 114  old sendmsg */
+    /* 115  old vtrace */
+    gettimeofday = 116,
+    getrusage = 117,
+    getsockopt = 118,
+    /* 119  old resuba */
+    readv = 120,
+    writev = 121,
+    settimeofday = 122,
+    fchown = 123,
+    fchmod = 124,
+    /* 125  old recvfrom */
+    setreuid = 126,
+    setregid = 127,
+    rename = 128,
+    /* 129  old truncate */
+    /* 130  old ftruncate */
+    flock = 131,
+    mkfifo = 132,
+    sendto = 133,
+    shutdown = 134,
+    socketpair = 135,
+    mkdir = 136,
+    rmdir = 137,
+    utimes = 138,
+    futimes = 139,
+    adjtime = 140,
+    /* 141  old getpeername */
+    gethostuuid = 142,
+    /* 143  old sethostid */
+    /* 144  old getrlimit */
+    /* 145  old setrlimit */
+    /* 146  old killpg */
+    setsid = 147,
+    /* 148  old setquota */
+    /* 149  old qquota */
+    /* 150  old getsockname */
+    getpgid = 151,
+    setprivexec = 152,
+    pread = 153,
+    pwrite = 154,
+    nfssvc = 155,
+    /* 156  old getdirentries */
+    statfs = 157,
+    fstatfs = 158,
+    unmount = 159,
+    /* 160  old async_daemon */
+    getfh = 161,
+    /* 162  old getdomainname */
+    /* 163  old setdomainname */
+    /* 164  */
+    quotactl = 165,
+    /* 166  old exportfs */
+    mount = 167,
+    /* 168  old ustat */
+    csops = 169,
+    csops_audittoken = 170,
+    /* 171  old wait3 */
+    /* 172  old rpause */
+    waitid = 173,
+    /* 174  old getdents */
+    /* 175  old gc_control */
+    /* 176  old add_profil */
+    kdebug_typefilter = 177,
+    kdebug_trace_string = 178,
+    kdebug_trace64 = 179,
+    kdebug_trace = 180,
+    setgid = 181,
+    setegid = 182,
+    seteuid = 183,
+    sigreturn = 184,
+    /* 185  old chud */
+    thread_selfcounts = 186,
+    fdatasync = 187,
+    stat = 188,
+    fstat = 189,
+    lstat = 190,
+    pathconf = 191,
+    fpathconf = 192,
+    /* 193  old getfsstat */
+    getrlimit = 194,
+    setrlimit = 195,
+    getdirentries = 196,
+    mmap = 197,
+    /* 198  old __syscall */
+    lseek = 199,
+    truncate = 200,
+    ftruncate = 201,
+    sysctl = 202,
+    mlock = 203,
+    munlock = 204,
+    undelete = 205,
+    /* 206  old ATsocket */
+    /* 207  old ATgetmsg */
+    /* 208  old ATputmsg */
+    /* 209  old ATsndreq */
+    /* 210  old ATsndrsp */
+    /* 211  old ATgetreq */
+    /* 212  old ATgetrsp */
+    /* 213  Reserved for AppleTalk */
+    /* 214  */
+    /* 215  */
+    open_dprotected_np = 216,
+    fsgetpath_ext = 217,
+    /* 218  old lstatv */
+    /* 219  old fstatv */
+    getattrlist = 220,
+    setattrlist = 221,
+    getdirentriesattr = 222,
+    exchangedata = 223,
+    /* 224  old checkuseraccess or fsgetpath */
+    searchfs = 225,
+    delete = 226,
+    copyfile = 227,
+    fgetattrlist = 228,
+    fsetattrlist = 229,
+    poll = 230,
+    /* 231  old watchevent */
+    /* 232  old waitevent */
+    /* 233  old modwatch */
+    getxattr = 234,
+    fgetxattr = 235,
+    setxattr = 236,
+    fsetxattr = 237,
+    removexattr = 238,
+    fremovexattr = 239,
+    listxattr = 240,
+    flistxattr = 241,
+    fsctl = 242,
+    initgroups = 243,
+    posix_spawn = 244,
+    ffsctl = 245,
+    /* 246  */
+    nfsclnt = 247,
+    fhopen = 248,
+    /* 249  */
+    minherit = 250,
+    semsys = 251,
+    msgsys = 252,
+    shmsys = 253,
+    semctl = 254,
+    semget = 255,
+    semop = 256,
+    /* 257  old semconfig */
+    msgctl = 258,
+    msgget = 259,
+    msgsnd = 260,
+    msgrcv = 261,
+    shmat = 262,
+    shmctl = 263,
+    shmdt = 264,
+    shmget = 265,
+    shm_open = 266,
+    shm_unlink = 267,
+    sem_open = 268,
+    sem_close = 269,
+    sem_unlink = 270,
+    sem_wait = 271,
+    sem_trywait = 272,
+    sem_post = 273,
+    sysctlbyname = 274,
+    /* 275  old sem_init */
+    /* 276  old sem_destroy */
+    open_extended = 277,
+    umask_extended = 278,
+    stat_extended = 279,
+    lstat_extended = 280,
+    fstat_extended = 281,
+    chmod_extended = 282,
+    fchmod_extended = 283,
+    access_extended = 284,
+    settid = 285,
+    gettid = 286,
+    setsgroups = 287,
+    getsgroups = 288,
+    setwgroups = 289,
+    getwgroups = 290,
+    mkfifo_extended = 291,
+    mkdir_extended = 292,
+    identitysvc = 293,
+    shared_region_check_np = 294,
+    /* 295  old shared_region_map_np */
+    vm_pressure_monitor = 296,
+    psynch_rw_longrdlock = 297,
+    psynch_rw_yieldwrlock = 298,
+    psynch_rw_downgrade = 299,
+    psynch_rw_upgrade = 300,
+    psynch_mutexwait = 301,
+    psynch_mutexdrop = 302,
+    psynch_cvbroad = 303,
+    psynch_cvsignal = 304,
+    psynch_cvwait = 305,
+    psynch_rw_rdlock = 306,
+    psynch_rw_wrlock = 307,
+    psynch_rw_unlock = 308,
+    psynch_rw_unlock2 = 309,
+    getsid = 310,
+    settid_with_pid = 311,
+    psynch_cvclrprepost = 312,
+    aio_fsync = 313,
+    aio_return = 314,
+    aio_suspend = 315,
+    aio_cancel = 316,
+    aio_error = 317,
+    aio_read = 318,
+    aio_write = 319,
+    lio_listio = 320,
+    /* 321  old __pthread_cond_wait */
+    iopolicysys = 322,
+    process_policy = 323,
+    mlockall = 324,
+    munlockall = 325,
+    /* 326  */
+    issetugid = 327,
+    __pthread_kill = 328,
+    __pthread_sigmask = 329,
+    __sigwait = 330,
+    __disable_threadsignal = 331,
+    __pthread_markcancel = 332,
+    __pthread_canceled = 333,
+    __semwait_signal = 334,
+    /* 335  old utrace */
+    proc_info = 336,
+    sendfile = 337,
+    stat64 = 338,
+    fstat64 = 339,
+    lstat64 = 340,
+    stat64_extended = 341,
+    lstat64_extended = 342,
+    fstat64_extended = 343,
+    getdirentries64 = 344,
+    statfs64 = 345,
+    fstatfs64 = 346,
+    getfsstat64 = 347,
+    __pthread_chdir = 348,
+    __pthread_fchdir = 349,
+    audit = 350,
+    auditon = 351,
+    /* 352  */
+    getauid = 353,
+    setauid = 354,
+    /* 355  old getaudit */
+    /* 356  old setaudit */
+    getaudit_addr = 357,
+    setaudit_addr = 358,
+    auditctl = 359,
+    bsdthread_create = 360,
+    bsdthread_terminate = 361,
+    kqueue = 362,
+    kevent = 363,
+    lchown = 364,
+    /* 365  old stack_snapshot */
+    bsdthread_register = 366,
+    workq_open = 367,
+    workq_kernreturn = 368,
+    kevent64 = 369,
+    __old_semwait_signal = 370,
+    __old_semwait_signal_nocancel = 371,
+    thread_selfid = 372,
+    ledger = 373,
+    kevent_qos = 374,
+    kevent_id = 375,
+    /* 376  */
+    /* 377  */
+    /* 378  */
+    /* 379  */
+    __mac_execve = 380,
+    __mac_syscall = 381,
+    __mac_get_file = 382,
+    __mac_set_file = 383,
+    __mac_get_link = 384,
+    __mac_set_link = 385,
+    __mac_get_proc = 386,
+    __mac_set_proc = 387,
+    __mac_get_fd = 388,
+    __mac_set_fd = 389,
+    __mac_get_pid = 390,
+    /* 391  */
+    /* 392  */
+    /* 393  */
+    pselect = 394,
+    pselect_nocancel = 395,
+    read_nocancel = 396,
+    write_nocancel = 397,
+    open_nocancel = 398,
+    close_nocancel = 399,
+    wait4_nocancel = 400,
+    recvmsg_nocancel = 401,
+    sendmsg_nocancel = 402,
+    recvfrom_nocancel = 403,
+    accept_nocancel = 404,
+    msync_nocancel = 405,
+    fcntl_nocancel = 406,
+    select_nocancel = 407,
+    fsync_nocancel = 408,
+    connect_nocancel = 409,
+    sigsuspend_nocancel = 410,
+    readv_nocancel = 411,
+    writev_nocancel = 412,
+    sendto_nocancel = 413,
+    pread_nocancel = 414,
+    pwrite_nocancel = 415,
+    waitid_nocancel = 416,
+    poll_nocancel = 417,
+    msgsnd_nocancel = 418,
+    msgrcv_nocancel = 419,
+    sem_wait_nocancel = 420,
+    aio_suspend_nocancel = 421,
+    __sigwait_nocancel = 422,
+    __semwait_signal_nocancel = 423,
+    __mac_mount = 424,
+    __mac_get_mount = 425,
+    __mac_getfsstat = 426,
+    fsgetpath = 427,
+    audit_session_self = 428,
+    audit_session_join = 429,
+    fileport_makeport = 430,
+    fileport_makefd = 431,
+    audit_session_port = 432,
+    pid_suspend = 433,
+    pid_resume = 434,
+    pid_hibernate = 435,
+    pid_shutdown_sockets = 436,
+    /* 437  old shared_region_slide_np */
+    shared_region_map_and_slide_np = 438,
+    kas_info = 439,
+    memorystatus_control = 440,
+    guarded_open_np = 441,
+    guarded_close_np = 442,
+    guarded_kqueue_np = 443,
+    change_fdguard_np = 444,
+    usrctl = 445,
+    proc_rlimit_control = 446,
+    connectx = 447,
+    disconnectx = 448,
+    peeloff = 449,
+    socket_delegate = 450,
+    telemetry = 451,
+    proc_uuid_policy = 452,
+    memorystatus_get_level = 453,
+    system_override = 454,
+    vfs_purge = 455,
+    sfi_ctl = 456,
+    sfi_pidctl = 457,
+    coalition = 458,
+    coalition_info = 459,
+    necp_match_policy = 460,
+    getattrlistbulk = 461,
+    clonefileat = 462,
+    openat = 463,
+    openat_nocancel = 464,
+    renameat = 465,
+    faccessat = 466,
+    fchmodat = 467,
+    fchownat = 468,
+    fstatat = 469,
+    fstatat64 = 470,
+    linkat = 471,
+    unlinkat = 472,
+    readlinkat = 473,
+    symlinkat = 474,
+    mkdirat = 475,
+    getattrlistat = 476,
+    proc_trace_log = 477,
+    bsdthread_ctl = 478,
+    openbyid_np = 479,
+    recvmsg_x = 480,
+    sendmsg_x = 481,
+    thread_selfusage = 482,
+    csrctl = 483,
+    guarded_open_dprotected_np = 484,
+    guarded_write_np = 485,
+    guarded_pwrite_np = 486,
+    guarded_writev_np = 487,
+    renameatx_np = 488,
+    mremap_encrypted = 489,
+    netagent_trigger = 490,
+    stack_snapshot_with_config = 491,
+    microstackshot = 492,
+    grab_pgo_data = 493,
+    persona = 494,
+    /* 495  */
+    mach_eventlink_signal = 496,
+    mach_eventlink_wait_until = 497,
+    mach_eventlink_signal_wait_until = 498,
+    work_interval_ctl = 499,
+    getentropy = 500,
+    necp_open = 501,
+    necp_client_action = 502,
+    nexus_open = 503,       // for those who are intressted http://newosxbook.com/bonus/vol1ch16.html
+    nexus_register = 504,  
+    nexus_deregister = 505,
+    nexus_create = 506,
+    nexus_destroy = 507,
+    nexus_get_opt = 508,
+    nexus_set_opt = 509,
+    channel_open = 510,
+    channel_get_info = 511,
+    channel_sync = 512,
+    channel_get_opt = 513,
+    channel_set_opt = 514,
+    ulock_wait = 515,
+    ulock_wake = 516,
+    fclonefileat = 517,
+    fs_snapshot = 518,
+    register_uexc_handler = 519,
+    terminate_with_payload = 520,
+    abort_with_payload = 521,
+    necp_session_open = 522,
+    necp_session_action = 523,
+    setattrlistat = 524,
+    net_qos_guideline = 525,
+    fmount = 526,
+    ntp_adjtime = 527,
+    ntp_gettime = 528,
+    os_fault_with_payload = 529,
+    kqueue_workloop_ctl = 530,
+    mach_bridge_remote_time = 531, 
+    coalition_ledger = 532,
+    log_data = 533,
+    memorystatus_available_memory = 534,
+    objc_bp_assist_cfg_np = 535,
+    shared_region_map_and_slide_2_np = 536,
+    pivot_root = 537,
+    task_inspect_for_pid = 538,
+    task_read_for_pid = 539,
+    preadv = 540,
+    pwritev = 541,
+    preadv_nocancel = 542,
+    pwritev_nocancel = 543,
+    ulock_wait2 = 544,
+    proc_info_extended_id = 545,
+    tracker_action = 546,
+    debug_syscall_reject = 547,
+    MAXSYSCALL = 548,
+    /* invalid = 63, */
+}

+ 419 - 0
core/sys/darwin/xnu_system_call_wrappers.odin

@@ -0,0 +1,419 @@
+package darwin
+
+import "core:c"
+import "core:intrinsics"
+
+/* flock */
+LOCK_SH :: 1 /* shared lock */
+LOCK_EX :: 2 /* exclusive lock */
+LOCK_NB :: 4 /* don't block when locking */
+LOCK_UN :: 8 /* unlock */
+
+/* sys/unistd.h for access */
+F_OK  :: c.int(0)         /* test for existence of file */
+X_OK  :: c.int((1 << 0))  /* test for execute or search permission */
+W_OK  :: c.int((1 << 1))  /* test for write permission */
+R_OK  :: c.int((1 << 2))  /* test for read permission */
+
+/* copyfile flags */
+COPYFILE_ACL   :: (1 << 0)
+COPYFILE_STAT  :: (1 << 1)
+COPYFILE_XATTR :: (1 << 2)
+COPYFILE_DATA  :: (1 << 3)
+
+COPYFILE_SECURITY :: (COPYFILE_STAT | COPYFILE_ACL)
+COPYFILE_METADATA :: (COPYFILE_SECURITY | COPYFILE_XATTR)
+COPYFILE_ALL	  :: (COPYFILE_METADATA | COPYFILE_DATA)
+
+/* syslimits.h */
+PATH_MAX	:: 1024	/* max bytes in pathname */
+
+/* param.h */
+MAXPATHLEN :: PATH_MAX
+
+/* proc_info.h */
+DARWIN_PROC_PIDPATHINFO_SIZE :: MAXPATHLEN
+DARWIN_PROC_PIDPATHINFO :: 11
+
+DARWIN_PROC_ALL_PIDS  :: c.int(1)
+DARWIN_PROC_PGRP_ONLY :: c.int(2)
+DARWIN_PROC_TTY_ONLY  :: c.int(3)
+DARWIN_PROC_UID_ONLY  :: c.int(4)
+DARWIN_PROC_RUID_ONLY :: c.int(5)
+DARWIN_PROC_PPID_ONLY :: c.int(6)
+DARWIN_PROC_KDBG_ONLY :: c.int(7)
+
+DARWIN_PROC_INFO_CALL_LISTPIDS          :: c.int(0x1)
+DARWIN_PROC_INFO_CALL_PIDINFO           :: c.int(0x2)
+DARWIN_PROC_INFO_CALL_PIDFDINFO         :: c.int(0x3)
+DARWIN_PROC_INFO_CALL_KERNMSGBUF        :: c.int(0x4)
+DARWIN_PROC_INFO_CALL_SETCONTROL        :: c.int(0x5)
+DARWIN_PROC_INFO_CALL_PIDFILEPORTINFO   :: c.int(0x6)
+DARWIN_PROC_INFO_CALL_TERMINATE         :: c.int(0x7)
+DARWIN_PROC_INFO_CALL_DIRTYCONTROL      :: c.int(0x8)
+DARWIN_PROC_INFO_CALL_PIDRUSAGE         :: c.int(0x9)
+DARWIN_PROC_INFO_CALL_PIDORIGINATORINFO :: c.int(0xa)
+DARWIN_PROC_INFO_CALL_LISTCOALITIONS    :: c.int(0xb)
+DARWIN_PROC_INFO_CALL_CANUSEFGHW        :: c.int(0xc)
+DARWIN_PROC_INFO_CALL_PIDDYNKQUEUEINFO  :: c.int(0xd)
+DARWIN_PROC_INFO_CALL_UDATA_INFO        :: c.int(0xe)
+
+/* mmap flags */
+MAP_ANONYMOUS    :: 0x1000 /* allocated from memory, swap space */
+MAP_FILE         :: 0x0000 /* map from file (default) */
+MAP_FIXED        :: 0x0010 /* [MF|SHM] interpret addr exactly */
+MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
+MAP_PRIVATE      :: 0x0002 /* [MF|SHM] changes are private */
+MAP_SHARED       :: 0x0001 /* [MF|SHM] share changes */
+MAP_NOCACHE      :: 0x0400 /* don't cache pages for this mapping */
+MAP_JIT          :: 0x0800 /* Allocate a region that will be used for JIT purposes */
+MAP_32BIT        :: 0x8000 /* Return virtual addresses <4G only */
+
+/* mmap prot flags */
+PROT_NONE  :: 0x00 /* [MC2] no permissions */
+PROT_READ  :: 0x01 /* [MC2] pages can be read */
+PROT_WRITE :: 0x02 /* [MC2] pages can be written */
+PROT_EXEC  :: 0x04 /* [MC2] pages can be executed */
+
+/* For owner Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXU :: 0o000700 /* RWX mask for owner */
+PERMISSION_MASK_IRUSR :: 0o000400 /* R for owner */
+PERMISSION_MASK_IWUSR :: 0o000200 /* W for owner */
+PERMISSION_MASK_IXUSR :: 0o000100 /* X for owner */
+
+/* For group Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXG :: 0o000070 /* RWX mask for group */
+PERMISSION_MASK_IRGRP :: 0o000040 /* R for group */
+PERMISSION_MASK_IWGRP :: 0o000020 /* W for group */
+PERMISSION_MASK_IXGRP :: 0o000010 /* X for group */
+
+/* For other Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXO :: 0o000007 /* RWX mask for other */
+PERMISSION_MASK_IROTH :: 0o000004 /* R for other */
+PERMISSION_MASK_IWOTH :: 0o000002 /* W for other */
+PERMISSION_MASK_IXOTH :: 0o000001 /* X for other */
+
+/* Special Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_ISUID :: 0o004000 /* set user id on execution */
+PERMISSION_MASK_ISGID :: 0o002000 /* set group id on execution */
+PERMISSION_MASK_ISVTX :: 0o001000 /* save swapped text even after use */
+
+OPEN_FLAG_RDONLY :: 0x0000 /* open for reading only */
+OPEN_FLAG_WRONLY :: 0x0001 /* open for writing only */
+OPEN_FLAG_RDWR   :: 0x0002 /* open for reading and writing */
+
+/* mask for above rd/wr/rdwr flags */
+MASK_ACCMODE      :: 0x0003 
+
+OPEN_FLAG_NONBLOCK     :: 0x00000004 /* no delay */
+OPEN_FLAG_APPEND       :: 0x00000008 /* set append mode */
+OPEN_FLAG_CREAT        :: 0x00000200 /* create if nonexistant */
+OPEN_FLAG_TRUNC        :: 0x00000400 /* truncate to zero length */
+OPEN_FLAG_EXCL         :: 0x00000800 /* error if already exists */
+OPEN_FLAG_SHLOCK       :: 0x00000010 /* open with shared file lock */
+OPEN_FLAG_EXLOCK       :: 0x00000020 /* open with exclusive file lock */
+OPEN_FLAG_DIRECTORY    :: 0x00100000 /* restrict open to only directories */
+OPEN_FLAG_NOFOLLOW     :: 0x00000100 /* don't follow symlinks */
+OPEN_FLAG_SYMLINK      :: 0x00200000 /* allow open of a symlink */
+OPEN_FLAG_EVTONLY      :: 0x00008000 /* descriptor requested for event notifications only */
+OPEN_FLAG_CLOEXEC      :: 0x01000000 /* causes the descriptor to be closed if you use any of the exec like functions */
+OPEN_FLAG_NOFOLLOW_ANY :: 0x20000000 /* no symlinks allowed in path */
+
+/* bsd/sys/param.h */
+DARWIN_MAXCOMLEN :: 16
+
+/*--==========================================================================--*/
+
+__darwin_ino64_t :: u64
+__darwin_time_t :: u32
+__darwin_dev_t :: i32
+__darwin_mode_t :: u16
+__darwin_off_t :: i64
+__darwin_blkcnt_t :: i64
+__darwin_blksize_t :: i32
+__darwin_pid_t :: i32
+__darwin_suseconds_t :: i32
+
+time_t :: __darwin_time_t
+dev_t :: __darwin_dev_t
+mode_t :: u16
+nlink_t :: u16  
+uid_t :: u16
+gid_t :: u16
+off_t :: __darwin_off_t
+blkcnt_t :: __darwin_blkcnt_t
+blksize_t :: __darwin_blksize_t
+pid_t :: __darwin_pid_t
+
+stat :: __DARWIN_STRUCT_STAT64 
+timeval :: _STRUCT_TIMEVAL
+
+/*--==========================================================================--*/
+
+/* sys/stat.h */
+__DARWIN_STRUCT_STAT64 :: struct {
+	st_dev: dev_t,            /* [XSI] ID of device containing file */
+	st_mode: mode_t,          /* [XSI] Mode of file (see below) */
+	st_nlink: nlink_t,        /* [XSI] Number of hard links */
+	st_ino: __darwin_ino64_t, /* [XSI] File serial number */
+	st_uid_t: uid_t,          /* [XSI] User ID of the file */
+	st_gid_t: gid_t,          /* [XSI] Group ID of the file */
+	st_rdev: dev_t,           /* [XSI] Device ID */
+
+	// __DARWIN_STRUCT_STAT64_TIMES 
+	st_atime: time_t,      /* [XSI] Time of last access */ 
+	st_atimensec: i32,     /* nsec of last access */ 
+	st_mtime: time_t,      /* [XSI] Last data modification time */
+	st_mtimensec: i32,     /* last data modification nsec */ 
+	st_ctime: time_t,      /* [XSI] Time of last status change */ 
+	st_ctimensec: u32,     /* nsec of last status change */ 
+	st_birthtime: time_t,  /*  File creation time(birth)  */
+	st_birthtimensec: i32, /* nsec of File creation time */
+	// end __DARWIN_STRUCT_STAT64_TIMES
+
+	st_size: off_t,        /* [XSI] file size, in bytes */
+	st_blocks: blkcnt_t,   /* [XSI] blocks allocated for file */
+	st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
+	st_flags: u32,         /* user defined flags for file */
+	st_gen: u32,           /* file generation number */
+	st_lspare: i32,        /* RESERVED: DO NOT USE! */
+	st_qspare: [2]i64,     /* RESERVED: DO NOT USE! */
+}
+
+/* sys/_types/_timeval.h */
+_STRUCT_TIMEVAL :: struct {
+	tv_sec: __darwin_time_t, /* seconds */
+	tv_usec: __darwin_suseconds_t, /* microseconds */
+}
+
+/* pwd.h */
+_Password_Entry :: struct {
+    pw_name: cstring, /* username */
+    pw_passwd: cstring, /* user password */
+    pw_uid: i32,   /* user ID */
+    pw_gid: i32,   /* group ID */
+	pw_change: u64,     /* password change time */
+	pw_class: cstring, /* user access class */
+    pw_gecos: cstring, /* full user name */
+    pw_dir: cstring, /* home directory */
+    pw_shell: cstring, /* shell program */
+	pw_expire: u64,     /* account expiration */
+	pw_fields: i32,     /* filled fields */
+}
+
+/* processinfo.h */
+_Proc_Bsdinfo :: struct {
+  pbi_flags: u32, /* if is 64bit; emulated etc */
+  pbi_status: u32,
+  pbi_xstatus: u32,
+  pbi_pid: u32,
+  pbi_ppid: u32,
+  pbi_uid: u32,
+  pbi_gid: u32,
+  pbi_ruid: u32,
+  pbi_rgid: u32,
+  pbi_svuid: u32,
+  pbi_svgid: u32,
+  res: u32,
+  pbi_comm: [DARWIN_MAXCOMLEN]u8,
+  pbi_name: [2 * DARWIN_MAXCOMLEN]u8,	/* empty if no name is registered */
+  pbi_nfiles: u32,
+  pbi_pgid: u32,
+  pbi_pjobc: u32,
+  e_tdev: u32, /* controlling tty dev */
+  e_tpgid: u32,	/* tty process group id */
+  pbi_nice: i32,
+  pbi_start_tvsec: u64,
+  pbi_start_tvusec: u64,
+}
+
+/*--==========================================================================--*/
+
+syscall_fsync :: #force_inline proc(fildes: c.int) -> bool {
+	return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes)))
+}
+
+syscall_write :: #force_inline proc (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
+	return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.write),  uintptr(fildes), uintptr(buf), uintptr(nbyte)))
+}
+ 
+syscall_read :: #force_inline proc(fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
+	return cast(i64)intrinsics.syscall(unix_offset_syscall(.read), uintptr(fildes), uintptr(buf), uintptr(nbyte))
+}
+
+syscall_open :: #force_inline proc(path: cstring, oflag: u32, mode: u32) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.open), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
+}
+
+syscall_close :: #force_inline proc(fd: c.int) -> bool {
+	return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd)))
+}
+
+syscall_fchmod :: #force_inline proc(fildes: c.int, mode: u32) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.fchmod), uintptr(fildes), uintptr(mode)))
+}
+
+syscall_chmod :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.chmod), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_mkdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_mkdir_at :: #force_inline proc(fd: c.int, path: cstring, mode: u32) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), uintptr(fd), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_rmdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rmdir), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_rename :: #force_inline proc(path_old: cstring, path_new: cstring) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rename), transmute(uintptr)path_old, transmute(uintptr)path_new))
+}
+
+syscall_rename_at :: #force_inline proc(from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
+	return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.renameat), uintptr(from_fd), transmute(uintptr)from, uintptr(to_fd), transmute(uintptr)to))
+}
+
+syscall_lseek :: #force_inline proc(fd: c.int, offset: i64, whence: c.int) -> i64 {
+	return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
+}
+
+syscall_gettid :: #force_inline proc() -> u64 {
+	return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
+}
+
+syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstat), uintptr(fd), uintptr(status))
+}
+
+syscall_lstat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.lstat), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_stat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.stat), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_fstatat :: #force_inline proc(fd: c.int, path: cstring, status: ^stat) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstatat), uintptr(fd), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_link :: #force_inline proc(path: cstring, to_link: cstring) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.link), transmute(uintptr)path, transmute(uintptr)to_link)
+}
+
+syscall_linkat :: #force_inline proc(fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.linkat), uintptr(fd), transmute(uintptr)path, uintptr(fd2), transmute(uintptr)to_link)
+}
+
+syscall_readlink :: #force_inline proc(path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+	return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlink), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
+}
+
+syscall_readlinkat :: #force_inline proc(fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+	return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlinkat), uintptr(fd), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
+}
+
+syscall_access :: #force_inline proc(path: cstring, mode: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.access), transmute(uintptr)path, uintptr(mode))
+}
+
+syscall_faccessat :: #force_inline proc(fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.faccessat), uintptr(fd), transmute(uintptr)path, uintptr(mode), uintptr(flag))
+}
+
+syscall_getdirentries :: #force_inline proc(fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getdirentries), uintptr(fd), uintptr(buf), uintptr(nbytes), uintptr(base_pointer))
+}
+
+syscall_truncate :: #force_inline proc (path: cstring, length: off_t) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.truncate), transmute(uintptr)path, uintptr(length))
+}
+
+syscall_ftruncate :: #force_inline proc (fd: c.int, length: off_t) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.ftruncate), uintptr(fd), uintptr(length))
+}
+
+syscall_sysctl :: #force_inline proc (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctl), uintptr(name), uintptr(namelen), uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
+}
+
+syscall_copyfile ::  #force_inline proc(from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.copyfile), transmute(uintptr)from, transmute(uintptr)to, uintptr(state), uintptr(flags))
+} 
+
+// think about this? last arg should be more than one
+syscall_fcntl :: #force_inline proc(fd: c.int, cmd: c.int, other: rawptr) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other))
+}
+
+syscall_exit :: #force_inline proc(code: c.int) {
+	intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
+}
+
+syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.kill), uintptr(pid), uintptr(sig))
+}
+
+syscall_dup :: #force_inline proc(fd: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.dup), uintptr(fd))
+}
+
+syscall_execve :: #force_inline proc(path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.execve), transmute(uintptr)path, transmute(uintptr)argv, transmute(uintptr)env)
+}
+
+syscall_munmap :: #force_inline proc(addr: rawptr, len: u64) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len))
+}
+
+syscall_mmap :: #force_inline proc(addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
+	return cast(^u8)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len), uintptr(port), uintptr(flags), uintptr(fd), uintptr(offset))
+}
+
+syscall_flock :: #force_inline proc(fd: c.int, operation: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.flock), uintptr(fd), uintptr(operation)) 
+}
+
+syscall_utimes :: #force_inline proc(path: cstring, times: ^timeval) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.utimes), transmute(uintptr)path, uintptr(times))
+}
+
+syscall_futimes :: #force_inline proc(fd: c.int, times: ^timeval) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.futimes), uintptr(fd), uintptr(times))
+}
+
+syscall_adjtime :: #force_inline proc(delta: ^timeval, old_delta: ^timeval) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.adjtime), uintptr(delta), uintptr(old_delta))
+}
+
+syscall_sysctlbyname :: #force_inline proc(name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctlbyname), transmute(uintptr)name, uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
+}
+
+syscall_proc_info :: #force_inline proc(num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.proc_info), uintptr(num), uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(buffer), uintptr(buffer_size))
+}
+
+syscall_openat :: #force_inline proc(fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.openat), uintptr(fd), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
+} 
+
+syscall_getentropy :: #force_inline proc(buf: ^u8, buflen: u64) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen))
+}
+
+syscall_pipe :: #force_inline proc(fds: [^]c.int) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(&fds[0]), uintptr(&fds[1]))
+}
+
+syscall_chdir :: #force_inline proc(path: cstring) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), transmute(uintptr)path)
+}
+
+syscall_fchdir :: #force_inline proc(fd: c.int, path: cstring) -> c.int {
+	return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path)
+}

+ 65 - 0
core/sys/unix/pthread_openbsd.odin

@@ -0,0 +1,65 @@
+//+build openbsd
+package unix
+
+import "core:c"
+
+pthread_t             :: distinct rawptr
+pthread_attr_t        :: distinct rawptr
+pthread_mutex_t       :: distinct rawptr
+pthread_mutexattr_t   :: distinct rawptr
+pthread_cond_t        :: distinct rawptr
+pthread_condattr_t    :: distinct rawptr
+pthread_rwlock_t      :: distinct rawptr
+pthread_rwlockattr_t  :: distinct rawptr
+pthread_barrier_t     :: distinct rawptr
+pthread_barrierattr_t :: distinct rawptr
+pthread_spinlock_t    :: distinct rawptr
+
+pthread_key_t  :: distinct c.int
+pthread_once_t :: struct {
+	state: c.int,
+	mutex: pthread_mutex_t,
+}
+
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE  :: 2
+PTHREAD_MUTEX_NORMAL     :: 3
+PTHREAD_MUTEX_STRICT_NP  :: 4
+
+PTHREAD_DETACHED      :: 0x1
+PTHREAD_SCOPE_SYSTEM  :: 0x2
+PTHREAD_INHERIT_SCHED :: 0x4
+PTHREAD_NOFLOAT       :: 0x8
+
+PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_SCOPE_PROCESS   :: 0
+PTHREAD_EXPLICIT_SCHED  :: 0
+
+SCHED_FIFO  :: 1
+SCHED_OTHER :: 2
+SCHED_RR    :: 3
+
+sched_param :: struct {
+	sched_priority: c.int,
+}
+
+sem_t :: distinct rawptr
+
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+	sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
+
+	sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+	sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+	sem_post :: proc(sem: ^sem_t) -> c.int ---
+	sem_wait :: proc(sem: ^sem_t) -> c.int ---
+	sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+	//sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
+
+	// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
+	// see https://linux.die.net/man/3/pthread_yield
+	pthread_yield :: proc() ---
+}

+ 1 - 1
core/sys/unix/pthread_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package unix
 
 foreign import "system:pthread"

+ 4 - 4
core/sys/unix/syscalls_linux.odin

@@ -15,7 +15,7 @@ import "core:intrinsics"
 //  386: arch/x86/entry/syscalls/sycall_32.tbl
 //  arm: arch/arm/tools/syscall.tbl
 
-when ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .amd64 {
 	SYS_read : uintptr : 0
 	SYS_write : uintptr : 1
 	SYS_open : uintptr : 2
@@ -374,7 +374,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
 	SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm64" {
+} else when ODIN_ARCH == .arm64 {
 	SYS_io_setup : uintptr : 0
 	SYS_io_destroy : uintptr : 1
 	SYS_io_submit : uintptr : 2
@@ -675,7 +675,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_create_ruleset : uintptr : 444
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
-} else when ODIN_ARCH == "i386" {
+} else when ODIN_ARCH == .i386 {
 	SYS_restart_syscall : uintptr : 0
 	SYS_exit : uintptr : 1
 	SYS_fork : uintptr : 2
@@ -1112,7 +1112,7 @@ when ODIN_ARCH == "amd64" {
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
 	SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm" {
+} else when false /*ODIN_ARCH == .arm*/ { // TODO
 	SYS_restart_syscall : uintptr : 0
 	SYS_exit : uintptr : 1
 	SYS_fork : uintptr : 2

+ 3 - 0
core/sys/win32/user32.odin

@@ -101,6 +101,9 @@ foreign user32 {
 	}
 	@(link_name="GetCursorPos")     get_cursor_pos      :: proc(p: ^Point) -> Bool ---
 	@(link_name="SetCursorPos")     set_cursor_pos      :: proc(x, y: i32) -> Bool ---
+	@(link_name="GetCapure")        get_capture         :: proc(hwnd: Hwnd) -> Hwnd ---
+	@(link_name="SetCapture")       set_capture         :: proc(hwnd: Hwnd) -> Hwnd ---
+	@(link_name="ReleaseCapture")   release_capture     :: proc() -> Bool ---
 	@(link_name="ScreenToClient")   screen_to_client    :: proc(h: Hwnd, p: ^Point) -> Bool ---
 	@(link_name="ClientToScreen")   client_to_screen    :: proc(h: Hwnd, p: ^Point) -> Bool ---
 	@(link_name="PostQuitMessage")  post_quit_message   :: proc(exit_code: i32) ---

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

@@ -376,6 +376,9 @@ FILE_TYPE_DISK :: 0x0001
 FILE_TYPE_CHAR :: 0x0002
 FILE_TYPE_PIPE :: 0x0003
 
+RECT  :: struct {left, top, right, bottom: LONG}
+POINT :: struct {x, y: LONG}
+
 
 when size_of(uintptr) == 4 {
 	WSADATA :: struct {

+ 1 - 1
core/thread/thread_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 // +private
 package thread
 

+ 24 - 12
core/time/time_unix.odin

@@ -1,9 +1,9 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package time
 
 IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
 
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
 	foreign import libc "System.framework"
 } else  {
 	foreign import libc "system:c"
@@ -22,16 +22,28 @@ TimeSpec :: struct {
 	tv_nsec : i64,  /* nanoseconds */
 }
 
-CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
-CLOCK_PROCESS_CPUTIME_ID :: 2
-CLOCK_THREAD_CPUTIME_ID  :: 3
-CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-CLOCK_MONOTONIC_COARSE   :: 6
-CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-CLOCK_REALTIME_ALARM     :: 8
-CLOCK_BOOTTIME_ALARM     :: 9
+when ODIN_OS == .OpenBSD {
+	CLOCK_REALTIME           :: 0
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_MONOTONIC          :: 3
+	CLOCK_THREAD_CPUTIME_ID  :: 4
+	CLOCK_UPTIME             :: 5
+	CLOCK_BOOTTIME           :: 6
+
+	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
+	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
+} else {
+	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
+	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_THREAD_CPUTIME_ID  :: 3
+	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
+	CLOCK_MONOTONIC_COARSE   :: 6
+	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
+	CLOCK_REALTIME_ALARM     :: 8
+	CLOCK_BOOTTIME_ALARM     :: 9
+}
 
 // TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
 // I do not know if Darwin programmers are used to the existance of these constants or not, so

+ 1 - 0
examples/all/all_main.odin

@@ -119,6 +119,7 @@ _ :: bit_array
 _ :: priority_queue
 _ :: queue
 _ :: small_array
+_ :: lru
 _ :: crypto
 _ :: blake
 _ :: blake2b

+ 7 - 14
examples/all/all_vendor.odin

@@ -1,7 +1,5 @@
-//+build windows
 package all
 
-
 import botan     "vendor:botan"
 import ENet      "vendor:ENet"
 import gl        "vendor:OpenGL"
@@ -17,14 +15,11 @@ import IMG    "vendor:sdl2/image"
 import MIX    "vendor:sdl2/mixer"
 import TTF    "vendor:sdl2/ttf"
 
-import stb_easy_font "vendor:stb/easy_font"
-import stbi          "vendor:stb/image"
-import stbrp         "vendor:stb/rect_pack"
-import stbtt         "vendor:stb/truetype"
-import stb_vorbis    "vendor:stb/vorbis"
-
 import vk "vendor:vulkan"
 
+import NS  "vendor:darwin/Foundation"
+import MTL "vendor:darwin/Metal"
+import CA  "vendor:darwin/QuartzCore"
 
 _ :: botan
 _ :: ENet
@@ -39,9 +34,7 @@ _ :: SDLNet
 _ :: IMG
 _ :: MIX
 _ :: TTF
-_ :: stb_easy_font
-_ :: stbi
-_ :: stbrp
-_ :: stbtt
-_ :: stb_vorbis
-_ :: vk
+_ :: vk
+_ :: NS
+_ :: MTL
+_ :: CA

+ 10 - 0
examples/all/all_vendor_directx.odin

@@ -0,0 +1,10 @@
+//+build windows
+package all
+
+import D3D11 "vendor:directx/d3d11"
+import D3D12 "vendor:directx/d3d12"
+import DXGI  "vendor:directx/dxgi"
+
+_ :: D3D11
+_ :: D3D12
+_ :: DXGI

+ 15 - 0
examples/all/all_vendor_stl.odin

@@ -0,0 +1,15 @@
+//+build windows, linux
+package all
+
+import stb_easy_font "vendor:stb/easy_font"
+import stbi          "vendor:stb/image"
+import stbrp         "vendor:stb/rect_pack"
+import stbtt         "vendor:stb/truetype"
+import stb_vorbis    "vendor:stb/vorbis"
+
+_ :: stb_easy_font
+_ :: stbi
+_ :: stbrp
+_ :: stbtt
+_ :: stb_vorbis
+

+ 22 - 16
examples/demo/demo.odin

@@ -11,22 +11,28 @@ import "core:intrinsics"
 import "core:math/big"
 
 /*
-	The Odin programming language is fast, concise, readable, pragmatic and open sourced.
-	It is designed with the intent of replacing C with the following goals:
-	 * simplicity
-	 * high performance
-	 * built for modern systems
-	 * joy of programming
+	Odin is a general-purpose programming language with distinct typing built
+	for high performance, modern systems and data-oriented programming.
+
+	Odin is the C alternative for the Joy of Programming.
 
 	# Installing Odin
 	Getting Started - https://odin-lang.org/docs/install/
 		Instructions for downloading and install the Odin compiler and libraries.
 
 	# Learning Odin
+	Getting Started - https://odin-lang.org/docs/install/
+		Getting Started with Odin. Downloading, installing, and getting your
+		first program to compile and run.
 	Overview of Odin - https://odin-lang.org/docs/overview/
-		An overview of the Odin programming language.
+		An overview of the Odin programming language and its features.
 	Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
 		Answers to common questions about Odin.
+	Packages - https://pkg.odin-lang.org/
+		Documentation for all the official packages part of the
+		core and vendor library collections.
+	Nightly Builds - https://odin-lang.org/docs/nightly/
+		Get the latest nightly builds of Odin.
 */
 
 the_basics :: proc() {
@@ -244,10 +250,10 @@ control_flow :: proc() {
 		// A switch statement is another way to write a sequence of if-else statements.
 		// In Odin, the default case is denoted as a case without any expression.
 
-		switch arch := ODIN_ARCH; arch {
-		case "386":
+		#partial switch arch := ODIN_ARCH; arch {
+		case .i386:
 			fmt.println("32-bit")
-		case "amd64":
+		case .amd64:
 			fmt.println("64-bit")
 		case: // default
 			fmt.println("Unsupported architecture")
@@ -363,12 +369,12 @@ control_flow :: proc() {
 		*/
 
 		// Example
-		when ODIN_ARCH == "386" {
+		when ODIN_ARCH == .i386 {
 			fmt.println("32 bit")
-		} else when ODIN_ARCH == "amd64" {
+		} else when ODIN_ARCH == .amd64 {
 			fmt.println("64 bit")
 		} else {
-			fmt.println("Unsupported architecture")
+			fmt.println("Unknown architecture")
 		}
 		// The when statement is very useful for writing platform specific code.
 		// This is akin to the #if construct in C’s preprocessor however, in Odin,
@@ -1100,7 +1106,7 @@ prefix_table := [?]string{
 }
 
 threading_example :: proc() {
-	if ODIN_OS == "darwin" {
+	if ODIN_OS == .Darwin {
 		// TODO: Fix threads on darwin/macOS
 		return
 	}
@@ -1606,13 +1612,13 @@ where_clauses :: proc() {
 }
 
 
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
 	foreign import kernel32 "system:kernel32.lib"
 }
 
 foreign_system :: proc() {
 	fmt.println("\n#foreign system")
-	when ODIN_OS == "windows" {
+	when ODIN_OS == .Windows {
 		// It is sometimes necessarily to interface with foreign code,
 		// such as a C library. In Odin, this is achieved through the
 		// foreign system. You can “import” a library into the code

+ 3 - 1
src/array.cpp

@@ -89,7 +89,9 @@ template <typename T>
 void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
 	GB_ASSERT(count >= 0);
 	s->data = gb_alloc_array(allocator, T, count);
-	GB_ASSERT(s->data != nullptr);
+	if (count > 0) {
+		GB_ASSERT(s->data != nullptr);
+	}
 	s->count = count;
 }
 

+ 24 - 3
src/bug_report.cpp

@@ -17,6 +17,11 @@
 	#include <sys/sysctl.h>
 #endif
 
+#if defined(GB_SYSTEM_OPENBSD)
+	#include <sys/sysctl.h>
+	#include <sys/utsname.h>
+#endif
+
 /*
 	NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
 */
@@ -242,6 +247,14 @@ void report_ram_info() {
 		if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
 			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
 		}
+	#elif defined(GB_SYSTEM_OPENBSD)
+		uint64_t ram_amount;
+		size_t   val_size = sizeof(ram_amount);
+
+		int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
+		if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+		}
 	#else
 		gb_printf("Unknown.\n");
 	#endif
@@ -473,11 +486,11 @@ void print_bug_report_help() {
 
 	#elif defined(GB_SYSTEM_LINUX)
 		/*
-			Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+			Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
 		*/
 		gbAllocator a = heap_allocator();
 
-		gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
+		gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
 		defer (gb_file_free_contents(&release));
 
 		b32 found = 0;
@@ -643,6 +656,14 @@ void print_bug_report_help() {
 		} else {
 			gb_printf("macOS: Unknown\n");
 		}			
+	#elif defined(GB_SYSTEM_OPENBSD)
+		struct utsname un;
+		
+		if (uname(&un) != -1) {
+			gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+		} else {
+			gb_printf("OpenBSD: Unknown\n");    
+		}
 	#else
 		gb_printf("Unknown\n");
 
@@ -657,4 +678,4 @@ void print_bug_report_help() {
 		And RAM info.
 	*/
 	report_ram_info();
-}
+}

+ 48 - 3
src/build_settings.cpp

@@ -1,4 +1,4 @@
-#if defined(GB_SYSTEM_FREEBSD)
+#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #endif
@@ -16,6 +16,7 @@ enum TargetOsKind {
 	TargetOs_linux,
 	TargetOs_essence,
 	TargetOs_freebsd,
+	TargetOs_openbsd,
 	
 	TargetOs_wasi,
 	TargetOs_js,
@@ -53,6 +54,7 @@ String target_os_names[TargetOs_COUNT] = {
 	str_lit("linux"),
 	str_lit("essence"),
 	str_lit("freebsd"),
+	str_lit("openbsd"),
 	
 	str_lit("wasi"),
 	str_lit("js"),
@@ -354,6 +356,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 
+gb_global TargetMetrics target_openbsd_amd64 = {
+	TargetOs_openbsd,
+	TargetArch_amd64,
+	8,
+	16,
+	str_lit("x86_64-unknown-openbsd-elf"),
+	str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
 gb_global TargetMetrics target_essence_amd64 = {
 	TargetOs_essence,
 	TargetArch_amd64,
@@ -417,6 +428,7 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
 	{ str_lit("freebsd_386"),         &target_freebsd_386    },
 	{ str_lit("freebsd_amd64"),       &target_freebsd_amd64  },
+	{ str_lit("openbsd_amd64"),       &target_openbsd_amd64  },
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
 	{ str_lit("wasi_wasm32"),         &target_wasi_wasm32 },
 	{ str_lit("js_wasm32"),           &target_js_wasm32 },
@@ -722,10 +734,38 @@ String internal_odin_root_dir(void) {
 		len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
 #elif defined(GB_SYSTEM_DRAGONFLYBSD)
 		len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
-#else
+#elif defined(GB_SYSTEM_LINUX)
 		len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
+#elif defined(GB_SYSTEM_OPENBSD)
+		int error;
+		int mib[] = {
+			CTL_KERN,
+			KERN_PROC_ARGS,
+			getpid(),
+			KERN_PROC_ARGV,
+		};
+		// get argv size
+		error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
+		if (error == -1) {
+			// sysctl error
+			return make_string(nullptr, 0);
+		}
+		// get argv
+		char **argv = (char **)gb_malloc(len);
+		error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
+		if (error == -1) {
+			// sysctl error
+			gb_mfree(argv);
+			return make_string(nullptr, 0);
+		}
+		// copy argv[0] to path_buf
+		len = gb_strlen(argv[0]);
+		if(len < path_buf.count) {
+			gb_memmove(&path_buf[0], argv[0], len);
+		}
+		gb_mfree(argv);
 #endif
-		if(len == 0) {
+		if(len == 0 || len == -1) {
 			return make_string(nullptr, 0);
 		}
 		if (len < path_buf.count) {
@@ -922,6 +962,8 @@ void init_build_context(TargetMetrics *cross_target) {
 			#endif
 		#elif defined(GB_SYSTEM_FREEBSD)
 			metrics = &target_freebsd_amd64;
+		#elif defined(GB_SYSTEM_OPENBSD)
+			metrics = &target_openbsd_amd64;
 		#elif defined(GB_CPU_ARM)
 			metrics = &target_linux_arm64;
 		#else
@@ -980,6 +1022,9 @@ void init_build_context(TargetMetrics *cross_target) {
 		case TargetOs_freebsd:
 			bc->link_flags = str_lit("-arch x86-64 ");
 			break;
+		case TargetOs_openbsd:
+			bc->link_flags = str_lit("-arch x86-64 ");
+			break;
 		}
 	} else if (bc->metrics.arch == TargetArch_i386) {
 		switch (bc->metrics.os) {

+ 296 - 7
src/check_builtin.cpp

@@ -143,6 +143,241 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
 }
 
 
+bool does_require_msgSend_stret(Type *return_type) {
+	if (return_type == nullptr) {
+		return false;
+	}
+	if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+		i64 struct_limit = type_size_of(t_uintptr) << 1;
+		return type_size_of(return_type) > struct_limit;
+	}
+	if (build_context.metrics.arch == TargetArch_arm64) {
+		return false;
+	}
+
+	// if (build_context.metrics.arch == TargetArch_arm32) {
+	// 	i64 struct_limit = type_size_of(t_uintptr);
+	// 	// NOTE(bill): This is technically wrong
+	// 	return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit;
+	// }
+	GB_PANIC("unsupported architecture");
+	return false;
+}
+
+ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+	if (return_type == nullptr) {
+		return ObjcMsg_normal;
+	}
+
+	if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+		if (is_type_float(return_type)) {
+			return ObjcMsg_fpret;
+		}
+		if (build_context.metrics.arch == TargetArch_amd64) {
+			if (is_type_complex(return_type)) {
+				// URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159
+				return ObjcMsg_fpret;
+			}
+		}
+	}
+	if (build_context.metrics.arch != TargetArch_arm64) {
+		if (does_require_msgSend_stret(return_type)) {
+			return ObjcMsg_stret;
+		}
+	}
+	return ObjcMsg_normal;
+}
+
+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);
+
+	// NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope
+	Type *params = alloc_type_tuple();
+	{
+		auto variables = array_make<Entity *>(permanent_allocator(), 0, param_types.count);
+
+		for_array(i, param_types)  {
+			Type *type = param_types[i];
+			Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
+			array_add(&variables, param);
+		}
+		params->Tuple.variables = slice_from_array(variables);
+	}
+
+	Type *results = alloc_type_tuple();
+	if (return_type) {
+		auto variables = array_make<Entity *>(permanent_allocator(), 1);
+		results->Tuple.variables = slice_from_array(variables);
+		Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true);
+		results->Tuple.variables[0] = param;
+	}
+
+
+	ObjcMsgData data = {};
+	data.kind = kind;
+	data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+	mutex_lock(&c->info->objc_types_mutex);
+	map_set(&c->info->objc_msgSend_types, call, data);
+	mutex_unlock(&c->info->objc_types_mutex);
+
+	try_to_add_package_dependency(c, "runtime", "objc_msgSend");
+	try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
+	try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
+	try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+}
+
+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) {
+		if (name_) *name_ = op.value.value_string;
+		return true;
+	}
+	gbString e = expr_to_string(op.expr);
+	gbString t = type_to_string(op.type);
+	error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
+	gb_string_free(t);
+	gb_string_free(e);
+	return false;
+}
+
+bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+	String builtin_name = builtin_procs[id].name;
+
+	if (build_context.metrics.os != TargetOs_darwin) {
+		// allow on doc generation (e.g. Metal stuff)
+		if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
+			error(call, "'%.*s' only works on darwin", LIT(builtin_name));
+		}
+	}
+
+
+	ast_node(ce, CallExpr, call);
+	switch (id) {
+	default:
+		GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name));
+		return false;
+
+	case BuiltinProc_objc_send: {
+		Type *return_type = nullptr;
+
+		Operand rt = {};
+		check_expr_or_type(c, &rt, ce->args[0]);
+		if (rt.mode == Addressing_Type) {
+			return_type = rt.type;
+		} else if (is_operand_nil(rt)) {
+			return_type = nullptr;
+		} else {
+			gbString e = expr_to_string(rt.expr);
+			error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e);
+			gb_string_free(e);
+			return false;
+		}
+
+		operand->type = return_type;
+		operand->mode = return_type ? Addressing_Value : Addressing_NoValue;
+
+		String class_name = {};
+		String sel_name = {};
+
+		Type *sel_type = t_objc_SEL;
+		Operand self = {};
+		check_expr_or_type(c, &self, ce->args[1]);
+		if (self.mode == Addressing_Type) {
+			if (!is_type_objc_object(self.type)) {
+				gbString t = type_to_string(self.type);
+				error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+			if (!has_type_got_objc_class_attribute(self.type)) {
+				gbString t = type_to_string(self.type);
+				error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+
+			sel_type = t_objc_Class;
+		} else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+			gbString e = expr_to_string(self.expr);
+			gbString t = type_to_string(self.type);
+			error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
+			gb_string_free(t);
+			gb_string_free(e);
+			return false;
+		} else if (!is_type_pointer(self.type)) {
+			gbString e = expr_to_string(self.expr);
+			gbString t = type_to_string(self.type);
+			error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+			gb_string_free(t);
+			gb_string_free(e);
+			return false;
+		} else {
+			Type *type = type_deref(self.type);
+			if (!(type->kind == Type_Named &&
+			      type->Named.type_name != nullptr &&
+			      type->Named.type_name->TypeName.objc_class_name != "")) {
+				gbString t = type_to_string(type);
+				error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+		}
+
+
+		if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) {
+			return false;
+		}
+
+		isize const arg_offset = 1;
+		auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
+		param_types[0] = t_objc_id;
+		param_types[1] = sel_type;
+
+		for (isize i = 2+arg_offset; i < ce->args.count; i++) {
+			Operand x = {};
+			check_expr(c, &x, ce->args[i]);
+			param_types[i-arg_offset] = x.type;
+		}
+
+		add_objc_proc_type(c, call, return_type, param_types);
+
+		return true;
+	} break;
+
+	case BuiltinProc_objc_find_selector: 
+	case BuiltinProc_objc_find_class: 
+	case BuiltinProc_objc_register_selector: 
+	case BuiltinProc_objc_register_class: 
+	{
+		String sel_name = {};
+		if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) {
+			return false;
+		}
+
+		switch (id) {
+		case BuiltinProc_objc_find_selector: 
+		case BuiltinProc_objc_register_selector: 
+			operand->type = t_objc_SEL;
+			break;
+		case BuiltinProc_objc_find_class: 
+		case BuiltinProc_objc_register_class: 
+			operand->type = t_objc_Class;
+			break;
+
+		}
+		operand->mode = Addressing_Value;
+
+		try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
+		try_to_add_package_dependency(c, "runtime", "sel_registerName");
+		try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
+		return true;
+	} break;
+	}
+}
 
 bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 	ast_node(ce, CallExpr, call);
@@ -179,6 +414,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_len:
 	case BuiltinProc_min:
 	case BuiltinProc_max:
+	case BuiltinProc_type_is_subtype_of:
+	case BuiltinProc_objc_send:
+	case BuiltinProc_objc_find_selector: 
+	case BuiltinProc_objc_find_class: 
+	case BuiltinProc_objc_register_selector: 
+	case BuiltinProc_objc_register_class: 
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		break;
 
@@ -202,7 +443,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 	}
 
-	String builtin_name = builtin_procs[id].name;;
+	String builtin_name = builtin_procs[id].name;
 
 
 	if (ce->args.count > 0) {
@@ -219,6 +460,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
 		break;
 
+	case BuiltinProc_objc_send:
+	case BuiltinProc_objc_find_selector: 
+	case BuiltinProc_objc_find_class: 
+	case BuiltinProc_objc_register_selector: 
+	case BuiltinProc_objc_register_class: 
+		return check_builtin_objc_procedure(c, operand, call, id, type_hint);
+
 	case BuiltinProc___entry_point:
 		operand->mode = Addressing_NoValue;
 		operand->type = nullptr;
@@ -858,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			gb_string_free(type_str);
@@ -870,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			return false;
 		}
 		if (sel.indirect) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
 			gb_string_free(type_str);
@@ -931,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			gb_string_free(type_str);
@@ -943,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			return false;
 		}
 		if (sel.indirect) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
 			gb_string_free(type_str);
@@ -3258,6 +3506,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			case TargetOs_linux:
 			case TargetOs_essence:
 			case TargetOs_freebsd:
+			case TargetOs_openbsd:
 				switch (build_context.metrics.arch) {
 				case TargetArch_i386:
 				case TargetArch_amd64:
@@ -3344,9 +3593,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_type_is_simple_compare:
 	case BuiltinProc_type_is_dereferenceable:
 	case BuiltinProc_type_is_valid_map_key:
+	case BuiltinProc_type_is_valid_matrix_elements:
 	case BuiltinProc_type_is_named:
 	case BuiltinProc_type_is_pointer:
 	case BuiltinProc_type_is_array:
+	case BuiltinProc_type_is_enumerated_array:
 	case BuiltinProc_type_is_slice:
 	case BuiltinProc_type_is_dynamic_array:
 	case BuiltinProc_type_is_map:
@@ -3354,10 +3605,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_type_is_union:
 	case BuiltinProc_type_is_enum:
 	case BuiltinProc_type_is_proc:
-	case BuiltinProc_type_is_bit_field:
-	case BuiltinProc_type_is_bit_field_value:
 	case BuiltinProc_type_is_bit_set:
 	case BuiltinProc_type_is_simd_vector:
+	case BuiltinProc_type_is_matrix:
 	case BuiltinProc_type_is_specialized_polymorphic_record:
 	case BuiltinProc_type_is_unspecialized_polymorphic_record:
 	case BuiltinProc_type_has_nil:
@@ -3725,6 +3975,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 		break;
 
+	case BuiltinProc_type_is_subtype_of:
+		{
+			Operand op_src = {};
+			Operand op_dst = {};
+
+			check_expr_or_type(c, &op_src, ce->args[0]);
+			if (op_src.mode != Addressing_Type) {
+				gbString e = expr_to_string(op_src.expr);
+				error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+				gb_string_free(e);
+				return false;
+			}
+			check_expr_or_type(c, &op_dst, ce->args[1]);
+			if (op_dst.mode != Addressing_Type) {
+				gbString e = expr_to_string(op_dst.expr);
+				error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+				gb_string_free(e);
+				return false;
+			}
+
+			operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+			operand->mode = Addressing_Constant;
+			operand->type = t_untyped_bool;
+		} break;
+
 	case BuiltinProc_type_field_index_of:
 		{
 			Operand op = {};
@@ -3814,6 +4089,20 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			operand->type = t_hasher_proc;
 			break;
 		}
+
+	case BuiltinProc_constant_utf16_cstring:
+		{
+			String value = {};
+			if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
+				return false;
+			}
+			operand->mode = Addressing_Value;
+			operand->type = alloc_type_multi_pointer(t_u16);
+			operand->value = {};
+			break;
+		}
+
+
 	}
 
 	return true;

+ 68 - 0
src/check_decl.cpp

@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
 		return;
 	}
 
+	if (is_type_proc(e->type)) {
+		error(e->token, "Illegal declaration of a constant procedure value");
+	}
+
 	e->parent_proc_decl = ctx->curr_proc_decl;
 
 	e->Constant.value = operand->value;
@@ -338,6 +342,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 	if (decl != nullptr) {
 		AttributeContext ac = {};
 		check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+		if (e->kind == Entity_TypeName && ac.objc_class != "") {
+			e->TypeName.objc_class_name = ac.objc_class;
+
+			if (type_size_of(e->type) > 0) {
+				error(e->token, "@(objc_class) marked type must be of zero size");
+			}
+		}
 	}
 
 
@@ -819,6 +830,63 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 	e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
 
+	if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
+		if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+			error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+		} else if (ac.objc_type == nullptr) {
+			error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+		} else if (ac.objc_name.len == 0 && ac.objc_type) {
+			error(e->token, "@(objc_name) is required with @(objc_type)");
+		} else {
+			Type *t = ac.objc_type;
+			if (t->kind == Type_Named) {
+				Entity *tn = t->Named.type_name;
+
+				GB_ASSERT(tn->kind == Entity_TypeName);
+
+				if (tn->scope != e->scope) {
+					error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+				} else {
+					mutex_lock(&global_type_name_objc_metadata_mutex);
+					defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+					if (!tn->TypeName.objc_metadata) {
+						tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+					}
+					auto *md = tn->TypeName.objc_metadata;
+					mutex_lock(md->mutex);
+					defer (mutex_unlock(md->mutex));
+
+					if (!ac.objc_is_class_method) {
+						bool ok = true;
+						for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+							if (entry.name == ac.objc_name) {
+								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+								ok = false;
+								break;
+							}
+						}
+						if (ok) {
+							array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+						}
+					} else {
+						bool ok = true;
+						for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+							if (entry.name == ac.objc_name) {
+								error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+								ok = false;
+								break;
+							}
+						}
+						if (ok) {
+							array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+						}
+					}
+				}
+			}
+		}
+	}
+
 
 	switch (e->Procedure.optimization_mode) {
 	case ProcedureOptimizationMode_None:

+ 73 - 44
src/check_expr.cpp

@@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
 	}
 }
 
+void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
+	if (e->kind != Entity_TypeName) {
+		return;
+	}
+	if (e->TypeName.objc_metadata == nullptr) {
+		return;
+	}
+	TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
+	Type *t = base_type(e->type);
+	GB_ASSERT(t->kind == Type_Struct);
+
+	if (is_type) {
+		for_array(i, objc_metadata->type_entries) {
+			String name = objc_metadata->type_entries[i].name;
+			string_set_add(set, name);
+		}
+	} else {
+		for_array(i, objc_metadata->value_entries) {
+			String name = objc_metadata->value_entries[i].name;
+			string_set_add(set, name);
+		}
+	}
+
+	for_array(i, t->Struct.fields) {
+		Entity *f = t->Struct.fields[i];
+		if (f->flags & EntityFlag_Using && f->type != nullptr) {
+			if (f->type->kind == Type_Named && f->type->Named.type_name) {
+				populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
+			}
+		}
+	}
+}
+
+
+void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+	ERROR_BLOCK();
+	GB_ASSERT(e->kind == Entity_TypeName);
+	GB_ASSERT(e->TypeName.objc_metadata != nullptr);
+	auto *objc_metadata = e->TypeName.objc_metadata;
+	mutex_lock(objc_metadata->mutex);
+	defer (mutex_unlock(objc_metadata->mutex));
+
+	StringSet set = {};
+	string_set_init(&set, heap_allocator());
+	defer (string_set_destroy(&set));
+	populate_check_did_you_mean_objc_entity(&set, e, is_type);
+
+
+	DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
+	defer (did_you_mean_destroy(&d));
+	for_array(i, set.entries) {
+		did_you_mean_append(&d, set.entries[i].value);
+	}
+	check_did_you_mean_print(&d, prefix);
+}
+
 void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
 	ERROR_BLOCK();
 
@@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array<Entity *> const &fields,
 	check_did_you_mean_print(&d, prefix);
 }
 
+
 void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
 	ERROR_BLOCK();
 
@@ -228,42 +285,6 @@ void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reser
 	}
 }
 
-
-isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
-	Type *prev_src = src;
-	src = type_deref(src);
-	if (!src_is_ptr) {
-		src_is_ptr = src != prev_src;
-	}
-	src = base_type(src);
-
-	if (!is_type_struct(src)) {
-		return 0;
-	}
-
-	for_array(i, src->Struct.fields) {
-		Entity *f = src->Struct.fields[i];
-		if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
-			continue;
-		}
-
-		if (are_types_identical(f->type, dst)) {
-			return level+1;
-		}
-		if (src_is_ptr && is_type_pointer(dst)) {
-			if (are_types_identical(f->type, type_deref(dst))) {
-				return level+1;
-			}
-		}
-		isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
-		if (nested_level > 0) {
-			return nested_level;
-		}
-	}
-
-	return 0;
-}
-
 bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
                                             Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
 	///////////////////////////////////////////////////////////////////////////////
@@ -4449,14 +4470,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 
 	if (entity == nullptr) {
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
 
 		if (operand->type != nullptr && selector->kind == Ast_Ident) {
 			String const &name = selector->Ident.token.string;
 			Type *bt = base_type(operand->type);
-			if (bt->kind == Type_Struct) {
+			if (operand->type->kind == Type_Named &&
+			    operand->type->Named.type_name &&
+			    operand->type->Named.type_name->kind == Entity_TypeName &&
+			    operand->type->Named.type_name->TypeName.objc_metadata) {
+				check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+			} else if (bt->kind == Type_Struct) {
 				check_did_you_mean_type(name, bt->Struct.fields);
 			} else if (bt->kind == Type_Enum) {
 				check_did_you_mean_type(name, bt->Enum.fields);
@@ -4485,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 		}
 
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
 		gb_string_free(sel_str);
@@ -4510,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 		}
 
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
 		gb_string_free(sel_str);
@@ -4523,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 
 	if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
 		gb_string_free(sel_str);
@@ -6432,10 +6458,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
 		return builtin_procs[id].kind;
 	}
 
-	Entity *e = entity_of_node(operand->expr);
+	Entity *initial_entity = entity_of_node(operand->expr);
 
-	if (e != nullptr && e->kind == Entity_Procedure) {
-		if (e->Procedure.deferred_procedure.entity != nullptr) {
+	if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) {
+		if (initial_entity->Procedure.deferred_procedure.entity != nullptr) {
 			call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure;
 		}
 	}
@@ -9810,6 +9836,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
 		if (f->flags&FieldFlag_const) {
 			str = gb_string_appendc(str, "#const ");
 		}
+		if (f->flags&FieldFlag_subtype) {
+			str = gb_string_appendc(str, "#subtype ");
+		}
 
 		for_array(i, f->names) {
 			Ast *name = f->names[i];

+ 41 - 0
src/check_type.cpp

@@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 		}
 
 		bool is_using = (p->flags&FieldFlag_using) != 0;
+		bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
 
 		for_array(j, p->names) {
 			Ast *name = p->names[j];
@@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 			Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
 			add_entity(ctx, ctx->scope, name, field);
 			field->Variable.field_group_index = field_group_index;
+			if (is_subtype) {
+				field->flags |= EntityFlag_Subtype;
+			}
 
 			if (j == 0) {
 				field->Variable.docs = docs;
@@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 
 			populate_using_entity_scope(ctx, node, p, type);
 		}
+
+		if (is_subtype && p->names.count > 0) {
+			Type *first_type = fields_array[fields_array.count-1]->type;
+			Type *t = base_type(type_deref(first_type));
+
+			if (!does_field_type_allow_using(t) &&
+			    p->names.count >= 1 &&
+			    p->names[0]->kind == Ast_Ident) {
+				Token name_token = p->names[0]->Ident.token;
+				gbString type_str = type_to_string(first_type);
+				error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
+				gb_string_free(type_str);
+			}
+		}
 	}
 	
 	*fields = slice_from_array(fields_array);
@@ -323,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
 	}
 
 	named_type->Named.type_name = e;
+	GB_ASSERT(original_type->kind == Type_Named);
+	e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
+	// 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);
 	auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
@@ -1906,6 +1928,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 		c->scope->flags &= ~ScopeFlag_ContextDefined;
 	}
 
+	TargetArchKind arch = build_context.metrics.arch;
+	switch (cc) {
+	case ProcCC_StdCall:
+	case ProcCC_FastCall:
+		if (arch != TargetArch_i386 && arch != TargetArch_amd64) {
+			error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s",
+			      proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+		}
+		break;
+	case ProcCC_Win64:
+	case ProcCC_SysV:
+		if (arch != TargetArch_amd64) {
+			error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s",
+			      proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+		}
+		break;
+	}
+
+
 	bool variadic = false;
 	isize variadic_index = -1;
 	bool success = true;

+ 136 - 11
src/checker.cpp

@@ -4,7 +4,7 @@
 void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
 void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
 void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
-
+Type *check_type(CheckerContext *ctx, Ast *e);
 
 bool is_operand_value(Operand o) {
 	switch (o.mode) {
@@ -225,8 +225,8 @@ bool decl_info_has_init(DeclInfo *d) {
 Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
 	Scope *s = gb_alloc_item(permanent_allocator(), Scope);
 	s->parent = parent;
-	string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
-	ptr_set_init(&s->imported, permanent_allocator(), 0);
+	string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
+	ptr_set_init(&s->imported, heap_allocator(), 0);
 	mutex_init(&s->mutex);
 
 	if (parent != nullptr && parent != builtin_pkg->scope) {
@@ -733,12 +733,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
 	String n = make_string_c(name);
 	AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
 	Entity *e = scope_lookup(p->scope, n);
-	e->flags |= EntityFlag_Used;
 	GB_ASSERT_MSG(e != nullptr, "%s", name);
 	GB_ASSERT(c->decl != nullptr);
+	e->flags |= EntityFlag_Used;
+	add_dependency(c->info, c->decl, e);
+}
+
+void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+	String n = make_string_c(name);
+	AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
+	Entity *e = scope_lookup(p->scope, n);
+	if (e == nullptr) {
+		return;
+	}
+	GB_ASSERT(c->decl != nullptr);
+	e->flags |= EntityFlag_Used;
 	add_dependency(c->info, c->decl, e);
 }
 
+
 void add_declaration_dependency(CheckerContext *c, Entity *e) {
 	if (e == nullptr) {
 		return;
@@ -841,6 +854,17 @@ void add_global_enum_constant(Slice<Entity *> const &fields, char const *name, i
 	GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
 }
 
+Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) {
+	Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+	Type *named_type = alloc_type_named(type_name, backing_type, e);
+	e->type = named_type;
+	set_base_type(named_type, backing_type);
+	if (scope_insert(scope, e)) {
+		compiler_error("double declaration of %.*s", LIT(e->token.string));
+	}
+	return named_type;
+}
+
 
 void init_universal(void) {
 	BuildContext *bc = &build_context;
@@ -870,11 +894,43 @@ void init_universal(void) {
 	add_global_bool_constant("false", false);
 
 	// TODO(bill): Set through flags in the compiler
-	add_global_string_constant("ODIN_OS",      bc->ODIN_OS);
-	add_global_string_constant("ODIN_ARCH",    bc->ODIN_ARCH);
 	add_global_string_constant("ODIN_VENDOR",  bc->ODIN_VENDOR);
 	add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
 	add_global_string_constant("ODIN_ROOT",    bc->ODIN_ROOT);
+
+	{
+		GlobalEnumValue values[TargetOs_COUNT] = {
+			{"Unknown",      TargetOs_Invalid},
+			{"Windows",      TargetOs_windows},
+			{"Darwin",       TargetOs_darwin},
+			{"Linux",        TargetOs_linux},
+			{"Essence",      TargetOs_essence},
+			{"FreeBSD",      TargetOs_freebsd},
+			{"OpenBSD",      TargetOs_openbsd},
+			{"WASI",         TargetOs_wasi},
+			{"JS",           TargetOs_js},
+			{"Freestanding", TargetOs_freestanding},
+		};
+
+		auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
+		add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+		add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
+	}
+
+	{
+		GlobalEnumValue values[TargetArch_COUNT] = {
+			{"Unknown", TargetArch_Invalid},
+			{"amd64",   TargetArch_amd64},
+			{"i386",    TargetArch_i386},
+			{"arm64",   TargetArch_arm64},
+			{"wasm32",  TargetArch_wasm32},
+			{"wasm64",  TargetArch_wasm64},
+		};
+
+		auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
+		add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
+		add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
+	}
 	
 	{
 		GlobalEnumValue values[BuildMode_COUNT] = {
@@ -889,7 +945,6 @@ void init_universal(void) {
 		add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode);
 	}
 
-	add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
 	{
 		GlobalEnumValue values[TargetEndian_COUNT] = {
 			{"Unknown", TargetEndian_Invalid},
@@ -900,6 +955,7 @@ void init_universal(void) {
 
 		auto fields = add_global_enum_type(str_lit("Odin_Endian_Type"), values, gb_count_of(values));
 		add_global_enum_constant(fields, "ODIN_ENDIAN", target_endians[bc->metrics.arch]);
+		add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
 	}
 
 	{
@@ -985,6 +1041,17 @@ void init_universal(void) {
 	t_f64_ptr      = alloc_type_pointer(t_f64);
 	t_u8_slice     = alloc_type_slice(t_u8);
 	t_string_slice = alloc_type_slice(t_string);
+
+	// intrinsics types for objective-c stuff
+	{
+		t_objc_object   = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"),   alloc_type_struct());
+		t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
+		t_objc_class    = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"),    alloc_type_struct());
+
+		t_objc_id       = alloc_type_pointer(t_objc_object);
+		t_objc_SEL      = alloc_type_pointer(t_objc_selector);
+		t_objc_Class    = alloc_type_pointer(t_objc_class);
+	}
 }
 
 
@@ -1041,6 +1108,9 @@ void init_checker_info(CheckerInfo *i) {
 	semaphore_init(&i->collect_semaphore);
 
 	mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
+
+	mutex_init(&i->objc_types_mutex);
+	map_init(&i->objc_msgSend_types, a);
 }
 
 void destroy_checker_info(CheckerInfo *i) {
@@ -1073,6 +1143,9 @@ void destroy_checker_info(CheckerInfo *i) {
 	mutex_destroy(&i->type_and_value_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
 	mutex_destroy(&i->foreign_mutex);
+
+	mutex_destroy(&i->objc_types_mutex);
+	map_destroy(&i->objc_msgSend_types);
 }
 
 CheckerContext make_checker_context(Checker *c) {
@@ -2712,6 +2785,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
 	return ev;
 }
 
+Type *check_decl_attribute_type(CheckerContext *c, Ast *value) {
+	if (value != nullptr) {
+		return check_type(c, value);
+	}
+	return nullptr;
+}
+
+
 #define ATTRIBUTE_USER_TAG_NAME "tag"
 
 
@@ -3011,6 +3092,42 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 			error(elem, "Expected a string for '%.*s'", LIT(name));
 		}
 		return true;
+	} else if (name == "objc_name") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_String) {
+			if (string_is_valid_identifier(ev.value_string)) {
+				ac->objc_name = ev.value_string;
+			} else {
+				error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+			}
+		} else {
+			error(elem, "Expected a string value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_is_class_method") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_Bool) {
+			ac->objc_is_class_method = ev.value_bool;
+		} else {
+			error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+		}
+		return true;
+	} else if (name == "objc_type") {
+		if (value == nullptr) {
+			error(elem, "Expected a type for '%.*s'", LIT(name));
+		} else {
+			Type *objc_type = check_type(c, value);
+			if (objc_type != nullptr) {
+				if (!has_type_got_objc_class_attribute(objc_type)) {
+					gbString t = type_to_string(objc_type);
+					error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
+					gb_string_free(t);
+				} else {
+					ac->objc_type = objc_type;
+				}
+			}
+		}
+		return true;
 	}
 	return false;
 }
@@ -3161,6 +3278,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
 	} else if (name == "private") {
 		// NOTE(bill): Handled elsewhere `check_collect_value_decl`
 		return true;
+	} else if (name == "objc_class") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String || ev.value_string == "") {
+			error(elem, "Expected a non-empty string value for '%.*s'", LIT(name));
+		} else {
+			ac->objc_class = ev.value_string;
+		}
+		return true;
 	}
 	return false;
 }
@@ -3477,11 +3602,11 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 	if (entity_visibility_kind == EntityVisiblity_Public &&
 	    (c->scope->flags&ScopeFlag_File) &&
 	    c->scope->file) {
-	    	if (c->scope->file->flags & AstFile_IsPrivatePkg) {
-			entity_visibility_kind = EntityVisiblity_PrivateToPackage;
-	    	} else if (c->scope->file->flags & AstFile_IsPrivateFile) {
+	    	if (c->scope->file->flags & AstFile_IsPrivateFile) {
 			entity_visibility_kind = EntityVisiblity_PrivateToFile;
-		}
+		} else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+			entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+	    	}
 	}
 
 	if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {

+ 18 - 1
src/checker.hpp

@@ -118,6 +118,11 @@ struct AttributeContext {
 	bool    init                : 1;
 	bool    set_cold            : 1;
 	u32 optimization_mode; // ProcedureOptimizationMode
+
+	String  objc_class;
+	String  objc_name;
+	bool    objc_is_class_method;
+	Type *  objc_type;
 };
 
 AttributeContext make_attribute_context(String link_prefix) {
@@ -267,6 +272,17 @@ struct UntypedExprInfo {
 typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 
 typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
 
+enum ObjcMsgKind : u32 {
+	ObjcMsg_normal,
+	ObjcMsg_fpret,
+	ObjcMsg_fp2ret,
+	ObjcMsg_stret,
+};
+struct ObjcMsgData {
+	ObjcMsgKind kind;
+	Type *proc_type;
+};
+
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 	Checker *checker;
@@ -340,7 +356,8 @@ struct CheckerInfo {
 
 	MPMCQueue<Ast *> intrinsics_entry_point_usage;
 
-
+	BlockingMutex objc_types_mutex;
+	PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
 };
 
 struct CheckerContext {

+ 23 - 4
src/checker_builtin_procs.hpp

@@ -213,8 +213,6 @@ BuiltinProc__type_simple_boolean_begin,
 	BuiltinProc_type_is_union,
 	BuiltinProc_type_is_enum,
 	BuiltinProc_type_is_proc,
-	BuiltinProc_type_is_bit_field,
-	BuiltinProc_type_is_bit_field_value,
 	BuiltinProc_type_is_bit_set,
 	BuiltinProc_type_is_simd_vector,
 	BuiltinProc_type_is_matrix,
@@ -243,6 +241,8 @@ BuiltinProc__type_simple_boolean_end,
 	BuiltinProc_type_polymorphic_record_parameter_count,
 	BuiltinProc_type_polymorphic_record_parameter_value,
 
+	BuiltinProc_type_is_subtype_of,
+
 	BuiltinProc_type_field_index_of,
 
 	BuiltinProc_type_equal_proc,
@@ -252,6 +252,15 @@ BuiltinProc__type_end,
 
 	BuiltinProc___entry_point,
 
+	BuiltinProc_objc_send,
+	BuiltinProc_objc_find_selector,
+	BuiltinProc_objc_find_class,
+	BuiltinProc_objc_register_selector,
+	BuiltinProc_objc_register_class,
+
+	BuiltinProc_constant_utf16_cstring,
+
+
 	BuiltinProc_COUNT,
 };
 gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -466,8 +475,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_is_union"),             1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_enum"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_proc"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-	{STR_LIT("type_is_bit_field"),         1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-	{STR_LIT("type_is_bit_field_value"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_bit_set"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_simd_vector"),       1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_matrix"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -495,6 +502,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("type_equal_proc"),  1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -504,4 +513,14 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("objc_send"),   3, true,  Expr_Expr, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("objc_find_selector"),     1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("objc_find_class"),        1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("objc_register_class"),    1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 };

+ 2 - 2
src/common.cpp

@@ -848,7 +848,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 
 	return ReadDirectory_None;
 }
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
 
 #include <dirent.h>
 
@@ -1021,7 +1021,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
 	#endif
 	}
 	
-	gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
+	gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
 
 	if (fc.size > I32_MAX) {
 		err = LoadedFile_FileTooLarge;

+ 17 - 2
src/common_memory.cpp

@@ -139,6 +139,7 @@ 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);
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
 
 	PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
 		PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
-		GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+		if (pmblock == nullptr) {
+			gb_printf_err("Out of Virtual memory, oh no...\n");
+			gb_printf_err("Requested: %lld bytes\n", cast(long long)total_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;
 		return pmblock;
 	}
 	void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+		global_platform_memory_total_usage -= block->total_size;
 		GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
 	}
 	void platform_virtual_memory_protect(void *memory, isize size) {
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
 	
 	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);
-		GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+		if (pmblock == nullptr) {
+			gb_printf_err("Out of Virtual memory, oh no...\n");
+			gb_printf_err("Requested: %lld bytes\n", cast(long long)total_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;
 		return pmblock;
 	}
 	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) {

+ 1 - 34
src/docs_writer.cpp

@@ -683,40 +683,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 			types[1] = odin_doc_type(w, type->Proc.results);
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 
-			String calling_convention = {};
-			switch (type->Proc.calling_convention) {
-			case ProcCC_Invalid:
-				// no need
-				break;
-			case ProcCC_Odin:
-				if (default_calling_convention() != ProcCC_Odin) {
-					calling_convention = str_lit("odin");
-				}
-				break;
-			case ProcCC_Contextless:
-				if (default_calling_convention() != ProcCC_Contextless) {
-					calling_convention = str_lit("contextless");
-				}
-				break;
-			case ProcCC_CDecl:
-				calling_convention = str_lit("cdecl");
-				break;
-			case ProcCC_StdCall:
-				calling_convention = str_lit("stdcall");
-				break;
-			case ProcCC_FastCall:
-				calling_convention = str_lit("fastcall");
-				break;
-			case ProcCC_None:
-				calling_convention = str_lit("none");
-				break;
-			case ProcCC_Naked:
-				calling_convention = str_lit("naked");
-				break;
-			case ProcCC_InlineAsm:
-				calling_convention = str_lit("inline-assembly");
-				break;
-			}
+			String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
 			doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
 		}
 		break;

+ 29 - 0
src/entity.cpp

@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
 
 	EntityFlag_Test          = 1ull<<30,
 	EntityFlag_Init          = 1ull<<31,
+	EntityFlag_Subtype       = 1ull<<32,
 	
 	EntityFlag_CustomLinkName = 1ull<<40,
 	EntityFlag_CustomLinkage_Internal = 1ull<<41,
@@ -86,6 +87,10 @@ enum EntityFlag : u64 {
 	EntityFlag_Overridden    = 1ull<<63,
 };
 
+enum : u64 {
+	EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
+};
+
 enum EntityState : u32 {
 	EntityState_Unresolved = 0,
 	EntityState_InProgress = 1,
@@ -122,6 +127,28 @@ enum ProcedureOptimizationMode : u32 {
 	ProcedureOptimizationMode_Speed,
 };
 
+
+BlockingMutex global_type_name_objc_metadata_mutex;
+
+struct TypeNameObjCMetadataEntry {
+	String name;
+	Entity *entity;
+};
+struct TypeNameObjCMetadata {
+	BlockingMutex *mutex;
+	Array<TypeNameObjCMetadataEntry> type_entries;
+	Array<TypeNameObjCMetadataEntry> value_entries;
+};
+
+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;
+}
+
 // An Entity is a named "thing" in the language
 struct Entity {
 	EntityKind  kind;
@@ -186,6 +213,8 @@ struct Entity {
 			Type * type_parameter_specialization;
 			String ir_mangled_name;
 			bool   is_type_alias;
+			String objc_class_name;
+			TypeNameObjCMetadata *objc_metadata;
 		} TypeName;
 		struct {
 			u64     tags;

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.