Browse Source

add support for linux_riscv64 and freestanding_riscv64

Laytan 1 year ago
parent
commit
ca6ef95b03

+ 50 - 0
.github/workflows/ci.yml

@@ -220,3 +220,53 @@ jobs:
         run: |
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           odin check examples/all -strict-style -target:windows_i386
           odin check examples/all -strict-style -target:windows_i386
+
+  build_linux_riscv64:
+    runs-on: ubuntu-latest
+    name: Linux riscv64 (emulated) Build, Check and Test
+    timeout-minutes: 15
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Download LLVM (Linux)
+        run: |
+          wget https://apt.llvm.org/llvm.sh
+          chmod +x llvm.sh
+          sudo ./llvm.sh 18
+          echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
+
+      - name: Build Odin
+        run: ./build_odin.sh release
+
+      - name: Odin version
+        run: ./odin version
+
+      - name: Odin report
+        run: ./odin report
+
+      - name: Compile needed Vendor
+        run: |
+          make -C vendor/stb/src
+          make -C vendor/cgltf/src
+          make -C vendor/miniaudio/src
+
+      - name: Odin check
+        run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
+
+      - name: Install riscv64 toolchain and qemu
+        run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
+
+      - name: Odin run
+        run: ./odin run examples/demo -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+
+      - name: Odin run -debug
+        run: ./odin run examples/demo -debug -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+
+      - name: Normal Core library tests
+        run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+
+      - name: Optimized Core library tests
+        run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+
+      - name: Internals tests
+        run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"

+ 3 - 0
base/runtime/entry_unix.odin

@@ -34,6 +34,9 @@ when ODIN_BUILD_MODE == .Dynamic {
 		} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
 		} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
 			@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
 			@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
 			SYS_exit :: 1
 			SYS_exit :: 1
+		} else when ODIN_ARCH == .riscv64 {
+			@require foreign import entry "entry_unix_no_crt_riscv64.asm"
+			SYS_exit :: 93
 		}
 		}
 		@(link_name="_start_odin", linkage="strong", require)
 		@(link_name="_start_odin", linkage="strong", require)
 		_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
 		_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {

+ 10 - 0
base/runtime/entry_unix_no_crt_riscv64.asm

@@ -0,0 +1,10 @@
+.text
+
+.globl _start
+
+_start:
+	ld a0, 0(sp)
+	addi a1, sp, 8
+	addi sp, sp, ~15
+	call _start_odin
+	ebreak

+ 2 - 0
base/runtime/os_specific_linux.odin

@@ -12,6 +12,8 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
 		SYS_write :: uintptr(4)
 		SYS_write :: uintptr(4)
 	} else when ODIN_ARCH == .arm32 {
 	} else when ODIN_ARCH == .arm32 {
 		SYS_write :: uintptr(4)
 		SYS_write :: uintptr(4)
+	} else when ODIN_ARCH == .riscv64 {
+		SYS_write :: uintptr(64)
 	}
 	}
 
 
 	stderr :: 2
 	stderr :: 2

+ 1 - 1
core/crypto/_chacha20/simd128/chacha20_simd128.odin

@@ -3,7 +3,7 @@ package chacha20_simd128
 import "base:intrinsics"
 import "base:intrinsics"
 import "core:crypto/_chacha20"
 import "core:crypto/_chacha20"
 import "core:simd"
 import "core:simd"
-import "core:sys/info"
+@(require) import "core:sys/info"
 
 
 // Portable 128-bit `core:simd` implementation.
 // Portable 128-bit `core:simd` implementation.
 //
 //

+ 2 - 2
core/net/socket_linux.odin

@@ -33,8 +33,8 @@ Socket_Option :: enum c.int {
 	Linger                    = c.int(linux.Socket_Option.LINGER),
 	Linger                    = c.int(linux.Socket_Option.LINGER),
 	Receive_Buffer_Size       = c.int(linux.Socket_Option.RCVBUF),
 	Receive_Buffer_Size       = c.int(linux.Socket_Option.RCVBUF),
 	Send_Buffer_Size          = c.int(linux.Socket_Option.SNDBUF),
 	Send_Buffer_Size          = c.int(linux.Socket_Option.SNDBUF),
-	Receive_Timeout           = c.int(linux.Socket_Option.RCVTIMEO_NEW),
-	Send_Timeout              = c.int(linux.Socket_Option.SNDTIMEO_NEW),
+	Receive_Timeout           = c.int(linux.Socket_Option.RCVTIMEO),
+	Send_Timeout              = c.int(linux.Socket_Option.SNDTIMEO),
 }
 }
 
 
 // Wrappers and unwrappers for system-native types
 // Wrappers and unwrappers for system-native types

+ 19 - 0
core/os/os_linux.odin

@@ -284,6 +284,25 @@ when ODIN_ARCH == .arm64 {
 		_reserved:     [2]i32,
 		_reserved:     [2]i32,
 	}
 	}
 	#assert(size_of(OS_Stat) == 128)
 	#assert(size_of(OS_Stat) == 128)
+} else when ODIN_ARCH == .riscv64 {
+	OS_Stat :: struct {
+		device_id:     u64,
+		serial:        u64,
+		mode:          u32,
+		nlink:         u32,
+		uid:           u32,
+		gid:           u32,
+		rdev:          u64,
+		_:             u64,
+		size:          i64,
+		block_size:    i32,
+		_:             i32,
+		blocks:        i64,
+		last_access:   Unix_File_Time,
+		modified:      Unix_File_Time,
+		status_change: Unix_File_Time,
+		_:             [3]uint,
+	}
 } else {
 } else {
 	OS_Stat :: struct {
 	OS_Stat :: struct {
 		device_id:     u64, // ID of device containing file
 		device_id:     u64, // ID of device containing file

+ 46 - 0
core/sys/info/cpu_linux_riscv64.odin

@@ -0,0 +1,46 @@
+//+build riscv64
+//+build linux
+package sysinfo
+
+import "base:intrinsics"
+
+import "core:sys/linux"
+
+@(init, private)
+init_cpu_features :: proc() {
+	fd, err := linux.open("/proc/self/auxv", {})
+	if err != .NONE { return }
+	defer linux.close(fd)
+
+	// This is probably enough right?
+	buf: [4096]byte
+	n, rerr := linux.read(fd, buf[:])
+	if rerr != .NONE || n == 0 { return }
+
+	ulong     :: u64
+	AT_HWCAP  :: 16
+
+	// TODO: using these we could get more information than just the basics.
+	// AT_HWCAP2 :: 26
+	// AT_HWCAP3 :: 29
+	// AT_HWCAP4 :: 30
+
+	auxv := buf[:n]
+	for len(auxv) >= size_of(ulong)*2 {
+		key := intrinsics.unaligned_load((^ulong)(&auxv[0]))
+		val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)]))
+		auxv = auxv[2*size_of(ulong):]
+
+		if key != AT_HWCAP {
+			continue
+		}
+
+		cpu_features = transmute(CPU_Features)(val)
+		break
+	}
+}
+
+@(init, private)
+init_cpu_name :: proc() {
+	cpu_name = "RISCV64"
+}

+ 16 - 0
core/sys/info/cpu_riscv64.odin

@@ -0,0 +1,16 @@
+package sysinfo
+
+CPU_Feature :: enum u64 {
+	I = 'I' - 'A', // Base features, don't think this is ever not here.
+	M = 'M' - 'A', // Integer multiplication and division, currently required by Odin.
+	A = 'A' - 'A', // Atomics.
+	F = 'F' - 'A', // Single precision floating point, currently required by Odin.
+	D = 'D' - 'A', // Double precision floating point, currently required by Odin.
+	C = 'C' - 'A', // Compressed instructions.
+	V = 'V' - 'A', // Vector operations.
+}
+
+CPU_Features :: distinct bit_set[CPU_Feature; u64]
+
+cpu_features: Maybe(CPU_Features)
+cpu_name: Maybe(string)

+ 1 - 1
core/sys/info/sysinfo.odin

@@ -1,6 +1,6 @@
 package sysinfo
 package sysinfo
 
 
-when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64) {
+when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64) {
 	#assert(false, "This package is unsupported on this architecture.")
 	#assert(false, "This package is unsupported on this architecture.")
 }
 }
 
 

+ 8 - 6
core/sys/linux/bits.odin

@@ -1343,14 +1343,16 @@ Socket_Option :: enum {
 	RESERVE_MEM                   = 73,
 	RESERVE_MEM                   = 73,
 	TXREHASH                      = 74,
 	TXREHASH                      = 74,
 	RCVMARK                       = 75,
 	RCVMARK                       = 75,
-	// Hardcoded 64-bit Time. It's time to move on.
-	TIMESTAMP                     = TIMESTAMP_NEW,
-	TIMESTAMPNS                   = TIMESTAMPNS_NEW,
-	TIMESTAMPING                  = TIMESTAMPING_NEW,
-	RCVTIMEO                      = RCVTIMEO_NEW,
-	SNDTIMEO                      = SNDTIMEO_NEW,
+	TIMESTAMP                     = TIMESTAMP_OLD    when _SOCKET_OPTION_OLD else TIMESTAMP_NEW,
+	TIMESTAMPNS                   = TIMESTAMPNS_OLD  when _SOCKET_OPTION_OLD else TIMESTAMPNS_NEW,
+	TIMESTAMPING                  = TIMESTAMPING_OLD when _SOCKET_OPTION_OLD else TIMESTAMPING_NEW,
+	RCVTIMEO                      = RCVTIMEO_OLD     when _SOCKET_OPTION_OLD else RCVTIMEO_NEW,
+	SNDTIMEO                      = SNDTIMEO_OLD     when _SOCKET_OPTION_OLD else SNDTIMEO_NEW,
 }
 }
 
 
