aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2023-02-28 12:15:54 +0000
committerGitHub <noreply@github.com>2023-02-28 12:15:54 +0000
commit9afd9f9beae310d2a3bea98cd713b22d2f167cf9 (patch)
tree1f5ed60d50eeb282ae4dd451dfe75fab73e32052 /core
parenteb60ec3899922b6b98a5ee1a00766d5d9b9917e1 (diff)
parentc8d3a9121bbed1cff1fee45d6ecf0fa4748f4d21 (diff)
Merge branch 'master' into new-temp-allocator
Diffstat (limited to 'core')
-rw-r--r--core/crypto/rand_linux.odin2
-rw-r--r--core/intrinsics/intrinsics.odin4
-rw-r--r--core/os/os_darwin.odin72
-rw-r--r--core/os/os_linux.odin227
-rw-r--r--core/prof/spall/spall.odin208
-rw-r--r--core/runtime/core.odin7
-rw-r--r--core/runtime/default_temporary_allocator.odin5
-rw-r--r--core/runtime/internal.odin21
-rw-r--r--core/runtime/print.odin39
-rw-r--r--core/sync/primitives.odin2
-rw-r--r--core/sys/darwin/xnu_system_call_numbers.odin2
-rw-r--r--core/sys/darwin/xnu_system_call_wrappers.odin96
-rw-r--r--core/sys/unix/syscalls_linux.odin192
-rw-r--r--core/sys/windows/kernel32.odin7
-rw-r--r--core/time/perf.odin51
-rw-r--r--core/time/tsc_darwin.odin21
-rw-r--r--core/time/tsc_freebsd.odin21
-rw-r--r--core/time/tsc_linux.odin35
-rw-r--r--core/time/tsc_openbsd.odin7
-rw-r--r--core/time/tsc_windows.odin7
20 files changed, 737 insertions, 289 deletions
diff --git a/core/crypto/rand_linux.odin b/core/crypto/rand_linux.odin
index 4d1183757..e5c194220 100644
--- a/core/crypto/rand_linux.odin
+++ b/core/crypto/rand_linux.odin
@@ -12,7 +12,7 @@ _rand_bytes :: proc (dst: []byte) {
for l > 0 {
to_read := min(l, _MAX_PER_CALL_BYTES)
- ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
+ ret := unix.sys_getrandom(raw_data(dst), uint(to_read), 0)
if ret < 0 {
switch os.Errno(-ret) {
case os.EINTR:
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 38542d2fc..890a6881d 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -283,7 +283,7 @@ wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
// x86 Targets (i386, amd64)
-x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
+x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
@@ -305,4 +305,4 @@ valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2,
// Internal compiler use only
-__entry_point :: proc() --- \ No newline at end of file
+__entry_point :: proc() ---
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index 838882404..c16cf737e 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -277,8 +277,10 @@ foreign libc {
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
+ @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
+ @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
+ @(link_name="pread") _unix_pread :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
+ @(link_name="pwrite") _unix_pwrite :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---
@@ -386,45 +388,51 @@ close :: proc(fd: Handle) -> bool {
@(private)
MAX_RW :: 0x7fffffff // The limit on Darwin is max(i32), trying to read/write more than that fails.
-write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
- assert(fd != -1)
-
- bytes_total := len(data)
- bytes_written_total := 0
-
- for bytes_written_total < bytes_total {
- bytes_to_write := min(bytes_total - bytes_written_total, MAX_RW)
- slice := data[bytes_written_total:bytes_written_total + bytes_to_write]
- bytes_written := _unix_write(fd, raw_data(slice), bytes_to_write)
- if bytes_written == -1 {
- return bytes_written_total, 1
- }
- bytes_written_total += bytes_written
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
}
- return bytes_written_total, 0
+ bytes_written := _unix_write(fd, raw_data(data), c.size_t(len(data)))
+ if bytes_written < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_written, ERROR_NONE
}
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
- assert(fd != -1)
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := _unix_read(fd, raw_data(data), c.size_t(len(data)))
+ if bytes_read < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_read, ERROR_NONE
+}
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
- bytes_total := len(data)
- bytes_read_total := 0
+ bytes_read := _unix_pread(fd, raw_data(data), c.size_t(len(data)), offset)
+ if bytes_read < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_read, ERROR_NONE
+}
- for bytes_read_total < bytes_total {
- bytes_to_read := min(bytes_total - bytes_read_total, MAX_RW)
- slice := data[bytes_read_total:bytes_read_total + bytes_to_read]
- bytes_read := _unix_read(fd, raw_data(slice), bytes_to_read)
- if bytes_read == -1 {
- return bytes_read_total, 1
- }
- if bytes_read == 0 {
- break
- }
- bytes_read_total += bytes_read
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
}
- return bytes_read_total, 0
+ bytes_written := _unix_pwrite(fd, raw_data(data), c.size_t(len(data)), offset)
+ if bytes_written < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 796298377..237e3495c 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -270,136 +270,6 @@ AT_FDCWD :: ~uintptr(99) /* -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 {
- when ODIN_ARCH != .arm64 {
- res := int(intrinsics.syscall(unix.SYS_fork))
- } else {
- res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD))
- }
- return -1 if res < 0 else Pid(res)
-}
-
-_unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle {
- 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
- }
- return -1 if res < 0 else Handle(res)
-}
-
-_unix_close :: proc(fd: Handle) -> int {
- return int(intrinsics.syscall(unix.SYS_close, uintptr(fd)))
-}
-
-_unix_read :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
-}
-
-_unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
-}
-
-_unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
- 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)
- high := uintptr(offset >> 32)
- result: i64
- res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
- return -1 if res < 0 else result
- }
-}
-
-_unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == .amd64 {
- return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
- } 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
- }
-}
-
-_unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
- 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)))
- }
-}
-
-_unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == .amd64 {
- return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
- } 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
- }
-}
-
-_unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
- 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
- }
-}
-
-_unix_access :: proc(path: cstring, mask: int) -> int {
- 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
- }
-}
-
-_unix_getcwd :: proc(buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_getcwd, uintptr(buf), uintptr(size)))
-}
-
-_unix_chdir :: proc(path: cstring) -> int {
- return int(intrinsics.syscall(unix.SYS_chdir, uintptr(rawptr(path))))
-}
-
-_unix_rename :: proc(old, new: cstring) -> int {
- 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, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
- }
-}
-
-_unix_unlink :: proc(path: cstring) -> int {
- 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, AT_FDCWD, uintptr(rawptr(path)), 0))
- }
-}
-
-_unix_rmdir :: proc(path: cstring) -> int {
- 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, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
- }
-}
-
-_unix_mkdir :: proc(path: cstring, mode: u32) -> int {
- 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, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
- }
-}
-
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
@@ -415,6 +285,7 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+ @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@@ -447,7 +318,7 @@ get_last_error :: proc "contextless" () -> int {
}
personality :: proc(persona: u64) -> (Errno) {
- res := _unix_personality(persona)
+ res := unix.sys_personality(persona)
if res == -1 {
return _get_errno(res)
}
@@ -455,29 +326,47 @@ personality :: proc(persona: u64) -> (Errno) {
}
fork :: proc() -> (Pid, Errno) {
- pid := _unix_fork()
+ pid := unix.sys_fork()
if pid == -1 {
- return -1, _get_errno(int(pid))
+ return -1, _get_errno(pid)
}
- return pid, ERROR_NONE
+ return Pid(pid), ERROR_NONE
}
-open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+execvp :: proc(path: string, args: []string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator)
+ args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator)
+ for i := 0; i < len(args); i += 1 {
+ args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator)
+ }
+
+ _unix_execvp(path_cstr, raw_data(args_cstrs))
+ return Errno(get_last_error())
+}
+
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- handle := _unix_open(cstr, flags, mode)
+ handle := unix.sys_open(cstr, flags, uint(mode))
if handle < 0 {
- return INVALID_HANDLE, _get_errno(int(handle))
+ return INVALID_HANDLE, _get_errno(handle)
}
- return handle, ERROR_NONE
+ return Handle(handle), ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- return _get_errno(_unix_close(fd))
+ return _get_errno(unix.sys_close(int(fd)))
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
- bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := unix.sys_read(int(fd), raw_data(data), len(data))
if bytes_read < 0 {
return -1, _get_errno(bytes_read)
}
@@ -488,25 +377,49 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], uint(len(data)))
+
+ bytes_written := unix.sys_write(int(fd), raw_data(data), len(data))
+ if bytes_written < 0 {
+ return -1, _get_errno(bytes_written)
+ }
+ return bytes_written, ERROR_NONE
+}
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := unix.sys_pread(int(fd), raw_data(data), len(data), offset)
+ if bytes_read < 0 {
+ return -1, _get_errno(bytes_read)
+ }
+ return bytes_read, ERROR_NONE
+}
+
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_written := unix.sys_pwrite(int(fd), raw_data(data), uint(len(data)), offset)
if bytes_written < 0 {
return -1, _get_errno(bytes_written)
}
- return int(bytes_written), ERROR_NONE
+ return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, whence)
+ res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 {
return -1, _get_errno(int(res))
}
- return res, ERROR_NONE
+ return i64(res), ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_fstat(fd, &s)
+ result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return 0, _get_errno(result)
}
@@ -517,25 +430,25 @@ rename :: proc(old_path, new_path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
- return _get_errno(_unix_rename(old_path_cstr, new_path_cstr))
+ return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr))
}
remove :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_unlink(path_cstr))
+ return _get_errno(unix.sys_unlink(path_cstr))
}
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_mkdir(path_cstr, mode))
+ return _get_errno(unix.sys_mkdir(path_cstr, uint(mode)))
}
remove_directory :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_rmdir(path_cstr))
+ return _get_errno(unix.sys_rmdir(path_cstr))
}
is_file_handle :: proc(fd: Handle) -> bool {
@@ -589,7 +502,7 @@ is_dir :: proc {is_dir_path, is_dir_handle}
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
- res := _unix_access(cpath, O_RDONLY)
+ res := unix.sys_access(cpath, O_RDONLY)
return res == 0
}
@@ -628,7 +541,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_stat(cstr, &s)
+ result := unix.sys_stat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -642,7 +555,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_lstat(cstr, &s)
+ result := unix.sys_lstat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -653,7 +566,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_fstat(fd, &s)
+ result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return s, _get_errno(result)
}
@@ -711,7 +624,7 @@ _readlink :: proc(path: string) -> (string, Errno) {
bufsz : uint = 256
buf := make([]byte, bufsz)
for {
- rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz)
if rc < 0 {
delete(buf)
return "", _get_errno(rc)
@@ -760,7 +673,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
access :: proc(path: string, mask: int) -> (bool, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- result := _unix_access(cstr, mask)
+ result := unix.sys_access(cstr, mask)
if result < 0 {
return false, _get_errno(result)
}
@@ -831,7 +744,7 @@ get_current_directory :: proc() -> string {
page_size := get_page_size()
buf := make([dynamic]u8, page_size)
for {
- #no_bounds_check res := _unix_getcwd(&buf[0], uint(len(buf)))
+ #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
@@ -848,7 +761,7 @@ get_current_directory :: proc() -> string {
set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- res := _unix_chdir(cstr)
+ res := unix.sys_chdir(cstr)
if res < 0 {
return _get_errno(res)
}
diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin
new file mode 100644
index 000000000..ff8c69222
--- /dev/null
+++ b/core/prof/spall/spall.odin
@@ -0,0 +1,208 @@
+package prof_spall
+
+import "core:os"
+import "core:time"
+import "core:intrinsics"
+import "core:mem"
+
+// File Format
+
+MANUAL_MAGIC :: u64le(0x0BADF00D)
+
+Manual_Header :: struct #packed {
+ magic: u64le,
+ version: u64le,
+ timestamp_scale: f64le,
+ reserved: u64le,
+}
+
+Manual_Event_Type :: enum u8 {
+ Invalid = 0,
+
+ Begin = 3,
+ End = 4,
+ Instant = 5,
+
+ Pad_Skip = 7,
+}
+
+Begin_Event :: struct #packed {
+ type: Manual_Event_Type,
+ category: u8,
+ pid: u32le,
+ tid: u32le,
+ ts: f64le,
+ name_len: u8,
+ args_len: u8,
+}
+BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255
+
+End_Event :: struct #packed {
+ type: Manual_Event_Type,
+ pid: u32le,
+ tid: u32le,
+ ts: f64le,
+}
+
+Pad_Skip :: struct #packed {
+ type: Manual_Event_Type,
+ size: u32le,
+}
+
+// User Interface
+
+Context :: struct {
+ precise_time: bool,
+ timestamp_scale: f64,
+ fd: os.Handle,
+}
+
+Buffer :: struct {
+ data: []u8,
+ head: int,
+ tid: u32,
+ pid: u32,
+}
+
+BUFFER_DEFAULT_SIZE :: 0x10_0000
+
+
+context_create :: proc(filename: string) -> (ctx: Context, ok: bool) #optional_ok {
+ fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600)
+ if err != os.ERROR_NONE {
+ return
+ }
+ ctx.fd = fd
+
+ freq, freq_ok := time.tsc_frequency()
+ ctx.precise_time = freq_ok
+ ctx.timestamp_scale = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1
+
+ temp := [size_of(Manual_Header)]u8{}
+ _build_header(temp[:], ctx.timestamp_scale)
+ os.write(ctx.fd, temp[:])
+ ok = true
+ return
+}
+
+context_destroy :: proc(ctx: ^Context) {
+ if ctx == nil {
+ return
+ }
+
+ os.close(ctx.fd)
+ ctx^ = Context{}
+}
+
+buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok {
+ assert(len(data) > 0)
+ buffer.data = data
+ buffer.tid = tid
+ buffer.pid = pid
+ buffer.head = 0
+ ok = true
+ return
+}
+
+buffer_flush :: proc(ctx: ^Context, buffer: ^Buffer) {
+ os.write(ctx.fd, buffer.data[:buffer.head])
+ buffer.head = 0
+}
+
+buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) {
+ buffer_flush(ctx, buffer)
+
+ buffer^ = Buffer{}
+}
+
+
+
+@(deferred_in=_scoped_buffer_end)
+SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool {
+ _buffer_begin(ctx, buffer, name, args, location)
+ return true
+}
+
+@(private)
+_scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) {
+ _buffer_end(ctx, buffer)
+}
+
+
+_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
+ if !ctx.precise_time {
+ return f64(time.tick_now()._nsec) / 1_000
+ }
+
+ return f64(intrinsics.read_cycle_counter())
+}
+
+_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
+ header_size = size_of(Manual_Header)
+ if header_size > len(buffer) {
+ return 0, false
+ }
+
+ hdr := (^Manual_Header)(raw_data(buffer))
+ hdr.magic = MANUAL_MAGIC
+ hdr.version = 1
+ hdr.timestamp_scale = f64le(timestamp_scale)
+ hdr.reserved = 0
+ ok = true
+ return
+}
+
+_build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
+ ev := (^Begin_Event)(raw_data(buffer))
+ name_len := min(len(name), 255)
+ args_len := min(len(args), 255)
+
+ event_size = size_of(Begin_Event) + name_len + args_len
+ if event_size > len(buffer) {
+ return 0, false
+ }
+
+ ev.type = .Begin
+ ev.pid = u32le(pid)
+ ev.tid = u32le(tid)
+ ev.ts = f64le(ts)
+ ev.name_len = u8(name_len)
+ ev.args_len = u8(args_len)
+ mem.copy(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
+ mem.copy(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len)
+ ok = true
+ return
+}
+
+_build_end :: proc(buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
+ ev := (^End_Event)(raw_data(buffer))
+ event_size = size_of(End_Event)
+ if event_size > len(buffer) {
+ return 0, false
+ }
+
+ ev.type = .End
+ ev.pid = u32le(pid)
+ ev.tid = u32le(tid)
+ ev.ts = f64le(ts)
+ ok = true
+ return
+}
+
+_buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) {
+ if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) {
+ buffer_flush(ctx, buffer)
+ }
+ name := location.procedure if name == "" else name
+ buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
+}
+
+_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) {
+ ts := _trace_now(ctx)
+
+ if buffer.head + size_of(End_Event) > len(buffer.data) {
+ buffer_flush(ctx, buffer)
+ }
+
+ buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
+}
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index c64ab7d3b..2d20310ae 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -507,11 +507,8 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN)
foreign {
@(link_name="__$startup_runtime")
_startup_runtime :: proc "odin" () ---
-}
-
-@(link_name="__$cleanup_runtime")
-_cleanup_runtime :: proc() {
- default_temp_allocator_destroy(&global_default_temp_allocator_data)
+ @(link_name="__$cleanup_runtime")
+ _cleanup_runtime :: proc "odin" () ---
}
_cleanup_runtime_contextless :: proc "contextless" () {
diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin
index 3bfdaab3d..296ead722 100644
--- a/core/runtime/default_temporary_allocator.odin
+++ b/core/runtime/default_temporary_allocator.odin
@@ -72,3 +72,8 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
data = allocator,
}
}
+
+@(fini, private)
+_destroy_temp_allocator_fini :: proc() {
+ default_temp_allocator_destroy(&global_default_temp_allocator_data)
+}
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index cb04ff0aa..3c8cade39 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -184,32 +184,33 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
return
}
-mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
if allocator.procedure == nil {
return nil, nil
}
if new_size == 0 {
if ptr != nil {
- _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
- return nil, err
+ _, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
+ return
}
- return nil, nil
+ return
} else if ptr == nil {
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
- return ([^]byte)(ptr)[:old_size], nil
+ data = ([^]byte)(ptr)[:old_size]
+ return
}
- data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
+ data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
- return data, err
+ return
}
copy(data, ([^]byte)(ptr)[:old_size])
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
}
- return data, err
+ return
}
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
@@ -223,7 +224,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
when size_of(uint) == 8 {
if word_length := length >> 3; word_length != 0 {
- for i in 0..<word_length {
+ for _ in 0..<word_length {
if intrinsics.unaligned_load((^u64)(a)) != intrinsics.unaligned_load((^u64)(b)) {
return false
}
@@ -254,7 +255,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
return true
} else {
if word_length := length >> 2; word_length != 0 {
- for i in 0..<word_length {
+ for _ in 0..<word_length {
if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
return false
}
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 819cd5796..9696488a4 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -2,6 +2,9 @@ package runtime
_INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
+@(private="file")
+_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
+
when !ODIN_DISALLOW_RTTI {
print_any_single :: proc(arg: any) {
x := arg
@@ -105,14 +108,14 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
return buf, 4
}
-print_string :: proc "contextless" (str: string) -> (int, _OS_Errno) {
- return os_write(transmute([]byte)str)
+print_string :: proc "contextless" (str: string) -> (n: int) {
+ n, _ = os_write(transmute([]byte)str)
+ return
}
-print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno) {
+print_strings :: proc "contextless" (args: ..string) -> (n: int) {
for str in args {
- m: int
- m, err = os_write(transmute([]byte)str)
+ m, err := os_write(transmute([]byte)str)
n += m
if err != 0 {
break
@@ -121,8 +124,9 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno)
return
}
-print_byte :: proc "contextless" (b: byte) -> (int, _OS_Errno) {
- return os_write([]byte{b})
+print_byte :: proc "contextless" (b: byte) -> (n: int) {
+ n, _ = os_write([]byte{b})
+ return
}
print_encoded_rune :: proc "contextless" (r: rune) {
@@ -141,11 +145,10 @@ print_encoded_rune :: proc "contextless" (r: rune) {
if r <= 0 {
print_string("\\x00")
} else if r < 32 {
- digits := _INTEGER_DIGITS
n0, n1 := u8(r) >> 4, u8(r) & 0xf
print_string("\\x")
- print_byte(digits[n0])
- print_byte(digits[n1])
+ print_byte(_INTEGER_DIGITS_VAR[n0])
+ print_byte(_INTEGER_DIGITS_VAR[n1])
} else {
print_rune(r)
}
@@ -153,7 +156,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
print_byte('\'')
}
-print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check {
+print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
RUNE_SELF :: 0x80
if r < RUNE_SELF {
@@ -161,29 +164,27 @@ print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check
}
b, n := encode_rune(r)
- return os_write(b[:n])
+ m, _ := os_write(b[:n])
+ return m
}
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
- digits := _INTEGER_DIGITS
-
a: [129]byte
i := len(a)
b := u64(10)
u := x
for u >= b {
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
os_write(a[i:])
}
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
- digits := _INTEGER_DIGITS
b :: i64(10)
u := x
@@ -193,10 +194,10 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
a: [129]byte
i := len(a)
for u >= b {
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
if neg {
i -= 1; a[i] = '-'
}
diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin
index 00d7812a8..8d6ce6e48 100644
--- a/core/sync/primitives.odin
+++ b/core/sync/primitives.odin
@@ -236,4 +236,4 @@ _panic :: proc "contextless" (msg: string) -> ! {
runtime.print_string(msg)
runtime.print_byte('\n')
runtime.trap()
-} \ No newline at end of file
+}
diff --git a/core/sys/darwin/xnu_system_call_numbers.odin b/core/sys/darwin/xnu_system_call_numbers.odin
index b90373fdc..429d18964 100644
--- a/core/sys/darwin/xnu_system_call_numbers.odin
+++ b/core/sys/darwin/xnu_system_call_numbers.odin
@@ -1,6 +1,6 @@
package darwin
-unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
+unix_offset_syscall :: proc "contextless" (number: System_Call_Number) -> uintptr {
return uintptr(number) + uintptr(0x2000000)
}
diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin
index 685f75ffa..c7a6d6bc4 100644
--- a/core/sys/darwin/xnu_system_call_wrappers.odin
+++ b/core/sys/darwin/xnu_system_call_wrappers.odin
@@ -229,191 +229,191 @@ _Proc_Bsdinfo :: struct {
/*--==========================================================================--*/
-syscall_fsync :: #force_inline proc(fildes: c.int) -> bool {
+syscall_fsync :: #force_inline proc "contextless" (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 {
+syscall_write :: #force_inline proc "contextless" (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 {
+syscall_read :: #force_inline proc "contextless" (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 {
+syscall_open :: #force_inline proc "contextless" (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 {
+syscall_close :: #force_inline proc "contextless" (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 {
+syscall_fchmod :: #force_inline proc "contextless" (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 {
+syscall_chmod :: #force_inline proc "contextless" (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 {
+syscall_mkdir :: #force_inline proc "contextless" (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 {
+syscall_mkdir_at :: #force_inline proc "contextless" (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 {
+syscall_rmdir :: #force_inline proc "contextless" (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 {
+syscall_rename :: #force_inline proc "contextless" (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 {
+syscall_rename_at :: #force_inline proc "contextless" (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 {
+syscall_lseek :: #force_inline proc "contextless" (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 {
+syscall_gettid :: #force_inline proc "contextless" () -> u64 {
return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
}
-syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int {
+syscall_fstat :: #force_inline proc "contextless" (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 {
+syscall_lstat :: #force_inline proc "contextless" (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 {
+syscall_stat :: #force_inline proc "contextless" (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 {
+syscall_fstatat :: #force_inline proc "contextless" (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 {
+syscall_link :: #force_inline proc "contextless" (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 {
+syscall_linkat :: #force_inline proc "contextless" (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 {
+syscall_readlink :: #force_inline proc "contextless" (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 {
+syscall_readlinkat :: #force_inline proc "contextless" (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 {
+syscall_access :: #force_inline proc "contextless" (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 {
+syscall_faccessat :: #force_inline proc "contextless" (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 {
+syscall_getdirentries :: #force_inline proc "contextless" (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 {
+syscall_truncate :: #force_inline proc "contextless" (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 {
+syscall_ftruncate :: #force_inline proc "contextless" (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 {
+syscall_sysctl :: #force_inline proc "contextless" (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 {
+syscall_copyfile :: #force_inline proc "contextless" (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 {
+syscall_fcntl :: #force_inline proc "contextless" (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) {
+syscall_exit :: #force_inline proc "contextless" (code: c.int) {
intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
}
-syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int {
+syscall_kill :: #force_inline proc "contextless" (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 {
+syscall_dup :: #force_inline proc "contextless" (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 {
+syscall_execve :: #force_inline proc "contextless" (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 {
+syscall_munmap :: #force_inline proc "contextless" (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 {
+syscall_mmap :: #force_inline proc "contextless" (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 {
+syscall_flock :: #force_inline proc "contextless" (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 {
+syscall_utimes :: #force_inline proc "contextless" (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 {
+syscall_futimes :: #force_inline proc "contextless" (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 {
+syscall_adjtime :: #force_inline proc "contextless" (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 {
+syscall_sysctlbyname :: #force_inline proc "contextless" (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 {
+syscall_proc_info :: #force_inline proc "contextless" (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 {
+syscall_openat :: #force_inline proc "contextless" (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 {
+syscall_getentropy :: #force_inline proc "contextless" (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 {
+syscall_pipe :: #force_inline proc "contextless" (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 {
+syscall_chdir :: #force_inline proc "contextless" (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 {
+syscall_fchdir :: #force_inline proc "contextless" (fd: c.int, path: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path)
-} \ No newline at end of file
+}
diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin
index 8ce3ca3cb..91c0accfa 100644
--- a/core/sys/unix/syscalls_linux.odin
+++ b/core/sys/unix/syscalls_linux.odin
@@ -1563,15 +1563,167 @@ MADV_WIPEONFORK :: 18
MADV_KEEPONFORK :: 19
MADV_HWPOISON :: 100
+// pipe2 flags
+O_CLOEXEC :: 0o2000000
+
+// perf event data
+Perf_Sample :: struct #raw_union {
+ period: u64,
+ frequency: u64,
+}
+Perf_Wakeup :: struct #raw_union {
+ events: u32,
+ watermark: u32,
+}
+Perf_Field1 :: struct #raw_union {
+ breakpoint_addr: u64,
+ kprobe_func: u64,
+ uprobe_path: u64,
+ config1: u64,
+}
+Perf_Field2 :: struct #raw_union {
+ breakpoint_len: u64,
+ kprobe_addr: u64,
+ uprobe_offset: u64,
+ config2: u64,
+}
+Perf_Event_Attr :: struct #packed {
+ type: u32,
+ size: u32,
+ config: u64,
+ sample: Perf_Sample,
+ sample_type: u64,
+ read_format: u64,
+ flags: Perf_Flags,
+ wakeup: Perf_Wakeup,
+ breakpoint_type: u32,
+ field1: Perf_Field1,
+ field2: Perf_Field2,
+ branch_sample_type: u64,
+ sample_regs_user: u64,
+ sample_stack_user: u32,
+ clock_id: i32,
+ sample_regs_intr: u64,
+ aux_watermark: u32,
+ sample_max_stack: u16,
+ _padding: u16,
+}
+
+Perf_Event_Flags :: distinct bit_set[Perf_Event_Flag; u64]
+Perf_Event_Flag :: enum u64 {
+ Bit0 = 0,
+ Bit0_Is_Deprecated = 1,
+ User_Rdpmc = 2,
+ User_Time = 3,
+ User_Time_Zero = 4,
+ User_Time_Short = 5,
+}
+Perf_Capabilities :: struct #raw_union {
+ capabilities: u64,
+ flags: Perf_Event_Flags,
+}
+Perf_Event_mmap_Page :: struct #packed {
+ version: u32,
+ compat_version: u32,
+ lock: u32,
+ index: u32,
+ offset: i64,
+ time_enabled: u64,
+ time_running: u64,
+ cap: Perf_Capabilities,
+ pmc_width: u16,
+ time_shift: u16,
+ time_mult: u32,
+ time_offset: u64,
+ time_zero: u64,
+ size: u32,
+ reserved1: u32,
+ time_cycles: u64,
+ time_mask: u64,
+ reserved2: [116*8]u8,
+ data_head: u64,
+ data_tail: u64,
+ data_offset: u64,
+ data_size: u64,
+ aux_head: u64,
+ aux_tail: u64,
+ aux_offset: u64,
+ aux_size: u64,
+}
+
+Perf_Type_Id :: enum u32 {
+ Hardware = 0,
+ Software = 1,
+ Tracepoint = 2,
+ HW_Cache = 3,
+ Raw = 4,
+ Breakpoint = 5,
+}
+
+Perf_Hardware_Id :: enum u64 {
+ CPU_Cycles = 0,
+ Instructions = 1,
+ Cache_References = 2,
+ Cache_Misses = 3,
+ Branch_Instructions = 4,
+ Branch_Misses = 5,
+ Bus_Cycles = 6,
+ Stalled_Cycles_Frontend = 7,
+ Stalled_Cycles_Backend = 8,
+ Ref_CPU_Cycles = 9,
+}
+
+Perf_Flags :: distinct bit_set[Perf_Flag; u64]
+Perf_Flag :: enum u64 {
+ Disabled = 0,
+ Inherit = 1,
+ Pinned = 2,
+ Exclusive = 3,
+ Exclude_User = 4,
+ Exclude_Kernel = 5,
+ Exclude_HV = 6,
+ Exclude_Idle = 7,
+ mmap = 8,
+ Comm = 9,
+ Freq = 10,
+ Inherit_Stat = 11,
+ Enable_On_Exec = 12,
+ Task = 13,
+ Watermark = 14,
+ Precise_IP_0 = 15,
+ Precise_IP_1 = 16,
+ mmap_Data = 17,
+ Sample_Id_All = 18,
+ Exclude_Host = 19,
+ Exclude_Guest = 20,
+ Exclude_Callchain_Kernel = 21,
+ Exclude_Callchain_User = 22,
+ mmap2 = 23,
+ Comm_Exec = 24,
+ Use_Clockid = 25,
+ Context_Switch = 26,
+ Write_Backward = 27,
+ Namespaces = 28,
+ KSymbol = 29,
+ BPF_Event = 30,
+ Aux_Output = 31,
+ CGroup = 32,
+ Text_Poke = 33,
+ Build_Id = 34,
+ Inherit_Thread = 35,
+ Remove_On_Exec = 36,
+ Sigtrap = 37,
+}
+
sys_gettid :: proc "contextless" () -> int {
- return cast(int)intrinsics.syscall(SYS_gettid)
+ return int(intrinsics.syscall(SYS_gettid))
}
-sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
- return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
+sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: uint, flags: int) -> int {
+ return int(intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags)))
}
-sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
+sys_open :: proc "contextless" (path: cstring, flags: int, mode: uint = 0o000) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
} else { // NOTE: arm64 does not have open
@@ -1579,7 +1731,7 @@ sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) ->
}
}
-sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int {
+sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: uint = 0o000) -> int {
return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
}
@@ -1691,7 +1843,7 @@ sys_fchdir :: proc "contextless" (fd: int) -> int {
return int(intrinsics.syscall(SYS_fchdir, uintptr(fd)))
}
-sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
+sys_chmod :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have chmod
@@ -1699,7 +1851,7 @@ sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
-sys_fchmod :: proc "contextless" (fd: int, mode: int) -> int {
+sys_fchmod :: proc "contextless" (fd: int, mode: uint) -> int {
return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode)))
}
@@ -1759,7 +1911,7 @@ sys_rmdir :: proc "contextless" (path: cstring) -> int {
}
}
-sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
+sys_mkdir :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have mkdir
@@ -1767,11 +1919,11 @@ sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
-sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int {
+sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: uint) -> int {
return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode)))
}
-sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
+sys_mknod :: proc "contextless" (path: cstring, mode: uint, dev: int) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
} else { // NOTE: arm64 does not have mknod
@@ -1779,7 +1931,7 @@ sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
}
}
-sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int {
+sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: uint, dev: int) -> int {
return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
}
@@ -1818,6 +1970,16 @@ sys_fork :: proc "contextless" () -> int {
return int(intrinsics.syscall(SYS_clone, SIGCHLD))
}
}
+sys_pipe2 :: proc "contextless" (fds: rawptr, flags: int) -> int {
+ return int(intrinsics.syscall(SYS_pipe2, uintptr(fds), uintptr(flags)))
+}
+sys_dup2 :: proc "contextless" (oldfd: int, newfd: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_dup2, uintptr(oldfd), uintptr(newfd)))
+ } else {
+ return int(intrinsics.syscall(SYS_dup3, uintptr(oldfd), uintptr(newfd), 0))
+ }
+}
sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int {
return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset))
@@ -1846,6 +2008,14 @@ sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, fla
return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags)))
}
+sys_perf_event_open :: proc "contextless" (event_attr: rawptr, pid: i32, cpu: i32, group_fd: i32, flags: u32) -> int {
+ return int(intrinsics.syscall(SYS_perf_event_open, uintptr(event_attr), uintptr(pid), uintptr(cpu), uintptr(group_fd), uintptr(flags)))
+}
+
+sys_personality :: proc(persona: u64) -> int {
+ return int(intrinsics.syscall(SYS_personality, uintptr(persona)))
+}
+
get_errno :: proc "contextless" (res: int) -> i32 {
if res < 0 && res > -4096 {
return i32(-res)
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 6def41c5d..f736696e1 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -315,6 +315,13 @@ foreign kernel32 {
lpOverlapped: LPOVERLAPPED,
lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
) -> BOOL ---
+ FindFirstChangeNotificationW :: proc(
+ lpPathName: LPWSTR,
+ bWatchSubtree: BOOL,
+ dwNotifyFilter: DWORD,
+ ) -> HANDLE ---
+ FindNextChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
+ FindCloseChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
diff --git a/core/time/perf.odin b/core/time/perf.odin
index 53406646f..e51b17441 100644
--- a/core/time/perf.odin
+++ b/core/time/perf.odin
@@ -1,11 +1,11 @@
package time
import "core:runtime"
+import "core:intrinsics"
Tick :: struct {
_nsec: i64, // relative amount
}
-
tick_now :: proc "contextless" () -> Tick {
return _tick_now()
}
@@ -40,6 +40,53 @@ _tick_duration_end :: proc "contextless" (d: ^Duration, t: Tick) {
d^ = tick_since(t)
}
+when ODIN_ARCH == .amd64 {
+ @(private)
+ x86_has_invariant_tsc :: proc "contextless" () -> bool {
+ eax, _, _, _ := intrinsics.x86_cpuid(0x80_000_000, 0)
+
+ // Is this processor *really* ancient?
+ if eax < 0x80_000_007 {
+ return false
+ }
+
+ // check if the invariant TSC bit is set
+ _, _, _, edx := intrinsics.x86_cpuid(0x80_000_007, 0)
+ return (edx & (1 << 8)) != 0
+ }
+}
+
+has_invariant_tsc :: proc "contextless" () -> bool {
+ when ODIN_ARCH == .amd64 {
+ return x86_has_invariant_tsc()
+ }
+
+ return false
+}
+
+tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ if !has_invariant_tsc() {
+ return 0, false
+ }
+
+ hz, ok := _get_tsc_frequency()
+ if !ok {
+ // fallback to approximate TSC
+ tsc_begin := intrinsics.read_cycle_counter()
+ tick_begin := tick_now()
+
+ sleep(2 * Second)
+
+ tsc_end := intrinsics.read_cycle_counter()
+ tick_end := tick_now()
+
+ time_diff := u128(duration_nanoseconds(tick_diff(tick_begin, tick_end)))
+ hz = u64((u128(tsc_end - tsc_begin) * 1_000_000_000) / time_diff)
+ }
+
+ return hz, true
+}
+
/*
Benchmark helpers
*/
@@ -94,4 +141,4 @@ benchmark :: proc(options: ^Benchmark_Options, allocator := context.allocator) -
options->teardown(allocator) or_return
}
return
-} \ No newline at end of file
+}
diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin
new file mode 100644
index 000000000..9e54ee8f7
--- /dev/null
+++ b/core/time/tsc_darwin.odin
@@ -0,0 +1,21 @@
+//+private
+//+build darwin
+package time
+
+import "core:c"
+
+foreign import libc "System.framework"
+foreign libc {
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
+}
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ tmp_freq : u64 = 0
+ tmp_size : i64 = size_of(tmp_freq)
+ ret := _sysctlbyname("machdep.tsc.frequency", &tmp_freq, &tmp_size, nil, 0)
+ if ret < 0 {
+ return 0, false
+ }
+
+ return tmp_freq, true
+}
diff --git a/core/time/tsc_freebsd.odin b/core/time/tsc_freebsd.odin
new file mode 100644
index 000000000..f4d6ccc3a
--- /dev/null
+++ b/core/time/tsc_freebsd.odin
@@ -0,0 +1,21 @@
+//+private
+//+build freebsd
+package time
+
+import "core:c"
+
+foreign import libc "system:c"
+foreign libc {
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
+}
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ tmp_freq : u64 = 0
+ tmp_size : i64 = size_of(tmp_freq)
+ ret := _sysctlbyname("machdep.tsc_freq", &tmp_freq, &tmp_size, nil, 0)
+ if ret < 0 {
+ return 0, false
+ }
+
+ return tmp_freq, true
+}
diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin
new file mode 100644
index 000000000..c5f2902e9
--- /dev/null
+++ b/core/time/tsc_linux.odin
@@ -0,0 +1,35 @@
+//+private
+//+build linux
+package time
+
+import "core:intrinsics"
+import "core:sys/unix"
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ perf_attr := unix.Perf_Event_Attr{}
+ perf_attr.type = u32(unix.Perf_Type_Id.Hardware)
+ perf_attr.config = u64(unix.Perf_Hardware_Id.Instructions)
+ perf_attr.size = size_of(perf_attr)
+ perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV}
+ fd := unix.sys_perf_event_open(&perf_attr, 0, -1, -1, 0)
+ if fd == -1 {
+ return 0, false
+ }
+ defer unix.sys_close(fd)
+
+ page_size : uint = 4096
+ ret := unix.sys_mmap(nil, page_size, unix.PROT_READ, unix.MAP_SHARED, fd, 0)
+ if ret < 0 && ret > -4096 {
+ return 0, false
+ }
+ addr := rawptr(uintptr(ret))
+ defer unix.sys_munmap(addr, page_size)
+
+ event_page := (^unix.Perf_Event_mmap_Page)(addr)
+ if .User_Time not_in event_page.cap.flags {
+ return 0, false
+ }
+
+ frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult))
+ return frequency, true
+}
diff --git a/core/time/tsc_openbsd.odin b/core/time/tsc_openbsd.odin
new file mode 100644
index 000000000..ab126d5c1
--- /dev/null
+++ b/core/time/tsc_openbsd.odin
@@ -0,0 +1,7 @@
+//+private
+//+build openbsd
+package time
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ return 0, false
+}
diff --git a/core/time/tsc_windows.odin b/core/time/tsc_windows.odin
new file mode 100644
index 000000000..7f7be6393
--- /dev/null
+++ b/core/time/tsc_windows.odin
@@ -0,0 +1,7 @@
+//+private
+//+build windows
+package time
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ return 0, false
+}