+@(private)
+_SOCKET_OPTION_OLD :: size_of(rawptr) == 8 /* || size_of(time_t) == size_of(__kernel_long_t) */
+
 Socket_UDP_Option :: enum {
 Socket_UDP_Option :: enum {
 	CORK                   = 1,
 	CORK                   = 1,
 	ENCAP                  = 100,
 	ENCAP                  = 100,

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

@@ -39,7 +39,7 @@ write :: proc "contextless" (fd: Fd, buf: []u8) -> (int, Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 open :: proc "contextless" (name: cstring, flags: Open_Flags, mode: Mode = {}) -> (Fd, Errno) {
 open :: proc "contextless" (name: cstring, flags: Open_Flags, mode: Mode = {}) -> (Fd, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_openat, AT_FDCWD, transmute(uintptr) name, transmute(u32) flags, transmute(u32) mode)
 		ret := syscall(SYS_openat, AT_FDCWD, transmute(uintptr) name, transmute(u32) flags, transmute(u32) mode)
 		return errno_unwrap(ret, Fd)
 		return errno_unwrap(ret, Fd)
 	} else {
 	} else {
@@ -68,7 +68,7 @@ close :: proc "contextless" (fd: Fd) -> (Errno) {
 */
 */
 stat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
 stat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
 	when size_of(int) == 8 {
 	when size_of(int) == 8 {
-		when ODIN_ARCH == .arm64 {
+		when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 			ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat, 0)
 			ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat, 0)
 			return Errno(-ret)
 			return Errno(-ret)
 		} else {
 		} else {
@@ -111,7 +111,7 @@ fstat :: proc "contextless" (fd: Fd, stat: ^Stat) -> (Errno) {
 */
 */
 lstat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
 lstat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
 	when size_of(int) == 8 {
 	when size_of(int) == 8 {
-		when ODIN_ARCH == .arm64 {
+		when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 			return fstatat(AT_FDCWD, filename, stat, {.SYMLINK_NOFOLLOW})
 			return fstatat(AT_FDCWD, filename, stat, {.SYMLINK_NOFOLLOW})
 		} else {
 		} else {
 			ret := syscall(SYS_lstat, cast(rawptr) filename, stat)
 			ret := syscall(SYS_lstat, cast(rawptr) filename, stat)
@@ -128,7 +128,7 @@ lstat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
 	Available since Linux 2.2.
 	Available since Linux 2.2.
 */
 */
 poll :: proc "contextless" (fds: []Poll_Fd, timeout: i32) -> (i32, Errno) {
 poll :: proc "contextless" (fds: []Poll_Fd, timeout: i32) -> (i32, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		seconds := cast(uint) timeout / 1000
 		seconds := cast(uint) timeout / 1000
 		nanoseconds := cast(uint) (timeout % 1000) * 1_000_000
 		nanoseconds := cast(uint) (timeout % 1000) * 1_000_000
 		timeout_spec := Time_Spec{seconds, nanoseconds}
 		timeout_spec := Time_Spec{seconds, nanoseconds}
@@ -291,7 +291,7 @@ writev :: proc "contextless" (fd: Fd, iov: []IO_Vec) -> (int, Errno) {
 	For ARM64 available since Linux 2.6.16.
 	For ARM64 available since Linux 2.6.16.
 */
 */
 access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (Errno) {
 access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -407,7 +407,7 @@ dup :: proc "contextless" (fd: Fd) -> (Fd, Errno) {
 	On ARM64 available since Linux 2.6.27.
 	On ARM64 available since Linux 2.6.27.
 */
 */
 dup2 :: proc "contextless" (old: Fd, new: Fd) -> (Fd, Errno) {
 dup2 :: proc "contextless" (old: Fd, new: Fd) -> (Fd, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_dup3, old, new, 0)
 		ret := syscall(SYS_dup3, old, new, 0)
 		return errno_unwrap(ret, Fd)
 		return errno_unwrap(ret, Fd)
 	} else {
 	} else {
@@ -422,7 +422,7 @@ dup2 :: proc "contextless" (old: Fd, new: Fd) -> (Fd, Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 pause :: proc "contextless" () {
 pause :: proc "contextless" () {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		syscall(SYS_ppoll, 0, 0, 0, 0)
 		syscall(SYS_ppoll, 0, 0, 0, 0)
 	} else {
 	} else {
 		syscall(SYS_pause)
 		syscall(SYS_pause)
@@ -452,7 +452,7 @@ getitimer :: proc "contextless" (which: ITimer_Which, cur: ^ITimer_Val) -> (Errn
 	Available since Linux 1.0.
 	Available since Linux 1.0.
 */
 */
 alarm :: proc "contextless" (seconds: u32) -> u32 {
 alarm :: proc "contextless" (seconds: u32) -> u32 {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		new := ITimer_Val { value = { seconds = cast(int) seconds } }
 		new := ITimer_Val { value = { seconds = cast(int) seconds } }
 		old := ITimer_Val {}
 		old := ITimer_Val {}
 		syscall(SYS_setitimer, ITimer_Which.REAL, &new, &old)
 		syscall(SYS_setitimer, ITimer_Which.REAL, &new, &old)
@@ -765,7 +765,7 @@ getsockopt :: proc {
 	Available since Linux 1.0.
 	Available since Linux 1.0.
 */
 */
 fork :: proc "contextless" () -> (Pid, Errno) {
 fork :: proc "contextless" () -> (Pid, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_clone, u64(Signal.SIGCHLD), cast(rawptr) nil, cast(rawptr) nil, cast(rawptr) nil, u64(0))
 		ret := syscall(SYS_clone, u64(Signal.SIGCHLD), cast(rawptr) nil, cast(rawptr) nil, cast(rawptr) nil, u64(0))
 		return errno_unwrap(ret, Pid)
 		return errno_unwrap(ret, Pid)
 	} else {
 	} else {
@@ -779,7 +779,7 @@ fork :: proc "contextless" () -> (Pid, Errno) {
 	Available since Linux 2.2.
 	Available since Linux 2.2.
 */
 */
 vfork :: proc "contextless" () -> Pid {
 vfork :: proc "contextless" () -> Pid {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return Pid(syscall(SYS_vfork))
 		return Pid(syscall(SYS_vfork))
 	} else {
 	} else {
 		return Pid(syscall(SYS_clone, Signal.SIGCHLD))
 		return Pid(syscall(SYS_clone, Signal.SIGCHLD))
@@ -792,7 +792,7 @@ vfork :: proc "contextless" () -> Pid {
 	On ARM64 available since Linux 3.19.
 	On ARM64 available since Linux 3.19.
 */
 */
 execve :: proc "contextless" (name: cstring, argv: [^]cstring, envp: [^]cstring) -> (Errno) {
 execve :: proc "contextless" (name: cstring, argv: [^]cstring, envp: [^]cstring) -> (Errno) {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		ret := syscall(SYS_execve, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
 		ret := syscall(SYS_execve, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1193,7 +1193,7 @@ fchdir :: proc "contextless" (fd: Fd) -> (Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 rename :: proc "contextless" (old: cstring, new: cstring) -> (Errno) {
 rename :: proc "contextless" (old: cstring, new: cstring) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_renameat, AT_FDCWD, cast(rawptr) old, AT_FDCWD, cast(rawptr) new)
 		ret := syscall(SYS_renameat, AT_FDCWD, cast(rawptr) old, AT_FDCWD, cast(rawptr) new)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1208,7 +1208,7 @@ rename :: proc "contextless" (old: cstring, new: cstring) -> (Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 mkdir :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
 mkdir :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_mkdirat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		ret := syscall(SYS_mkdirat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1223,7 +1223,7 @@ mkdir :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 rmdir :: proc "contextless" (name: cstring) -> (Errno) {
 rmdir :: proc "contextless" (name: cstring) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_unlinkat, AT_FDCWD, cast(rawptr) name, transmute(i32) FD_Flags{.REMOVEDIR})
 		ret := syscall(SYS_unlinkat, AT_FDCWD, cast(rawptr) name, transmute(i32) FD_Flags{.REMOVEDIR})
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1238,7 +1238,7 @@ rmdir :: proc "contextless" (name: cstring) -> (Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 creat :: proc "contextless" (name: cstring, mode: Mode) -> (Fd, Errno) {
 creat :: proc "contextless" (name: cstring, mode: Mode) -> (Fd, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return openat(AT_FDCWD, name, {.CREAT, .WRONLY,.TRUNC}, mode)
 		return openat(AT_FDCWD, name, {.CREAT, .WRONLY,.TRUNC}, mode)
 	} else {
 	} else {
 		ret := syscall(SYS_creat, cast(rawptr) name, transmute(u32) mode)
 		ret := syscall(SYS_creat, cast(rawptr) name, transmute(u32) mode)
@@ -1252,7 +1252,7 @@ creat :: proc "contextless" (name: cstring, mode: Mode) -> (Fd, Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
 link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath, 0)
 		ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath, 0)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1267,7 +1267,7 @@ link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 unlink :: proc "contextless" (name: cstring) -> (Errno) {
 unlink :: proc "contextless" (name: cstring) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_unlinkat, AT_FDCWD, cast(rawptr) name, 0)
 		ret := syscall(SYS_unlinkat, AT_FDCWD, cast(rawptr) name, 0)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1282,7 +1282,7 @@ unlink :: proc "contextless" (name: cstring) -> (Errno) {
 	On arm64 available since Linux 2.6.16.
 	On arm64 available since Linux 2.6.16.
 */
 */
 symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
 symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_symlinkat, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
 		ret := syscall(SYS_symlinkat, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1297,7 +1297,7 @@ symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
 	On arm64 available since Linux 2.6.16.
 	On arm64 available since Linux 2.6.16.
 */
 */
 readlink :: proc "contextless" (name: cstring, buf: []u8) -> (int, Errno) {
 readlink :: proc "contextless" (name: cstring, buf: []u8) -> (int, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_readlinkat, AT_FDCWD, cast(rawptr) name, raw_data(buf), len(buf))
 		ret := syscall(SYS_readlinkat, AT_FDCWD, cast(rawptr) name, raw_data(buf), len(buf))
 		return errno_unwrap(ret, int)
 		return errno_unwrap(ret, int)
 	} else {
 	} else {
@@ -1312,7 +1312,7 @@ readlink :: proc "contextless" (name: cstring, buf: []u8) -> (int, Errno) {
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 chmod :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
 chmod :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_fchmodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		ret := syscall(SYS_fchmodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1340,7 +1340,7 @@ chown :: proc "contextless" (name: cstring, uid: Uid, gid: Gid) -> (Errno) {
 	when size_of(int) == 4 {
 	when size_of(int) == 4 {
 		ret := syscall(SYS_chown32, cast(rawptr) name, uid, gid)
 		ret := syscall(SYS_chown32, cast(rawptr) name, uid, gid)
 		return Errno(-ret)
 		return Errno(-ret)
-	} else when ODIN_ARCH == .arm64 {
+	} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_fchownat, AT_FDCWD, cast(rawptr) name, uid, gid, 0)
 		ret := syscall(SYS_fchownat, AT_FDCWD, cast(rawptr) name, uid, gid, 0)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1374,7 +1374,7 @@ lchown :: proc "contextless" (name: cstring, uid: Uid, gid: Gid) -> (Errno) {
 	when size_of(int) == 4 {
 	when size_of(int) == 4 {
 		ret := syscall(SYS_lchown32, cast(rawptr) name, uid, gid)
 		ret := syscall(SYS_lchown32, cast(rawptr) name, uid, gid)
 		return Errno(-ret)
 		return Errno(-ret)
-	} else when ODIN_ARCH == .arm64 {
+	} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_fchownat, AT_FDCWD, cast(rawptr) name, uid, gid, transmute(i32) FD_Flags{.SYMLINK_NOFOLLOW})
 		ret := syscall(SYS_fchownat, AT_FDCWD, cast(rawptr) name, uid, gid, transmute(i32) FD_Flags{.SYMLINK_NOFOLLOW})
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -1727,7 +1727,7 @@ getppid :: proc "contextless" () -> Pid {
 	Available since Linux 1.0.
 	Available since Linux 1.0.
 */
 */
 getpgrp :: proc "contextless" () -> (Pid, Errno) {
 getpgrp :: proc "contextless" () -> (Pid, Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_getpgid, 0)
 		ret := syscall(SYS_getpgid, 0)
 		return errno_unwrap(ret, Pid)
 		return errno_unwrap(ret, Pid)
 	} else {
 	} else {
@@ -1950,7 +1950,7 @@ sigaltstack :: proc "contextless" (stack: ^Sig_Stack, old_stack: ^Sig_Stack) ->
 	On ARM64 available since Linux 2.6.16.
 	On ARM64 available since Linux 2.6.16.
 */
 */
 mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
 mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
 		ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -2207,7 +2207,7 @@ gettid :: proc "contextless" () -> Pid {
 	Available since Linux 1.0.
 	Available since Linux 1.0.
 */
 */
 time :: proc "contextless" (tloc: ^uint) -> (Errno) {
 time :: proc "contextless" (tloc: ^uint) -> (Errno) {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		ret := syscall(SYS_time, tloc)
 		ret := syscall(SYS_time, tloc)
 		return Errno(-ret)
 		return Errno(-ret)
 	} else {
 	} else {
@@ -2335,7 +2335,7 @@ futex :: proc{
 	Available since Linux 2.6.
 	Available since Linux 2.6.
 */
 */
 epoll_create :: proc(size: i32 = 1) -> (Fd, Errno) {
 epoll_create :: proc(size: i32 = 1) -> (Fd, Errno) {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		ret := syscall(SYS_epoll_create, i32(1))
 		ret := syscall(SYS_epoll_create, i32(1))
 		return errno_unwrap(ret, Fd)
 		return errno_unwrap(ret, Fd)
 	} else {
 	} else {
@@ -2433,7 +2433,7 @@ exit_group :: proc "contextless" (code: i32) -> ! {
 	Available since Linux 2.6.
 	Available since Linux 2.6.
 */
 */
 epoll_wait :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: i32) -> (i32, Errno) {
 epoll_wait :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: i32) -> (i32, Errno) {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		ret := syscall(SYS_epoll_wait, epfd, events, count, timeout)
 		ret := syscall(SYS_epoll_wait, epfd, events, count, timeout)
 		return errno_unwrap(ret, i32)
 		return errno_unwrap(ret, i32)
 	} else {
 	} else {

+ 334 - 0
core/sys/linux/syscall_riscv64.odin

@@ -0,0 +1,334 @@
+//+build riscv64
+package linux
+
+// https://github.com/riscv-collab/riscv-gnu-toolchain/blob/master/linux-headers/include/asm-generic/unistd.h
+
+SYS_io_setup                     :: uintptr(0)
+SYS_io_destroy                   :: uintptr(1)
+SYS_io_submit                    :: uintptr(2)
+SYS_io_cancel                    :: uintptr(3)
+SYS_io_getevents                 :: uintptr(4)
+SYS_setxattr                     :: uintptr(5)
+SYS_lsetxattr                    :: uintptr(6)
+SYS_fsetxattr                    :: uintptr(7)
+SYS_getxattr                     :: uintptr(8)
+SYS_lgetxattr                    :: uintptr(9)
+SYS_fgetxattr                    :: uintptr(10)
+SYS_listxattr                    :: uintptr(11)
+SYS_llistxattr                   :: uintptr(12)
+SYS_flistxattr                   :: uintptr(13)
+SYS_removexattr                  :: uintptr(14)
+SYS_lremovexattr                 :: uintptr(15)
+SYS_fremovexattr                 :: uintptr(16)
+SYS_getcwd                       :: uintptr(17)
+SYS_lookup_dcookie               :: uintptr(18)
+SYS_eventfd2                     :: uintptr(19)
+SYS_epoll_create1                :: uintptr(20)
+SYS_epoll_ctl                    :: uintptr(21)
+SYS_epoll_pwait                  :: uintptr(22)
+SYS_dup                          :: uintptr(23)
+SYS_dup3                         :: uintptr(24)
+SYS_fcntl                        :: uintptr(25)
+SYS_inotify_init1                :: uintptr(26)
+SYS_inotify_add_watch            :: uintptr(27)
+SYS_inotify_rm_watch             :: uintptr(28)
+SYS_ioctl                        :: uintptr(29)
+SYS_ioprio_set                   :: uintptr(30)
+SYS_ioprio_get                   :: uintptr(31)
+SYS_flock                        :: uintptr(32)
+SYS_mknodat                      :: uintptr(33)
+SYS_mkdirat                      :: uintptr(34)
+SYS_unlinkat                     :: uintptr(35)
+SYS_symlinkat                    :: uintptr(36)
+SYS_linkat                       :: uintptr(37)
+SYS_renameat                     :: uintptr(38)
+SYS_umount2                      :: uintptr(39)
+SYS_mount                        :: uintptr(40)
+SYS_pivot_root                   :: uintptr(41)
+SYS_nfsservctl                   :: uintptr(42)
+SYS_statfs                       :: uintptr(43)
+SYS_fstatfs                      :: uintptr(44)
+SYS_truncate                     :: uintptr(45)
+SYS_ftruncate                    :: uintptr(46)
+SYS_fallocate                    :: uintptr(47)
+SYS_faccessat                    :: uintptr(48)
+SYS_chdir                        :: uintptr(49)
+SYS_fchdir                       :: uintptr(50)
+SYS_chroot                       :: uintptr(51)
+SYS_fchmod                       :: uintptr(52)
+SYS_fchmodat                     :: uintptr(53)
+SYS_fchownat                     :: uintptr(54)
+SYS_fchown                       :: uintptr(55)
+SYS_openat                       :: uintptr(56)
+SYS_close                        :: uintptr(57)
+SYS_vhangup                      :: uintptr(58)
+SYS_pipe2                        :: uintptr(59)
+SYS_quotactl                     :: uintptr(60)
+SYS_getdents64                   :: uintptr(61)
+SYS_lseek                        :: uintptr(62)
+SYS_read                         :: uintptr(63)
+SYS_write                        :: uintptr(64)
+SYS_readv                        :: uintptr(65)
+SYS_writev                       :: uintptr(66)
+SYS_pread64                      :: uintptr(67)
+SYS_pwrite64                     :: uintptr(68)
+SYS_preadv                       :: uintptr(69)
+SYS_pwritev                      :: uintptr(70)
+SYS_sendfile                     :: uintptr(71)
+SYS_pselect6                     :: uintptr(72)
+SYS_ppoll                        :: uintptr(73)
+SYS_signalfd4                    :: uintptr(74)
+SYS_vmsplice                     :: uintptr(75)
+SYS_splice                       :: uintptr(76)
+SYS_tee                          :: uintptr(77)
+SYS_readlinkat                   :: uintptr(78)
+SYS_fstatat                      :: uintptr(79)
+SYS_fstat                        :: uintptr(80)
+SYS_sync                         :: uintptr(81)
+SYS_fsync                        :: uintptr(82)
+SYS_fdatasync                    :: uintptr(83)
+SYS_sync_file_range2             :: uintptr(84)
+SYS_sync_file_range              :: uintptr(84)
+SYS_timerfd_create               :: uintptr(85)
+SYS_timerfd_settime              :: uintptr(86)
+SYS_timerfd_gettime              :: uintptr(87)
+SYS_utimensat                    :: uintptr(88)
+SYS_acct                         :: uintptr(89)
+SYS_capget                       :: uintptr(90)
+SYS_capset                       :: uintptr(91)
+SYS_personality                  :: uintptr(92)
+SYS_exit                         :: uintptr(93)
+SYS_exit_group                   :: uintptr(94)
+SYS_waitid                       :: uintptr(95)
+SYS_set_tid_address              :: uintptr(96)
+SYS_unshare                      :: uintptr(97)
+SYS_futex                        :: uintptr(98)
+SYS_set_robust_list              :: uintptr(99)
+SYS_get_robust_list              :: uintptr(100)
+SYS_nanosleep                    :: uintptr(101)
+SYS_getitimer                    :: uintptr(102)
+SYS_setitimer                    :: uintptr(103)
+SYS_kexec_load                   :: uintptr(104)
+SYS_init_module                  :: uintptr(105)
+SYS_delete_module                :: uintptr(106)
+SYS_timer_create                 :: uintptr(107)
+SYS_timer_gettime                :: uintptr(108)
+SYS_timer_getoverrun             :: uintptr(109)
+SYS_timer_settime                :: uintptr(110)
+SYS_timer_delete                 :: uintptr(111)
+SYS_clock_settime                :: uintptr(112)
+SYS_clock_gettime                :: uintptr(113)
+SYS_clock_getres                 :: uintptr(114)
+SYS_clock_nanosleep              :: uintptr(115)
+SYS_syslog                       :: uintptr(116)
+SYS_ptrace                       :: uintptr(117)
+SYS_sched_setparam               :: uintptr(118)
+SYS_sched_setscheduler           :: uintptr(119)
+SYS_sched_getscheduler           :: uintptr(120)
+SYS_sched_getparam               :: uintptr(121)
+SYS_sched_setaffinity            :: uintptr(122)
+SYS_sched_getaffinity            :: uintptr(123)
+SYS_sched_yield                  :: uintptr(124)
+SYS_sched_get_priority_max       :: uintptr(125)
+SYS_sched_get_priority_min       :: uintptr(126)
+SYS_sched_rr_get_interval        :: uintptr(127)
+SYS_restart_syscall              :: uintptr(128)
+SYS_kill                         :: uintptr(129)
+SYS_tkill                        :: uintptr(130)
+SYS_tgkill                       :: uintptr(131)
+SYS_sigaltstack                  :: uintptr(132)
+SYS_rt_sigsuspend                :: uintptr(133)
+SYS_rt_sigaction                 :: uintptr(134)
+SYS_rt_sigprocmask               :: uintptr(135)
+SYS_rt_sigpending                :: uintptr(136)
+SYS_rt_sigtimedwait              :: uintptr(137)
+SYS_rt_sigqueueinfo              :: uintptr(138)
+SYS_rt_sigreturn                 :: uintptr(139)
+SYS_setpriority                  :: uintptr(140)
+SYS_getpriority                  :: uintptr(141)
+SYS_reboot                       :: uintptr(142)
+SYS_setregid                     :: uintptr(143)
+SYS_setgid                       :: uintptr(144)
+SYS_setreuid                     :: uintptr(145)
+SYS_setuid                       :: uintptr(146)
+SYS_setresuid                    :: uintptr(147)
+SYS_getresuid                    :: uintptr(148)
+SYS_setresgid                    :: uintptr(149)
+SYS_getresgid                    :: uintptr(150)
+SYS_setfsuid                     :: uintptr(151)
+SYS_setfsgid                     :: uintptr(152)
+SYS_times                        :: uintptr(153)
+SYS_setpgid                      :: uintptr(154)
+SYS_getpgid                      :: uintptr(155)
+SYS_getsid                       :: uintptr(156)
+SYS_setsid                       :: uintptr(157)
+SYS_getgroups                    :: uintptr(158)
+SYS_setgroups                    :: uintptr(159)
+SYS_uname                        :: uintptr(160)
+SYS_sethostname                  :: uintptr(161)
+SYS_setdomainname                :: uintptr(162)
+SYS_getrlimit                    :: uintptr(163)
+SYS_setrlimit                    :: uintptr(164)
+SYS_getrusage                    :: uintptr(165)
+SYS_umask                        :: uintptr(166)
+SYS_prctl                        :: uintptr(167)
+SYS_getcpu                       :: uintptr(168)
+SYS_gettimeofday                 :: uintptr(169)
+SYS_settimeofday                 :: uintptr(170)
+SYS_adjtimex                     :: uintptr(171)
+SYS_getpid                       :: uintptr(172)
+SYS_getppid                      :: uintptr(173)
+SYS_getuid                       :: uintptr(174)
+SYS_geteuid                      :: uintptr(175)
+SYS_getgid                       :: uintptr(176)
+SYS_getegid                      :: uintptr(177)
+SYS_gettid                       :: uintptr(178)
+SYS_sysinfo                      :: uintptr(179)
+SYS_mq_open                      :: uintptr(180)
+SYS_mq_unlink                    :: uintptr(181)
+SYS_mq_timedsend                 :: uintptr(182)
+SYS_mq_timedreceive              :: uintptr(183)
+SYS_mq_notify                    :: uintptr(184)
+SYS_mq_getsetattr                :: uintptr(185)
+SYS_msgget                       :: uintptr(186)
+SYS_msgctl                       :: uintptr(187)
+SYS_msgrcv                       :: uintptr(188)
+SYS_msgsnd                       :: uintptr(189)
+SYS_semget                       :: uintptr(190)
+SYS_semctl                       :: uintptr(191)
+SYS_semtimedop                   :: uintptr(192)
+SYS_semop                        :: uintptr(193)
+SYS_shmget                       :: uintptr(194)
+SYS_shmctl                       :: uintptr(195)
+SYS_shmat                        :: uintptr(196)
+SYS_shmdt                        :: uintptr(197)
+SYS_socket                       :: uintptr(198)
+SYS_socketpair                   :: uintptr(199)
+SYS_bind                         :: uintptr(200)
+SYS_listen                       :: uintptr(201)
+SYS_accept                       :: uintptr(202)
+SYS_connect                      :: uintptr(203)
+SYS_getsockname                  :: uintptr(204)
+SYS_getpeername                  :: uintptr(205)
+SYS_sendto                       :: uintptr(206)
+SYS_recvfrom                     :: uintptr(207)
+SYS_setsockopt                   :: uintptr(208)
+SYS_getsockopt                   :: uintptr(209)
+SYS_shutdown                     :: uintptr(210)
+SYS_sendmsg                      :: uintptr(211)
+SYS_recvmsg                      :: uintptr(212)
+SYS_readahead                    :: uintptr(213)
+SYS_brk                          :: uintptr(214)
+SYS_munmap                       :: uintptr(215)
+SYS_mremap                       :: uintptr(216)
+SYS_add_key                      :: uintptr(217)
+SYS_request_key                  :: uintptr(218)
+SYS_keyctl                       :: uintptr(219)
+SYS_clone                        :: uintptr(220)
+SYS_execve                       :: uintptr(221)
+SYS_mmap                         :: uintptr(222)
+SYS_fadvise64                    :: uintptr(223)
+SYS_swapon                       :: uintptr(224)
+SYS_swapoff                      :: uintptr(225)
+SYS_mprotect                     :: uintptr(226)
+SYS_msync                        :: uintptr(227)
+SYS_mlock                        :: uintptr(228)
+SYS_munlock                      :: uintptr(229)
+SYS_mlockall                     :: uintptr(230)
+SYS_munlockall                   :: uintptr(231)
+SYS_mincore                      :: uintptr(232)
+SYS_madvise                      :: uintptr(233)
+SYS_remap_file_pages             :: uintptr(234)
+SYS_mbind                        :: uintptr(235)
+SYS_get_mempolicy                :: uintptr(236)
+SYS_set_mempolicy                :: uintptr(237)
+SYS_migrate_pages                :: uintptr(238)
+SYS_move_pages                   :: uintptr(239)
+SYS_rt_tgsigqueueinfo            :: uintptr(240)
+SYS_perf_event_open              :: uintptr(241)
+SYS_accept4                      :: uintptr(242)
+SYS_recvmmsg                     :: uintptr(243)
+SYS_wait4                        :: uintptr(260)
+SYS_prlimit64                    :: uintptr(261)
+SYS_fanotify_init                :: uintptr(262)
+SYS_fanotify_mark                :: uintptr(263)
+SYS_name_to_handle_at            :: uintptr(264)
+SYS_open_by_handle_at            :: uintptr(265)
+SYS_clock_adjtime                :: uintptr(266)
+SYS_syncfs                       :: uintptr(267)
+SYS_setns                        :: uintptr(268)
+SYS_sendmmsg                     :: uintptr(269)
+SYS_process_vm_readv             :: uintptr(270)
+SYS_process_vm_writev            :: uintptr(271)
+SYS_kcmp                         :: uintptr(272)
+SYS_finit_module                 :: uintptr(273)
+SYS_sched_setattr                :: uintptr(274)
+SYS_sched_getattr                :: uintptr(275)
+SYS_renameat2                    :: uintptr(276)
+SYS_seccomp                      :: uintptr(277)
+SYS_getrandom                    :: uintptr(278)
+SYS_memfd_create                 :: uintptr(279)
+SYS_bpf                          :: uintptr(280)
+SYS_execveat                     :: uintptr(281)
+SYS_userfaultfd                  :: uintptr(282)
+SYS_membarrier                   :: uintptr(283)
+SYS_mlock2                       :: uintptr(284)
+SYS_copy_file_range              :: uintptr(285)
+SYS_preadv2                      :: uintptr(286)
+SYS_pwritev2                     :: uintptr(287)
+SYS_pkey_mprotect                :: uintptr(288)
+SYS_pkey_alloc                   :: uintptr(289)
+SYS_pkey_free                    :: uintptr(290)
+SYS_statx                        :: uintptr(291)
+SYS_io_pgetevents                :: uintptr(292)
+SYS_rseq                         :: uintptr(293)
+SYS_kexec_file_load              :: uintptr(294)
+SYS_clock_gettime64              :: uintptr(403)
+SYS_clock_settime64              :: uintptr(404)
+SYS_clock_adjtime64              :: uintptr(405)
+SYS_clock_getres_time64          :: uintptr(406)
+SYS_clock_nanosleep_time64       :: uintptr(407)
+SYS_timer_gettime64              :: uintptr(408)
+SYS_timer_settime64              :: uintptr(409)
+SYS_timerfd_gettime64            :: uintptr(410)
+SYS_timerfd_settime64            :: uintptr(411)
+SYS_utimensat_time64             :: uintptr(412)
+SYS_pselect6_time64              :: uintptr(413)
+SYS_ppoll_time64                 :: uintptr(414)
+SYS_io_pgetevents_time64         :: uintptr(416)
+SYS_recvmmsg_time64              :: uintptr(417)
+SYS_mq_timedsend_time64          :: uintptr(418)
+SYS_mq_timedreceive_time64       :: uintptr(419)
+SYS_semtimedop_time64            :: uintptr(420)
+SYS_rt_sigtimedwait_time64       :: uintptr(421)
+SYS_futex_time64                 :: uintptr(422)
+SYS_sched_rr_get_interval_time64 :: uintptr(423)
+SYS_pidfd_send_signal            :: uintptr(424)
+SYS_io_uring_setup               :: uintptr(425)
+SYS_io_uring_enter               :: uintptr(426)
+SYS_io_uring_register            :: uintptr(427)
+SYS_open_tree                    :: uintptr(428)
+SYS_move_mount                   :: uintptr(429)
+SYS_fsopen                       :: uintptr(430)
+SYS_fsconfig                     :: uintptr(431)
+SYS_fsmount                      :: uintptr(432)
+SYS_fspick                       :: uintptr(433)
+SYS_pidfd_open                   :: uintptr(434)
+SYS_clone3                       :: uintptr(435)
+SYS_close_range                  :: uintptr(436)
+SYS_openat2                      :: uintptr(437)
+SYS_pidfd_getfd                  :: uintptr(438)
+SYS_faccessat2                   :: uintptr(439)
+SYS_process_madvise              :: uintptr(440)
+SYS_epoll_pwait2                 :: uintptr(441)
+SYS_mount_setattr                :: uintptr(442)
+SYS_quotactl_fd                  :: uintptr(443)
+SYS_landlock_create_ruleset      :: uintptr(444)
+SYS_landlock_add_rule            :: uintptr(445)
+SYS_landlock_restrict_self       :: uintptr(446)
+SYS_memfd_secret                 :: uintptr(447)
+SYS_process_mrelease             :: uintptr(448)
+SYS_futex_waitv                  :: uintptr(449)
+SYS_set_mempolicy_home_node      :: uintptr(450)
+SYS_cachestat                    :: uintptr(451)
+SYS_fchmodat2                    :: uintptr(452)

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

@@ -120,6 +120,25 @@ when ODIN_ARCH == .amd64 {
 		_:         [3]uint,
 		_:         [3]uint,
 	}
 	}
 } else when ODIN_ARCH == .arm64 {
 } else when ODIN_ARCH == .arm64 {
+	_Arch_Stat :: struct {
+		dev:        Dev,
+		ino:        Inode,
+		mode:       Mode,
+		nlink:      u32,
+		uid:        Uid,
+		gid:        Gid,
+		rdev:       Dev,
+		_:          u64,
+		size:       int,
+		blksize:    i32,
+		_:          i32,
+		blocks:     int,
+		atime:      Time_Spec,
+		mtime:      Time_Spec,
+		ctime:      Time_Spec,
+		_:          [2]u32,
+	}
+} else when ODIN_ARCH == .riscv64 {
 	_Arch_Stat :: struct {
 	_Arch_Stat :: struct {
 		dev:        Dev,
 		dev:        Dev,
 		ino:        Inode,
 		ino:        Inode,
@@ -927,7 +946,7 @@ when ODIN_ARCH == .i386 {
 		nsems:      uint,
 		nsems:      uint,
 		_:          [2]uint,
 		_:          [2]uint,
 	}
 	}
-} else when ODIN_ARCH == .arm64 {
+} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 	_Arch_Semid_DS :: struct {
 	_Arch_Semid_DS :: struct {
 		perm:       IPC_Perm,
 		perm:       IPC_Perm,
 		otime:      int,
 		otime:      int,
@@ -1167,6 +1186,33 @@ when ODIN_ARCH == .arm32 {
 		xmm_space:        [32]uint,
 		xmm_space:        [32]uint,
 		padding:          [56]uint,
 		padding:          [56]uint,
 	}
 	}
+} else when ODIN_ARCH == .riscv64 {
+	_Arch_User_Regs :: struct {
+		pc, ra, sp, gp, tp,
+		t0, t1, t2,
+		s0, s1,
+		a0, a1, a2, a3, a4, a5, a6, a7,
+		s2, s3, s4, s5, s6, s7, s8, s9, s10, s11,
+		t3, t4, t5, t6: uint,
+	}
+	_Arch_User_FP_Regs :: struct #raw_union {
+		f_ext: struct {
+			f:    [32]u32,
+			fcsr: u32,
+		},
+		d_ext: struct {
+			f:    [32]u64,
+			fcsr: u32,
+		},
+		q_ext: struct {
+			using _: struct #align(16) {
+				f:    [64]u64,
+			},
+			fcsr:     u32,
+			reserved: [3]u32,
+		},
+	}
+	_Arch_User_FPX_Regs :: struct {}
 }
 }
 
 
 /*
 /*

+ 356 - 24
core/sys/unix/syscalls_linux.odin

@@ -1514,6 +1514,338 @@ when ODIN_ARCH == .amd64 {
 	SYS_landlock_create_ruleset : uintptr : 444
 	SYS_landlock_create_ruleset : uintptr : 444
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_add_rule : uintptr : 445
 	SYS_landlock_restrict_self : uintptr : 446
 	SYS_landlock_restrict_self : uintptr : 446
+} else when ODIN_ARCH == .riscv64 {
+	SYS_io_setup                     :: uintptr(0)
+	SYS_io_destroy                   :: uintptr(1)
+	SYS_io_submit                    :: uintptr(2)
+	SYS_io_cancel                    :: uintptr(3)
+	SYS_io_getevents                 :: uintptr(4)
+	SYS_setxattr                     :: uintptr(5)
+	SYS_lsetxattr                    :: uintptr(6)
+	SYS_fsetxattr                    :: uintptr(7)
+	SYS_getxattr                     :: uintptr(8)
+	SYS_lgetxattr                    :: uintptr(9)
+	SYS_fgetxattr                    :: uintptr(10)
+	SYS_listxattr                    :: uintptr(11)
+	SYS_llistxattr                   :: uintptr(12)
+	SYS_flistxattr                   :: uintptr(13)
+	SYS_removexattr                  :: uintptr(14)
+	SYS_lremovexattr                 :: uintptr(15)
+	SYS_fremovexattr                 :: uintptr(16)
+	SYS_getcwd                       :: uintptr(17)
+	SYS_lookup_dcookie               :: uintptr(18)
+	SYS_eventfd2                     :: uintptr(19)
+	SYS_epoll_create1                :: uintptr(20)
+	SYS_epoll_ctl                    :: uintptr(21)
+	SYS_epoll_pwait                  :: uintptr(22)
+	SYS_dup                          :: uintptr(23)
+	SYS_dup3                         :: uintptr(24)
+	SYS_fcntl                        :: uintptr(25)
+	SYS_inotify_init1                :: uintptr(26)
+	SYS_inotify_add_watch            :: uintptr(27)
+	SYS_inotify_rm_watch             :: uintptr(28)
+	SYS_ioctl                        :: uintptr(29)
+	SYS_ioprio_set                   :: uintptr(30)
+	SYS_ioprio_get                   :: uintptr(31)
+	SYS_flock                        :: uintptr(32)
+	SYS_mknodat                      :: uintptr(33)
+	SYS_mkdirat                      :: uintptr(34)
+	SYS_unlinkat                     :: uintptr(35)
+	SYS_symlinkat                    :: uintptr(36)
+	SYS_linkat                       :: uintptr(37)
+	SYS_renameat                     :: uintptr(38)
+	SYS_umount2                      :: uintptr(39)
+	SYS_mount                        :: uintptr(40)
+	SYS_pivot_root                   :: uintptr(41)
+	SYS_nfsservctl                   :: uintptr(42)
+	SYS_statfs                       :: uintptr(43)
+	SYS_fstatfs                      :: uintptr(44)
+	SYS_truncate                     :: uintptr(45)
+	SYS_ftruncate                    :: uintptr(46)
+	SYS_fallocate                    :: uintptr(47)
+	SYS_faccessat                    :: uintptr(48)
+	SYS_chdir                        :: uintptr(49)
+	SYS_fchdir                       :: uintptr(50)
+	SYS_chroot                       :: uintptr(51)
+	SYS_fchmod                       :: uintptr(52)
+	SYS_fchmodat                     :: uintptr(53)
+	SYS_fchownat                     :: uintptr(54)
+	SYS_fchown                       :: uintptr(55)
+	SYS_openat                       :: uintptr(56)
+	SYS_close                        :: uintptr(57)
+	SYS_vhangup                      :: uintptr(58)
+	SYS_pipe2                        :: uintptr(59)
+	SYS_quotactl                     :: uintptr(60)
+	SYS_getdents64                   :: uintptr(61)
+	SYS_lseek                        :: uintptr(62)
+	SYS_read                         :: uintptr(63)
+	SYS_write                        :: uintptr(64)
+	SYS_readv                        :: uintptr(65)
+	SYS_writev                       :: uintptr(66)
+	SYS_pread64                      :: uintptr(67)
+	SYS_pwrite64                     :: uintptr(68)
+	SYS_preadv                       :: uintptr(69)
+	SYS_pwritev                      :: uintptr(70)
+	SYS_sendfile                     :: uintptr(71)
+	SYS_pselect6                     :: uintptr(72)
+	SYS_ppoll                        :: uintptr(73)
+	SYS_signalfd4                    :: uintptr(74)
+	SYS_vmsplice                     :: uintptr(75)
+	SYS_splice                       :: uintptr(76)
+	SYS_tee                          :: uintptr(77)
+	SYS_readlinkat                   :: uintptr(78)
+	SYS_fstatat                      :: uintptr(79)
+	SYS_fstat                        :: uintptr(80)
+	SYS_sync                         :: uintptr(81)
+	SYS_fsync                        :: uintptr(82)
+	SYS_fdatasync                    :: uintptr(83)
+	SYS_sync_file_range2             :: uintptr(84)
+	SYS_sync_file_range              :: uintptr(84)
+	SYS_timerfd_create               :: uintptr(85)
+	SYS_timerfd_settime              :: uintptr(86)
+	SYS_timerfd_gettime              :: uintptr(87)
+	SYS_utimensat                    :: uintptr(88)
+	SYS_acct                         :: uintptr(89)
+	SYS_capget                       :: uintptr(90)
+	SYS_capset                       :: uintptr(91)
+	SYS_personality                  :: uintptr(92)
+	SYS_exit                         :: uintptr(93)
+	SYS_exit_group                   :: uintptr(94)
+	SYS_waitid                       :: uintptr(95)
+	SYS_set_tid_address              :: uintptr(96)
+	SYS_unshare                      :: uintptr(97)
+	SYS_futex                        :: uintptr(98)
+	SYS_set_robust_list              :: uintptr(99)
+	SYS_get_robust_list              :: uintptr(100)
+	SYS_nanosleep                    :: uintptr(101)
+	SYS_getitimer                    :: uintptr(102)
+	SYS_setitimer                    :: uintptr(103)
+	SYS_kexec_load                   :: uintptr(104)
+	SYS_init_module                  :: uintptr(105)
+	SYS_delete_module                :: uintptr(106)
+	SYS_timer_create                 :: uintptr(107)
+	SYS_timer_gettime                :: uintptr(108)
+	SYS_timer_getoverrun             :: uintptr(109)
+	SYS_timer_settime                :: uintptr(110)
+	SYS_timer_delete                 :: uintptr(111)
+	SYS_clock_settime                :: uintptr(112)
+	SYS_clock_gettime                :: uintptr(113)
+	SYS_clock_getres                 :: uintptr(114)
+	SYS_clock_nanosleep              :: uintptr(115)
+	SYS_syslog                       :: uintptr(116)
+	SYS_ptrace                       :: uintptr(117)
+	SYS_sched_setparam               :: uintptr(118)
+	SYS_sched_setscheduler           :: uintptr(119)
+	SYS_sched_getscheduler           :: uintptr(120)
+	SYS_sched_getparam               :: uintptr(121)
+	SYS_sched_setaffinity            :: uintptr(122)
+	SYS_sched_getaffinity            :: uintptr(123)
+	SYS_sched_yield                  :: uintptr(124)
+	SYS_sched_get_priority_max       :: uintptr(125)
+	SYS_sched_get_priority_min       :: uintptr(126)
+	SYS_sched_rr_get_interval        :: uintptr(127)
+	SYS_restart_syscall              :: uintptr(128)
+	SYS_kill                         :: uintptr(129)
+	SYS_tkill                        :: uintptr(130)
+	SYS_tgkill                       :: uintptr(131)
+	SYS_sigaltstack                  :: uintptr(132)
+	SYS_rt_sigsuspend                :: uintptr(133)
+	SYS_rt_sigaction                 :: uintptr(134)
+	SYS_rt_sigprocmask               :: uintptr(135)
+	SYS_rt_sigpending                :: uintptr(136)
+	SYS_rt_sigtimedwait              :: uintptr(137)
+	SYS_rt_sigqueueinfo              :: uintptr(138)
+	SYS_rt_sigreturn                 :: uintptr(139)
+	SYS_setpriority                  :: uintptr(140)
+	SYS_getpriority                  :: uintptr(141)
+	SYS_reboot                       :: uintptr(142)
+	SYS_setregid                     :: uintptr(143)
+	SYS_setgid                       :: uintptr(144)
+	SYS_setreuid                     :: uintptr(145)
+	SYS_setuid                       :: uintptr(146)
+	SYS_setresuid                    :: uintptr(147)
+	SYS_getresuid                    :: uintptr(148)
+	SYS_setresgid                    :: uintptr(149)
+	SYS_getresgid                    :: uintptr(150)
+	SYS_setfsuid                     :: uintptr(151)
+	SYS_setfsgid                     :: uintptr(152)
+	SYS_times                        :: uintptr(153)
+	SYS_setpgid                      :: uintptr(154)
+	SYS_getpgid                      :: uintptr(155)
+	SYS_getsid                       :: uintptr(156)
+	SYS_setsid                       :: uintptr(157)
+	SYS_getgroups                    :: uintptr(158)
+	SYS_setgroups                    :: uintptr(159)
+	SYS_uname                        :: uintptr(160)
+	SYS_sethostname                  :: uintptr(161)
+	SYS_setdomainname                :: uintptr(162)
+	SYS_getrlimit                    :: uintptr(163)
+	SYS_setrlimit                    :: uintptr(164)
+	SYS_getrusage                    :: uintptr(165)
+	SYS_umask                        :: uintptr(166)
+	SYS_prctl                        :: uintptr(167)
+	SYS_getcpu                       :: uintptr(168)
+	SYS_gettimeofday                 :: uintptr(169)
+	SYS_settimeofday                 :: uintptr(170)
+	SYS_adjtimex                     :: uintptr(171)
+	SYS_getpid                       :: uintptr(172)
+	SYS_getppid                      :: uintptr(173)
+	SYS_getuid                       :: uintptr(174)
+	SYS_geteuid                      :: uintptr(175)
+	SYS_getgid                       :: uintptr(176)
+	SYS_getegid                      :: uintptr(177)
+	SYS_gettid                       :: uintptr(178)
+	SYS_sysinfo                      :: uintptr(179)
+	SYS_mq_open                      :: uintptr(180)
+	SYS_mq_unlink                    :: uintptr(181)
+	SYS_mq_timedsend                 :: uintptr(182)
+	SYS_mq_timedreceive              :: uintptr(183)
+	SYS_mq_notify                    :: uintptr(184)
+	SYS_mq_getsetattr                :: uintptr(185)
+	SYS_msgget                       :: uintptr(186)
+	SYS_msgctl                       :: uintptr(187)
+	SYS_msgrcv                       :: uintptr(188)
+	SYS_msgsnd                       :: uintptr(189)
+	SYS_semget                       :: uintptr(190)
+	SYS_semctl                       :: uintptr(191)
+	SYS_semtimedop                   :: uintptr(192)
+	SYS_semop                        :: uintptr(193)
+	SYS_shmget                       :: uintptr(194)
+	SYS_shmctl                       :: uintptr(195)
+	SYS_shmat                        :: uintptr(196)
+	SYS_shmdt                        :: uintptr(197)
+	SYS_socket                       :: uintptr(198)
+	SYS_socketpair                   :: uintptr(199)
+	SYS_bind                         :: uintptr(200)
+	SYS_listen                       :: uintptr(201)
+	SYS_accept                       :: uintptr(202)
+	SYS_connect                      :: uintptr(203)
+	SYS_getsockname                  :: uintptr(204)
+	SYS_getpeername                  :: uintptr(205)
+	SYS_sendto                       :: uintptr(206)
+	SYS_recvfrom                     :: uintptr(207)
+	SYS_setsockopt                   :: uintptr(208)
+	SYS_getsockopt                   :: uintptr(209)
+	SYS_shutdown                     :: uintptr(210)
+	SYS_sendmsg                      :: uintptr(211)
+	SYS_recvmsg                      :: uintptr(212)
+	SYS_readahead                    :: uintptr(213)
+	SYS_brk                          :: uintptr(214)
+	SYS_munmap                       :: uintptr(215)
+	SYS_mremap                       :: uintptr(216)
+	SYS_add_key                      :: uintptr(217)
+	SYS_request_key                  :: uintptr(218)
+	SYS_keyctl                       :: uintptr(219)
+	SYS_clone                        :: uintptr(220)
+	SYS_execve                       :: uintptr(221)
+	SYS_mmap                         :: uintptr(222)
+	SYS_fadvise64                    :: uintptr(223)
+	SYS_swapon                       :: uintptr(224)
+	SYS_swapoff                      :: uintptr(225)
+	SYS_mprotect                     :: uintptr(226)
+	SYS_msync                        :: uintptr(227)
+	SYS_mlock                        :: uintptr(228)
+	SYS_munlock                      :: uintptr(229)
+	SYS_mlockall                     :: uintptr(230)
+	SYS_munlockall                   :: uintptr(231)
+	SYS_mincore                      :: uintptr(232)
+	SYS_madvise                      :: uintptr(233)
+	SYS_remap_file_pages             :: uintptr(234)
+	SYS_mbind                        :: uintptr(235)
+	SYS_get_mempolicy                :: uintptr(236)
+	SYS_set_mempolicy                :: uintptr(237)
+	SYS_migrate_pages                :: uintptr(238)
+	SYS_move_pages                   :: uintptr(239)
+	SYS_rt_tgsigqueueinfo            :: uintptr(240)
+	SYS_perf_event_open              :: uintptr(241)
+	SYS_accept4                      :: uintptr(242)
+	SYS_recvmmsg                     :: uintptr(243)
+	SYS_wait4                        :: uintptr(260)
+	SYS_prlimit64                    :: uintptr(261)
+	SYS_fanotify_init                :: uintptr(262)
+	SYS_fanotify_mark                :: uintptr(263)
+	SYS_name_to_handle_at            :: uintptr(264)
+	SYS_open_by_handle_at            :: uintptr(265)
+	SYS_clock_adjtime                :: uintptr(266)
+	SYS_syncfs                       :: uintptr(267)
+	SYS_setns                        :: uintptr(268)
+	SYS_sendmmsg                     :: uintptr(269)
+	SYS_process_vm_readv             :: uintptr(270)
+	SYS_process_vm_writev            :: uintptr(271)
+	SYS_kcmp                         :: uintptr(272)
+	SYS_finit_module                 :: uintptr(273)
+	SYS_sched_setattr                :: uintptr(274)
+	SYS_sched_getattr                :: uintptr(275)
+	SYS_renameat2                    :: uintptr(276)
+	SYS_seccomp                      :: uintptr(277)
+	SYS_getrandom                    :: uintptr(278)
+	SYS_memfd_create                 :: uintptr(279)
+	SYS_bpf                          :: uintptr(280)
+	SYS_execveat                     :: uintptr(281)
+	SYS_userfaultfd                  :: uintptr(282)
+	SYS_membarrier                   :: uintptr(283)
+	SYS_mlock2                       :: uintptr(284)
+	SYS_copy_file_range              :: uintptr(285)
+	SYS_preadv2                      :: uintptr(286)
+	SYS_pwritev2                     :: uintptr(287)
+	SYS_pkey_mprotect                :: uintptr(288)
+	SYS_pkey_alloc                   :: uintptr(289)
+	SYS_pkey_free                    :: uintptr(290)
+	SYS_statx                        :: uintptr(291)
+	SYS_io_pgetevents                :: uintptr(292)
+	SYS_rseq                         :: uintptr(293)
+	SYS_kexec_file_load              :: uintptr(294)
+	SYS_clock_gettime64              :: uintptr(403)
+	SYS_clock_settime64              :: uintptr(404)
+	SYS_clock_adjtime64              :: uintptr(405)
+	SYS_clock_getres_time64          :: uintptr(406)
+	SYS_clock_nanosleep_time64       :: uintptr(407)
+	SYS_timer_gettime64              :: uintptr(408)
+	SYS_timer_settime64              :: uintptr(409)
+	SYS_timerfd_gettime64            :: uintptr(410)
+	SYS_timerfd_settime64            :: uintptr(411)
+	SYS_utimensat_time64             :: uintptr(412)
+	SYS_pselect6_time64              :: uintptr(413)
+	SYS_ppoll_time64                 :: uintptr(414)
+	SYS_io_pgetevents_time64         :: uintptr(416)
+	SYS_recvmmsg_time64              :: uintptr(417)
+	SYS_mq_timedsend_time64          :: uintptr(418)
+	SYS_mq_timedreceive_time64       :: uintptr(419)
+	SYS_semtimedop_time64            :: uintptr(420)
+	SYS_rt_sigtimedwait_time64       :: uintptr(421)
+	SYS_futex_time64                 :: uintptr(422)
+	SYS_sched_rr_get_interval_time64 :: uintptr(423)
+	SYS_pidfd_send_signal            :: uintptr(424)
+	SYS_io_uring_setup               :: uintptr(425)
+	SYS_io_uring_enter               :: uintptr(426)
+	SYS_io_uring_register            :: uintptr(427)
+	SYS_open_tree                    :: uintptr(428)
+	SYS_move_mount                   :: uintptr(429)
+	SYS_fsopen                       :: uintptr(430)
+	SYS_fsconfig                     :: uintptr(431)
+	SYS_fsmount                      :: uintptr(432)
+	SYS_fspick                       :: uintptr(433)
+	SYS_pidfd_open                   :: uintptr(434)
+	SYS_clone3                       :: uintptr(435)
+	SYS_close_range                  :: uintptr(436)
+	SYS_openat2                      :: uintptr(437)
+	SYS_pidfd_getfd                  :: uintptr(438)
+	SYS_faccessat2                   :: uintptr(439)
+	SYS_process_madvise              :: uintptr(440)
+	SYS_epoll_pwait2                 :: uintptr(441)
+	SYS_mount_setattr                :: uintptr(442)
+	SYS_quotactl_fd                  :: uintptr(443)
+	SYS_landlock_create_ruleset      :: uintptr(444)
+	SYS_landlock_add_rule            :: uintptr(445)
+	SYS_landlock_restrict_self       :: uintptr(446)
+	SYS_memfd_secret                 :: uintptr(447)
+	SYS_process_mrelease             :: uintptr(448)
+	SYS_futex_waitv                  :: uintptr(449)
+	SYS_set_mempolicy_home_node      :: uintptr(450)
+	SYS_cachestat                    :: uintptr(451)
+	SYS_fchmodat2                    :: uintptr(452)
+
+	SIGCHLD :: 17
 } else {
 } else {
 	#panic("Unsupported architecture")
 	#panic("Unsupported architecture")
 }
 }
@@ -1742,7 +2074,7 @@ sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: uint, flags: int) ->
 }
 }
 
 
 sys_open :: proc "contextless" (path: cstring, flags: int, mode: uint = 0o000) -> int {
 sys_open :: proc "contextless" (path: cstring, flags: int, mode: uint = 0o000) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
 	} else { // NOTE: arm64 does not have open
 	} else { // NOTE: arm64 does not have open
 		return int(intrinsics.syscall(SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
@@ -1762,7 +2094,7 @@ sys_read :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int {
 }
 }
 
 
 sys_pread :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
 sys_pread :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
 		return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
 	} else {
 	} else {
 		low := uintptr(offset & 0xFFFFFFFF)
 		low := uintptr(offset & 0xFFFFFFFF)
@@ -1776,7 +2108,7 @@ sys_write :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int {
 }
 }
 
 
 sys_pwrite :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
 sys_pwrite :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
 		return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
 	} else {
 	} else {
 		low := uintptr(offset & 0xFFFFFFFF)
 		low := uintptr(offset & 0xFFFFFFFF)
@@ -1786,7 +2118,7 @@ sys_pwrite :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64)
 }
 }
 
 
 sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
 sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return i64(intrinsics.syscall(SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
 		return i64(intrinsics.syscall(SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
 	} else {
 	} else {
 		low := uintptr(offset & 0xFFFFFFFF)
 		low := uintptr(offset & 0xFFFFFFFF)
@@ -1800,7 +2132,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
 sys_stat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 sys_stat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 	when ODIN_ARCH == .amd64 {
 	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != .arm64 {
+	} else when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have stat
 	} else { // NOTE: arm64 does not have stat
 		return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
 		return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
@@ -1808,7 +2140,7 @@ sys_stat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 }
 }
 
 
 sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int {
 sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat)))
 	} else {
 	} else {
 		return int(intrinsics.syscall(SYS_fstat64, uintptr(fd), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_fstat64, uintptr(fd), uintptr(stat)))
@@ -1818,7 +2150,7 @@ sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int {
 sys_lstat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 sys_lstat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 	when ODIN_ARCH == .amd64 {
 	when ODIN_ARCH == .amd64 {
 		return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
-	} else when ODIN_ARCH != .arm64 {
+	} else when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
 		return int(intrinsics.syscall(SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
 	} else { // NOTE: arm64 does not have any lstat
 	} else { // NOTE: arm64 does not have any lstat
 		return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
 		return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
@@ -1826,7 +2158,7 @@ sys_lstat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
 }
 }
 
 
 sys_readlink :: proc "contextless" (path: cstring, buf: rawptr, bufsiz: uint) -> int {
 sys_readlink :: proc "contextless" (path: cstring, buf: rawptr, bufsiz: uint) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 		return int(intrinsics.syscall(SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 	} else { // NOTE: arm64 does not have readlink
 	} else { // NOTE: arm64 does not have readlink
 		return int(intrinsics.syscall(SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
 		return int(intrinsics.syscall(SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
@@ -1834,7 +2166,7 @@ sys_readlink :: proc "contextless" (path: cstring, buf: rawptr, bufsiz: uint) ->
 }
 }
 
 
 sys_symlink :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
 sys_symlink :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_symlink, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
 		return int(intrinsics.syscall(SYS_symlink, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
 	} else { // NOTE: arm64 does not have symlink
 	} else { // NOTE: arm64 does not have symlink
 		return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name))))
 		return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name))))
@@ -1842,7 +2174,7 @@ sys_symlink :: proc "contextless" (old_name: cstring, new_name: cstring) -> int
 }
 }
 
 
 sys_access :: proc "contextless" (path: cstring, mask: int) -> int {
 sys_access :: proc "contextless" (path: cstring, mask: int) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_access, uintptr(rawptr(path)), uintptr(mask)))
 		return int(intrinsics.syscall(SYS_access, uintptr(rawptr(path)), uintptr(mask)))
 	} else { // NOTE: arm64 does not have access
 	} else { // NOTE: arm64 does not have access
 		return int(intrinsics.syscall(SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
 		return int(intrinsics.syscall(SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
@@ -1862,7 +2194,7 @@ sys_fchdir :: proc "contextless" (fd: int) -> int {
 }
 }
 
 
 sys_chmod :: proc "contextless" (path: cstring, mode: uint) -> int {
 sys_chmod :: proc "contextless" (path: cstring, mode: uint) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
 	} else { // NOTE: arm64 does not have chmod
 	} else { // NOTE: arm64 does not have chmod
 		return int(intrinsics.syscall(SYS_fchmodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_fchmodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
@@ -1874,7 +2206,7 @@ sys_fchmod :: proc "contextless" (fd: int, mode: uint) -> int {
 }
 }
 
 
 sys_chown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
 sys_chown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH !=. riscv64 {
 		return int(intrinsics.syscall(SYS_chown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
 		return int(intrinsics.syscall(SYS_chown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
 	} else { // NOTE: arm64 does not have chown
 	} else { // NOTE: arm64 does not have chown
 		return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), 0))
 		return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), 0))
@@ -1886,7 +2218,7 @@ sys_fchown :: proc "contextless" (fd: int, user: int, group: int) -> int {
 }
 }
 
 
 sys_lchown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
 sys_lchown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_lchown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
 		return int(intrinsics.syscall(SYS_lchown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
 	} else { // NOTE: arm64 does not have lchown
 	} else { // NOTE: arm64 does not have lchown
 		return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW))
 		return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW))
@@ -1894,7 +2226,7 @@ sys_lchown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
 }
 }
 
 
 sys_rename :: proc "contextless" (old, new: cstring) -> int {
 sys_rename :: proc "contextless" (old, new: cstring) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
 		return int(intrinsics.syscall(SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
 	} else { // NOTE: arm64 does not have rename
 	} else { // NOTE: arm64 does not have rename
 		return int(intrinsics.syscall(SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
 		return int(intrinsics.syscall(SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
@@ -1902,7 +2234,7 @@ sys_rename :: proc "contextless" (old, new: cstring) -> int {
 }
 }
 
 
 sys_link :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
 sys_link :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_link, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
 		return int(intrinsics.syscall(SYS_link, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
 	} else { // NOTE: arm64 does not have link
 	} else { // NOTE: arm64 does not have link
 		return int(intrinsics.syscall(SYS_linkat, AT_FDCWD, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW))
 		return int(intrinsics.syscall(SYS_linkat, AT_FDCWD, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW))
@@ -1910,7 +2242,7 @@ sys_link :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
 }
 }
 
 
 sys_unlink :: proc "contextless" (path: cstring) -> int {
 sys_unlink :: proc "contextless" (path: cstring) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path))))
 		return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have unlink
 	} else { // NOTE: arm64 does not have unlink
 		return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
 		return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
@@ -1922,7 +2254,7 @@ sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> i
 }
 }
 
 
 sys_rmdir :: proc "contextless" (path: cstring) -> int {
 sys_rmdir :: proc "contextless" (path: cstring) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path))))
 		return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path))))
 	} else { // NOTE: arm64 does not have rmdir
 	} else { // NOTE: arm64 does not have rmdir
 		return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
 		return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
@@ -1930,7 +2262,7 @@ sys_rmdir :: proc "contextless" (path: cstring) -> int {
 }
 }
 
 
 sys_mkdir :: proc "contextless" (path: cstring, mode: uint) -> int {
 sys_mkdir :: proc "contextless" (path: cstring, mode: uint) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
 	} else { // NOTE: arm64 does not have mkdir
 	} else { // NOTE: arm64 does not have mkdir
 		return int(intrinsics.syscall(SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
 		return int(intrinsics.syscall(SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
@@ -1942,7 +2274,7 @@ sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: uint) -> int {
 }
 }
 
 
 sys_mknod :: proc "contextless" (path: cstring, mode: uint, dev: int) -> int {
 sys_mknod :: proc "contextless" (path: cstring, mode: uint, dev: int) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
 		return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
 	} else { // NOTE: arm64 does not have mknod
 	} else { // NOTE: arm64 does not have mknod
 		return int(intrinsics.syscall(SYS_mknodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
 		return int(intrinsics.syscall(SYS_mknodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
@@ -1954,7 +2286,7 @@ sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: uint, dev: int
 }
 }
 
 
 sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int {
 sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
 		return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
 	} else {
 	} else {
 		low := uintptr(length & 0xFFFFFFFF)
 		low := uintptr(length & 0xFFFFFFFF)
@@ -1964,7 +2296,7 @@ sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int {
 }
 }
 
 
 sys_ftruncate :: proc "contextless" (fd: int, length: i64) -> int {
 sys_ftruncate :: proc "contextless" (fd: int, length: i64) -> int {
-	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length)))
 		return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length)))
 	} else {
 	} else {
 		low := uintptr(length & 0xFFFFFFFF)
 		low := uintptr(length & 0xFFFFFFFF)
@@ -1982,7 +2314,7 @@ sys_getdents64 :: proc "contextless" (fd: int, dirent: rawptr, count: int) -> in
 }
 }
 
 
 sys_fork :: proc "contextless" () -> int {
 sys_fork :: proc "contextless" () -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_fork))
 		return int(intrinsics.syscall(SYS_fork))
 	} else {
 	} else {
 		return int(intrinsics.syscall(SYS_clone, SIGCHLD))
 		return int(intrinsics.syscall(SYS_clone, SIGCHLD))
@@ -1992,7 +2324,7 @@ sys_pipe2 :: proc "contextless" (fds: rawptr, flags: int) -> int {
 	return int(intrinsics.syscall(SYS_pipe2, uintptr(fds), uintptr(flags)))
 	return int(intrinsics.syscall(SYS_pipe2, uintptr(fds), uintptr(flags)))
 }
 }
 sys_dup2 :: proc "contextless" (oldfd: int, newfd: int) -> int {
 sys_dup2 :: proc "contextless" (oldfd: int, newfd: int) -> int {
-	when ODIN_ARCH != .arm64 {
+	when ODIN_ARCH != .arm64 && ODIN_ARCH != .riscv64 {
 		return int(intrinsics.syscall(SYS_dup2, uintptr(oldfd), uintptr(newfd)))
 		return int(intrinsics.syscall(SYS_dup2, uintptr(oldfd), uintptr(newfd)))
 	} else {
 	} else {
 		return int(intrinsics.syscall(SYS_dup3, uintptr(oldfd), uintptr(newfd), 0))
 		return int(intrinsics.syscall(SYS_dup3, uintptr(oldfd), uintptr(newfd), 0))
@@ -2076,7 +2408,7 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int {
 
 
 sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int {
 sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int {
 	// NOTE: specialcased here because `arm64` does not have `poll`
 	// NOTE: specialcased here because `arm64` does not have `poll`
-	when ODIN_ARCH == .arm64 {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
 		seconds := i64(timeout / 1_000)
 		seconds := i64(timeout / 1_000)
 		nanoseconds := i64((timeout % 1000) * 1_000_000)
 		nanoseconds := i64((timeout % 1000) * 1_000_000)
 		timeout_spec := timespec{seconds, nanoseconds}
 		timeout_spec := timespec{seconds, nanoseconds}

+ 2 - 2
misc/featuregen/README.md

@@ -5,7 +5,7 @@ for features regarding microarchitecture and target features of the compiler.
 
 
 It is not pretty! But LLVM has no way to query this information with their C API.
 It is not pretty! But LLVM has no way to query this information with their C API.
 
 
-It generates these globals (intended for `src/build_settings.cpp`:
+It generates these globals (intended for `src/build_settings_microarch.cpp`:
 
 
 - `target_microarch_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of microarchitectures available on that architecture
 - `target_microarch_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of microarchitectures available on that architecture
 - `target_features_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of target features available on that architecture
 - `target_features_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of target features available on that architecture
@@ -23,6 +23,6 @@ does not impact much at all, the only thing it will do is make LLVM print a mess
 ## Usage
 ## Usage
 
 
 1. Make sure the table of architectures at the top of the python script is up-to-date (the triple can be any valid triple for the architecture)
 1. Make sure the table of architectures at the top of the python script is up-to-date (the triple can be any valid triple for the architecture)
-1. `./build.sh`
+1. `./build_featuregen.sh`
 1. `python3 featuregen.py`
 1. `python3 featuregen.py`
 1. Copy the output into `src/build_settings.cpp`
 1. Copy the output into `src/build_settings.cpp`

+ 5 - 0
misc/featuregen/build_featuregen.sh

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -ex
+
+$(llvm-config --bindir)/clang++ $(llvm-config --cxxflags --ldflags --libs) featuregen.cpp -o featuregen

+ 11 - 8
misc/featuregen/featuregen.py

@@ -4,12 +4,13 @@ import os
 import sys
 import sys
 
 
 archs = [
 archs = [
-	("amd64",     "linux_amd64", "x86_64-pc-linux-gnu", [], []),
-	("i386",      "linux_i386",  "i386-pc-linux-gnu",   [], []),
-	("arm32",     "linux_arm32", "arm-linux-gnu",       [], []),
-	("arm64",     "linux_arm64", "aarch64-linux-elf",   [], []),
-	("wasm32",    "js_wasm32",   "wasm32-js-js",        [], []),
-	("wasm64p32", "js_wasm64p32","wasm32-js-js",        [], []),
+	("amd64",     "linux_amd64",   "x86_64-pc-linux-gnu", [], []),
+	("i386",      "linux_i386",    "i386-pc-linux-gnu",   [], []),
+	("arm32",     "linux_arm32",   "arm-linux-gnu",       [], []),
+	("arm64",     "linux_arm64",   "aarch64-linux-elf",   [], []),
+	("wasm32",    "js_wasm32",     "wasm32-js-js",        [], []),
+	("wasm64p32", "js_wasm64p32",  "wasm32-js-js",        [], []),
+	("riscv64",   "linux_riscv64", "riscv64-linux-gnu",   [], []),
 ];
 ];
 
 
 SEEKING_CPUS     = 0
 SEEKING_CPUS     = 0
@@ -78,7 +79,8 @@ print("\t// TargetArch_Invalid:")
 print('\tstr_lit(""),')
 print('\tstr_lit(""),')
 for arch, target, triple, cpus, features in archs:
 for arch, target, triple, cpus, features in archs:
 	print(f"\t// TargetArch_{arch}:")
 	print(f"\t// TargetArch_{arch}:")
-	print(f'\tstr_lit("{','.join(cpus)}"),')
+	cpus_str = ','.join(cpus)
+	print(f'\tstr_lit("{cpus_str}"),')
 print("};")
 print("};")
 
 
 print("")
 print("")
@@ -89,7 +91,8 @@ print("\t// TargetArch_Invalid:")
 print('\tstr_lit(""),')
 print('\tstr_lit(""),')
 for arch, target, triple, cpus, features in archs:
 for arch, target, triple, cpus, features in archs:
 	print(f"\t// TargetArch_{arch}:")
 	print(f"\t// TargetArch_{arch}:")
-	print(f'\tstr_lit("{','.join(features)}"),')
+	features_str = ','.join(features)
+	print(f'\tstr_lit("{features_str}"),')
 print("};")
 print("};")
 
 
 print("")
 print("")

+ 19 - 1
src/build_settings.cpp

@@ -39,6 +39,7 @@ enum TargetArchKind : u16 {
 	TargetArch_arm64,
 	TargetArch_arm64,
 	TargetArch_wasm32,
 	TargetArch_wasm32,
 	TargetArch_wasm64p32,
 	TargetArch_wasm64p32,
+	TargetArch_riscv64,
 
 
 	TargetArch_COUNT,
 	TargetArch_COUNT,
 };
 };
@@ -104,6 +105,7 @@ gb_global String target_arch_names[TargetArch_COUNT] = {
 	str_lit("arm64"),
 	str_lit("arm64"),
 	str_lit("wasm32"),
 	str_lit("wasm32"),
 	str_lit("wasm64p32"),
 	str_lit("wasm64p32"),
+	str_lit("riscv64"),
 };
 };
 
 
 #include "build_settings_microarch.cpp"
 #include "build_settings_microarch.cpp"
@@ -555,13 +557,18 @@ gb_global TargetMetrics target_linux_arm64 = {
 	8, 8, 16, 32,
 	8, 8, 16, 32,
 	str_lit("aarch64-linux-elf"),
 	str_lit("aarch64-linux-elf"),
 };
 };
-
 gb_global TargetMetrics target_linux_arm32 = {
 gb_global TargetMetrics target_linux_arm32 = {
 	TargetOs_linux,
 	TargetOs_linux,
 	TargetArch_arm32,
 	TargetArch_arm32,
 	4, 4, 8, 16,
 	4, 4, 8, 16,
 	str_lit("arm-unknown-linux-gnueabihf"),
 	str_lit("arm-unknown-linux-gnueabihf"),
 };
 };
+gb_global TargetMetrics target_linux_riscv64 = {
+	TargetOs_linux,
+	TargetArch_riscv64,
+	8, 8, 16, 32,
+	str_lit("riscv64-linux-gnu"),
+};
 
 
 gb_global TargetMetrics target_darwin_amd64 = {
 gb_global TargetMetrics target_darwin_amd64 = {
 	TargetOs_darwin,
 	TargetOs_darwin,
@@ -716,6 +723,12 @@ gb_global TargetMetrics target_freestanding_arm32 = {
 	4, 4, 8, 16,
 	4, 4, 8, 16,
 	str_lit("arm-unknown-unknown-gnueabihf"),
 	str_lit("arm-unknown-unknown-gnueabihf"),
 };
 };
+gb_global TargetMetrics target_freestanding_riscv64 = {
+	TargetOs_freestanding,
+	TargetArch_riscv64,
+	8, 8, 16, 32,
+	str_lit("riscv64-unknown-gnu"),
+};
 
 
 
 
 struct NamedTargetMetrics {
 struct NamedTargetMetrics {
@@ -733,6 +746,7 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("linux_amd64"),         &target_linux_amd64    },
 	{ str_lit("linux_amd64"),         &target_linux_amd64    },
 	{ str_lit("linux_arm64"),         &target_linux_arm64    },
 	{ str_lit("linux_arm64"),         &target_linux_arm64    },
 	{ str_lit("linux_arm32"),         &target_linux_arm32    },
 	{ str_lit("linux_arm32"),         &target_linux_arm32    },
+	{ str_lit("linux_riscv64"),       &target_linux_riscv64  },
 
 
 	{ str_lit("windows_i386"),        &target_windows_i386   },
 	{ str_lit("windows_i386"),        &target_windows_i386   },
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
@@ -761,6 +775,8 @@ gb_global NamedTargetMetrics named_targets[] = {
 
 
 	{ str_lit("freestanding_arm64"), &target_freestanding_arm64 },
 	{ str_lit("freestanding_arm64"), &target_freestanding_arm64 },
 	{ str_lit("freestanding_arm32"), &target_freestanding_arm32 },
 	{ str_lit("freestanding_arm32"), &target_freestanding_arm32 },
+
+	{ str_lit("freestanding_riscv64"), &target_freestanding_riscv64 },
 };
 };
 
 
 gb_global NamedTargetMetrics *selected_target_metrics;
 gb_global NamedTargetMetrics *selected_target_metrics;
@@ -1631,6 +1647,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
 
 
 		// Disallow on wasm
 		// Disallow on wasm
 		bc->use_separate_modules = false;
 		bc->use_separate_modules = false;
+	} if(bc->metrics.arch == TargetArch_riscv64) {
+		bc->link_flags = str_lit("-target riscv64 ");
 	} else {
 	} else {
 		// NOTE: for targets other than darwin, we don't specify a `-target` link flag.
 		// NOTE: for targets other than darwin, we don't specify a `-target` link flag.
 		// This is because we don't support cross-linking and clang is better at figuring
 		// This is because we don't support cross-linking and clang is better at figuring

File diff suppressed because it is too large
+ 6 - 5
src/build_settings_microarch.cpp


+ 5 - 0
src/check_builtin.cpp

@@ -155,6 +155,11 @@ gb_internal bool does_require_msgSend_stret(Type *return_type) {
 		return false;
 		return false;
 	}
 	}
 
 
+	// No objc here so this doesn't matter, right?
+	if (build_context.metrics.arch == TargetArch_riscv64) {
+		return false;
+	}
+
 	// if (build_context.metrics.arch == TargetArch_arm32) {
 	// if (build_context.metrics.arch == TargetArch_arm32) {
 	// 	i64 struct_limit = type_size_of(t_uintptr);
 	// 	i64 struct_limit = type_size_of(t_uintptr);
 	// 	// NOTE(bill): This is technically wrong
 	// 	// NOTE(bill): This is technically wrong

+ 1 - 0
src/checker.cpp

@@ -1039,6 +1039,7 @@ gb_internal void init_universal(void) {
 			{"arm64",     TargetArch_arm64},
 			{"arm64",     TargetArch_arm64},
 			{"wasm32",    TargetArch_wasm32},
 			{"wasm32",    TargetArch_wasm32},
 			{"wasm64p32", TargetArch_wasm64p32},
 			{"wasm64p32", TargetArch_wasm64p32},
+			{"riscv64",   TargetArch_riscv64},
 		};
 		};
 
 
 		auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
 		auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));

+ 21 - 8
src/linker.cpp

@@ -388,6 +388,12 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 		} else {
 		} else {
 			timings_start_section(timings, str_lit("ld-link"));
 			timings_start_section(timings, str_lit("ld-link"));
 
 
+			// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
+			const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
+			if (clang_path == NULL) {
+				clang_path = "clang";
+			}
+
 			// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
 			// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
 			char cwd[256];
 			char cwd[256];
 			#if !defined(GB_SYSTEM_WINDOWS)
 			#if !defined(GB_SYSTEM_WINDOWS)
@@ -458,7 +464,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 						}
 						}
 					#endif // GB_ARCH_*_BIT
 					#endif // GB_ARCH_*_BIT
 
 
-						if (is_osx) {
+						if (build_context.metrics.arch == TargetArch_riscv64) {
+							result = system_exec_command_line_app("clang",
+								"%s \"%.*s\" "
+								"-c -o \"%.*s\" "
+								"-target %.*s -march=rv64gc "
+								"%.*s "
+								"",
+								clang_path,
+								LIT(asm_file),
+								LIT(obj_file),
+								LIT(build_context.metrics.target_triplet),
+								LIT(build_context.extra_assembler_flags)
+							);
+						} else if (is_osx) {
 							// `as` comes with MacOS.
 							// `as` comes with MacOS.
 							result = system_exec_command_line_app("as",
 							result = system_exec_command_line_app("as",
 								"as \"%.*s\" "
 								"as \"%.*s\" "
@@ -592,7 +611,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 					link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
 					link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
 				}
 				}
 
 
-			} else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
+			} else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku && build_context.metrics.arch != TargetArch_riscv64) {
 				// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
 				// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
 				link_settings = gb_string_appendc(link_settings, "-no-pie ");
 				link_settings = gb_string_appendc(link_settings, "-no-pie ");
 			}
 			}
@@ -635,12 +654,6 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 				}
 				}
 			}
 			}
 
 
-			// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
-			const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
-			if (clang_path == NULL) {
-				clang_path = "clang";
-			}
-
 			gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
 			gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
 			defer (gb_string_free(link_command_line));
 			defer (gb_string_free(link_command_line));
 
 

+ 260 - 9
src/llvm_abi.cpp

@@ -332,7 +332,8 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
 }
 }
 
 
 
 
-#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
+#define LB_ABI_INFO(name) lbFunctionType *name(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
+#define LB_ABI_INFO_CTX(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
 typedef LB_ABI_INFO(lbAbiInfoType);
 typedef LB_ABI_INFO(lbAbiInfoType);
 
 
 #define LB_ABI_COMPUTE_RETURN_TYPE(name) lbArgType name(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple)
 #define LB_ABI_COMPUTE_RETURN_TYPE(name) lbArgType name(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple)
@@ -379,7 +380,7 @@ namespace lbAbi386 {
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
 		ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -460,7 +461,7 @@ namespace lbAbiAmd64Win64 {
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
 		ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -570,7 +571,7 @@ namespace lbAbiAmd64SysV {
 	gb_internal Array<RegClass> classify(LLVMTypeRef t);
 	gb_internal Array<RegClass> classify(LLVMTypeRef t);
 	gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
 	gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->calling_convention = calling_convention;
 		ft->calling_convention = calling_convention;
@@ -1008,7 +1009,7 @@ namespace lbAbiArm64 {
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 	gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
 	gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
 	gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count);
 		ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -1242,7 +1243,7 @@ namespace lbAbiWasm {
 
 
 	enum {MAX_DIRECT_STRUCT_SIZE = 32};
 	enum {MAX_DIRECT_STRUCT_SIZE = 32};
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->calling_convention = calling_convention;
 		ft->calling_convention = calling_convention;
@@ -1407,7 +1408,7 @@ namespace lbAbiArm32 {
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
 	gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
 	gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
 	gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
 
 
-	gb_internal LB_ABI_INFO(abi_info) {
+	gb_internal LB_ABI_INFO_CTX(abi_info) {
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
 		ft->ctx = c;
 		ft->ctx = c;
 		ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
 		ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
@@ -1485,8 +1486,256 @@ namespace lbAbiArm32 {
 	}
 	}
 };
 };
 
 
+namespace lbAbiRiscv64 {
+
+	gb_internal bool is_register(LLVMTypeRef type) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMIntegerTypeKind:
+		case LLVMHalfTypeKind:
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+		case LLVMPointerTypeKind:
+			return true;
+		}
+		return false;
+	}
+
+	gb_internal bool is_float(LLVMTypeRef type) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMHalfTypeKind:
+		case LLVMFloatTypeKind:
+		case LLVMDoubleTypeKind:
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+		LLVMAttributeRef attr = nullptr;
+		LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+		if (type == i1) {
+			attr = lb_create_enum_attribute(c, "zeroext");
+		}
+		return lb_arg_type_direct(type, nullptr, nullptr, attr);
+	}
+
+	gb_internal void flatten(lbModule *m, Array<LLVMTypeRef> *fields, LLVMTypeRef type, bool with_padding) {
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		switch (kind) {
+		case LLVMStructTypeKind: {
+			if (LLVMIsPackedStruct(type)) {
+				array_add(fields, type);
+				break;
+			}
+
+			if (!with_padding) {
+				auto field_remapping = map_get(&m->struct_field_remapping, cast(void *)type);
+				if (field_remapping) {
+					auto remap = *field_remapping;
+					for_array(i, remap) {
+						flatten(m, fields, LLVMStructGetTypeAtIndex(type, remap[i]), with_padding);
+					}
+					break;
+				} else {
+					debugf("no field mapping for type: %s\n", LLVMPrintTypeToString(type));
+				}
+			}
+
+			unsigned elem_count = LLVMCountStructElementTypes(type);
+			for (unsigned i = 0; i < elem_count; i += 1) {
+				flatten(m, fields, LLVMStructGetTypeAtIndex(type, i), with_padding);
+			}
+			break;
+		}
+		case LLVMArrayTypeKind: {
+			unsigned len     = LLVMGetArrayLength(type);
+			LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
+			for (unsigned i = 0; i < len; i += 1) {
+				flatten(m, fields, elem, with_padding);
+			}
+			break;
+		}
+		default:
+			array_add(fields, type);
+		}
+	}
+
+	gb_internal lbArgType compute_arg_type(lbModule *m, LLVMTypeRef type, int *gprs_left, int *fprs_left, Type *odin_type) {
+		LLVMContextRef c = m->ctx;
+
+		int xlen = 8; // 8 byte int register size for riscv64.
+
+		// NOTE: we are requiring both of these to be enabled so we can just hard-code 8.
+		// int flen = 0;
+		// if (check_target_feature_is_enabled(str_lit("d"), nullptr)) {
+		// 	flen = 8; // Double precision floats are enabled.
+		// } else if (check_target_feature_is_enabled(str_lit("f"), nullptr)) {
+		// 	flen = 4; // Single precision floats are enabled.
+		// }
+		int flen = 8;
+
+		LLVMTypeKind kind = LLVMGetTypeKind(type);
+		i64 size = lb_sizeof(type);
+
+		if (size == 0) {
+			return lb_arg_type_direct(type, LLVMStructTypeInContext(c, nullptr, 0, false), nullptr, nullptr);
+		}
+
+		LLVMTypeRef orig_type = type;
+
+		// Flatten down the type so it is easier to check all the ABI conditions.
+		// Note that we also need to remove all implicit padding fields Odin adds so we keep ABI
+		// compatibility for struct declarations.
+		if (kind == LLVMStructTypeKind && size <= gb_max(2*xlen, 2*flen)) {
+			Array<LLVMTypeRef> fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, LLVMCountStructElementTypes(type));
+			flatten(m, &fields, type, false);
+
+			if (fields.count == 1) {
+				type = fields[0];
+			} else {
+				type = LLVMStructTypeInContext(c, fields.data, cast(unsigned)fields.count, false);
+			}
+
+			kind = LLVMGetTypeKind(type);
+			size = lb_sizeof(type);
+			GB_ASSERT_MSG(size == lb_sizeof(orig_type), "flattened: %s of size %d, original: %s of size %d", LLVMPrintTypeToString(type), size, LLVMPrintTypeToString(orig_type), lb_sizeof(orig_type));
+		}
+
+		if (is_float(type) && size <= flen && *fprs_left >= 1) {
+			*fprs_left -= 1;
+			return non_struct(c, orig_type);
+		}
+
+		if (kind == LLVMStructTypeKind && size <= 2*flen) {
+			unsigned elem_count = LLVMCountStructElementTypes(type);
+			if (elem_count == 2) {
+				LLVMTypeRef ty1 = LLVMStructGetTypeAtIndex(type, 0);
+				i64 ty1s = lb_sizeof(ty1);
+				LLVMTypeRef ty2 = LLVMStructGetTypeAtIndex(type, 1);
+				i64 ty2s = lb_sizeof(ty2);
+
+				if (is_float(ty1) && is_float(ty2) && ty1s <= flen && ty2s <= flen && *fprs_left >= 2) {
+					*fprs_left -= 2;
+					return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+				}
+
+				if (is_float(ty1) && is_register(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) {
+					*fprs_left -= 1;
+					*gprs_left -= 1;
+					return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+				}
+
+				if (is_register(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) {
+					*fprs_left -= 1;
+					*gprs_left -= 1;
+					return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+				}
+			}
+		}
+
+		// At this point all the cases for floating point registers are exhausted, fit it into
+		// integer registers or the stack.
+		// LLVM automatically handles putting args on the stack so we don't check the amount of registers that are left here.
+
+		if (size <= xlen) {
+			*gprs_left -= 1;
+			if (is_register(type)) {
+				return non_struct(c, orig_type);
+			} else {
+				return lb_arg_type_direct(orig_type, LLVMIntTypeInContext(c, cast(unsigned)(size*8)), nullptr, nullptr);
+			}
+		} else if (size <= 2*xlen) {
+			LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, 2);
+			fields[0] = LLVMIntTypeInContext(c, cast(unsigned)(xlen*8));
+			fields[1] = LLVMIntTypeInContext(c, cast(unsigned)((size-xlen)*8));
+
+			*gprs_left -= 2;
+			return lb_arg_type_direct(orig_type, LLVMStructTypeInContext(c, fields, 2, false), nullptr, nullptr);
+		} else {
+			return lb_arg_type_indirect(orig_type, nullptr);
+		}
+	}
+
+	gb_internal Array<lbArgType> compute_arg_types(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *odin_type, int *gprs, int *fprs) {
+		auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef type = arg_types[i];
+			args[i] = compute_arg_type(m, type, gprs, fprs, odin_type);
+		}
+
+		return args;
+	}
+
+	gb_internal lbArgType compute_return_type(lbFunctionType *ft, lbModule *m, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type *odin_type, int *agprs) {
+		LLVMContextRef c = m->ctx;
+
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		}
+
+		// There are two registers for return types.
+		int gprs = 2;
+		int fprs = 2;
+		lbArgType ret = compute_arg_type(m, return_type, &gprs, &fprs, odin_type);
+
+		// Return didn't fit into the return registers, so caller allocates and it is returned via
+		// an out-pointer.
+		if (ret.kind == lbArg_Indirect) {
+
+			// Transform multiple return into out pointers if possible.
+			if (return_is_tuple) {
+				if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+					int field_count = cast(int)LLVMCountStructElementTypes(return_type);
+					if (field_count > 1 && field_count <= *agprs) {
+						ft->original_arg_count = ft->args.count;
+						ft->multiple_return_original_type = return_type;
+
+						for (int i = 0; i < field_count-1; i++) {
+							LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i);
+							LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0);
+							lbArgType ret_partial = lb_arg_type_direct(field_pointer_type);
+							array_add(&ft->args, ret_partial);
+							*agprs -= 1;
+						}
+						GB_ASSERT(*agprs >= 0);
+
+						// override the return type for the last field
+						LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
+						return compute_return_type(ft, m, new_return_type, true, false, odin_type, agprs);
+					}
+				}
+			}
+
+			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", ret.type);
+			return lb_arg_type_indirect(ret.type, attr);
+		}
+
+		return ret;
+	}
+
+	gb_internal LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+		ft->ctx = m->ctx;
+		ft->calling_convention = calling_convention;
+
+		int gprs = 8;
+		int fprs = 8;
+
+		ft->args = compute_arg_types(m, arg_types, arg_count, calling_convention, original_type, &gprs, &fprs);
+		ft->ret = compute_return_type(ft, m, return_type, return_is_defined, return_is_tuple, original_type, &gprs);
+
+		return ft;
+	}
+}
+
 
 
 gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
 gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
+	LLVMContextRef c = m->ctx;
+
 	switch (calling_convention) {
 	switch (calling_convention) {
 	case ProcCC_None:
 	case ProcCC_None:
 	case ProcCC_InlineAsm:
 	case ProcCC_InlineAsm:
@@ -1534,6 +1783,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
 		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
 		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
 	case TargetArch_wasm64p32:
 	case TargetArch_wasm64p32:
 		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
 		return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+	case TargetArch_riscv64:
+		return lbAbiRiscv64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
 	}
 	}
 
 
 	GB_PANIC("Unsupported ABI");
 	GB_PANIC("Unsupported ABI");
@@ -1543,7 +1794,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
 
 
 gb_internal LB_ABI_INFO(lb_get_abi_info) {
 gb_internal LB_ABI_INFO(lb_get_abi_info) {
 	lbFunctionType *ft = lb_get_abi_info_internal(
 	lbFunctionType *ft = lb_get_abi_info_internal(
-		c,
+		m,
 		arg_types, arg_count,
 		arg_types, arg_count,
 		return_type, return_is_defined,
 		return_type, return_is_defined,
 		ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
 		ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
@@ -1555,7 +1806,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
 	// This is to make it consistent when and how it is handled
 	// This is to make it consistent when and how it is handled
 	if (calling_convention == ProcCC_Odin) {
 	if (calling_convention == ProcCC_Odin) {
 		// append the `context` pointer
 		// append the `context` pointer
-		lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
+		lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0));
 		array_add(&ft->args, context_param);
 		array_add(&ft->args, context_param);
 	}
 	}
 
 

+ 30 - 1
src/llvm_backend.cpp

@@ -40,7 +40,10 @@ String get_default_microarchitecture() {
 				default_march = str_lit("x86-64-v2");
 				default_march = str_lit("x86-64-v2");
 			}
 			}
 		}
 		}
+	} else if (build_context.metrics.arch == TargetArch_riscv64) {
+		default_march = str_lit("generic-rv64");
 	}
 	}
+
 	return default_march;
 	return default_march;
 }
 }
 
 
@@ -65,13 +68,33 @@ gb_internal String get_default_features() {
 	}
 	}
 
 
 	String microarch = get_final_microarchitecture();
 	String microarch = get_final_microarchitecture();
+
+	// NOTE(laytan): for riscv64 to work properly with Odin, we need to enforce some features.
+	// and we also overwrite the generic target to include more features so we don't default to
+	// a potato feature set.
+	if (bc->metrics.arch == TargetArch_riscv64) {
+		if (microarch == str_lit("generic-rv64")) {
+			// This is what clang does by default (on -march=rv64gc for General Computing), seems good to also default to.
+			String features = str_lit("64bit,a,c,d,f,m,relax,zicsr,zifencei");
+
+			// Update the features string so LLVM uses it later.
+			if (bc->target_features_string.len > 0) {
+				bc->target_features_string = concatenate3_strings(permanent_allocator(), features, str_lit(","), bc->target_features_string);
+			} else {
+				bc->target_features_string = features;
+			}
+
+			return features;
+		}
+	}
+
 	for (int i = off; i < off+target_microarch_counts[bc->metrics.arch]; i += 1) {
 	for (int i = off; i < off+target_microarch_counts[bc->metrics.arch]; i += 1) {
 		if (microarch_features_list[i].microarch == microarch) {
 		if (microarch_features_list[i].microarch == microarch) {
 			return microarch_features_list[i].features;
 			return microarch_features_list[i].features;
 		}
 		}
 	}
 	}
 
 
-	GB_PANIC("unknown microarch");
+	GB_PANIC("unknown microarch: %.*s", LIT(microarch));
 	return {};
 	return {};
 }
 }
 
 
@@ -3030,6 +3053,12 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
 			// Always use PIC for OpenBSD and Haiku: they default to PIE
 			// Always use PIC for OpenBSD and Haiku: they default to PIE
 			reloc_mode = LLVMRelocPIC;
 			reloc_mode = LLVMRelocPIC;
 		}
 		}
+
+		if (build_context.metrics.arch == TargetArch_riscv64) {
+			// NOTE(laytan): didn't seem to work without this.
+			reloc_mode = LLVMRelocPIC;
+		}
+
 		break;
 		break;
 	case RelocMode_Static:
 	case RelocMode_Static:
 		reloc_mode = LLVMRelocStatic;
 		reloc_mode = LLVMRelocStatic;

+ 7 - 1
src/llvm_backend_general.cpp

@@ -1787,7 +1787,7 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
 		}
 		}
 	}
 	}
 	GB_ASSERT(param_index == param_count);
 	GB_ASSERT(param_index == param_count);
-	lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, return_is_tuple, type->Proc.calling_convention, type);
+	lbFunctionType *ft = lb_get_abi_info(m, params, param_count, ret, ret != nullptr, return_is_tuple, type->Proc.calling_convention, type);
 	{
 	{
 		for_array(j, ft->args) {
 		for_array(j, ft->args) {
 			auto arg = ft->args[j];
 			auto arg = ft->args[j];
@@ -2114,6 +2114,12 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 					llvm_type = LLVMStructCreateNamed(ctx, name);
 					llvm_type = LLVMStructCreateNamed(ctx, name);
 					map_set(&m->types, type, llvm_type);
 					map_set(&m->types, type, llvm_type);
 					lb_clone_struct_type(llvm_type, lb_type(m, base));
 					lb_clone_struct_type(llvm_type, lb_type(m, base));
+
+					if (base->kind == Type_Struct) {
+						map_set(&m->struct_field_remapping, cast(void *)llvm_type, lb_get_struct_remapping(m, base));
+						map_set(&m->struct_field_remapping, cast(void *)type, lb_get_struct_remapping(m, base));
+					}
+
 					return llvm_type;
 					return llvm_type;
 				}
 				}
 			}
 			}

+ 25 - 0
src/llvm_backend_proc.cpp

@@ -2877,6 +2877,31 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 			LLVMValueRef inline_asm = nullptr;
 			LLVMValueRef inline_asm = nullptr;
 
 
 			switch (build_context.metrics.arch) {
 			switch (build_context.metrics.arch) {
+			case TargetArch_riscv64:
+				{
+					GB_ASSERT(arg_count <= 7);
+
+					char asm_string[] = "ecall";
+					gbString constraints = gb_string_make(heap_allocator(), "={a0}");
+					for (unsigned i = 0; i < arg_count; i++) {
+						constraints = gb_string_appendc(constraints, ",{");
+						static char const *regs[] = {
+							"a7",
+							"a0",
+							"a1",
+							"a2",
+							"a3",
+							"a4",
+							"a5",
+							"a6"
+						};
+						constraints = gb_string_appendc(constraints, regs[i]);
+						constraints = gb_string_appendc(constraints, "}");
+					}
+
+					inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+				}
+				break;
 			case TargetArch_amd64:
 			case TargetArch_amd64:
 				{
 				{
 					GB_ASSERT(arg_count <= 7);
 					GB_ASSERT(arg_count <= 7);

+ 9 - 0
src/main.cpp

@@ -3245,6 +3245,15 @@ int main(int arg_count, char const **arg_ptr) {
 		}
 		}
 	}
 	}
 
 
+	// NOTE(laytan): on riscv64 we want to enforce some features.
+	if (build_context.metrics.arch == TargetArch_riscv64) {
+		String disabled;
+		if (!check_target_feature_is_enabled(str_lit("64bit,f,d,m"), &disabled)) { // 64bit, floats, doubles, integer multiplication.
+			gb_printf_err("missing required target feature: \"%.*s\", enable it by setting a different -microarch or explicitly adding it through -target-features\n", LIT(disabled));
+			gb_exit(1);
+		}
+	}
+
 	if (build_context.show_debug_messages) {
 	if (build_context.show_debug_messages) {
 		debugf("Selected microarch: %.*s\n", LIT(march));
 		debugf("Selected microarch: %.*s\n", LIT(march));
 		debugf("Default microarch features: %.*s\n", LIT(default_features));
 		debugf("Default microarch features: %.*s\n", LIT(default_features));

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