Browse Source

Merge pull request #1255 from odin-lang/wasi-wasm

`wasi_wasm32` support
gingerBill 3 years ago
parent
commit
b1de429d2c

+ 0 - 33
core/fmt/fmt.odin

@@ -2,7 +2,6 @@ package fmt
 
 import "core:math/bits"
 import "core:mem"
-import "core:os"
 import "core:io"
 import "core:reflect"
 import "core:runtime"
@@ -62,38 +61,6 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
 }
 
 
-fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
-	w := io.to_writer(os.stream_from_handle(fd))
-	return wprint(w=w, args=args, sep=sep)
-}
-
-fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
-	w := io.to_writer(os.stream_from_handle(fd))
-	return wprintln(w=w, args=args, sep=sep)
-}
-fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
-	w := io.to_writer(os.stream_from_handle(fd))
-	return wprintf(w, fmt, ..args)
-}
-fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
-	w := io.to_writer(os.stream_from_handle(fd))
-	return wprint_type(w, info)
-}
-fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
-	w := io.to_writer(os.stream_from_handle(fd))
-	return wprint_typeid(w, id)
-}
-
-// print* procedures return the number of bytes written
-print   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
-println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
-printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
-
-eprint   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
-eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
-eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
-
-
 // aprint* procedures return a string that was allocated with the current context
 // They must be freed accordingly
 aprint :: proc(args: ..any, sep := " ") -> string {

+ 37 - 0
core/fmt/fmt_os.odin

@@ -0,0 +1,37 @@
+//+build !freestanding
+package fmt
+
+import "core:runtime"
+import "core:os"
+import "core:io"
+
+fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
+	w := io.to_writer(os.stream_from_handle(fd))
+	return wprint(w=w, args=args, sep=sep)
+}
+
+fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
+	w := io.to_writer(os.stream_from_handle(fd))
+	return wprintln(w=w, args=args, sep=sep)
+}
+fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
+	w := io.to_writer(os.stream_from_handle(fd))
+	return wprintf(w, fmt, ..args)
+}
+fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
+	w := io.to_writer(os.stream_from_handle(fd))
+	return wprint_type(w, info)
+}
+fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
+	w := io.to_writer(os.stream_from_handle(fd))
+	return wprint_typeid(w, id)
+}
+
+// print* procedures return the number of bytes written
+print   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
+println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
+printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
+
+eprint   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
+eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
+eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }

+ 0 - 70
core/os/os_js_wasm32.odin

@@ -1,70 +0,0 @@
-package os
-
-Handle :: distinct i32;
-Errno :: distinct i32;
-
-ERROR_NONE :: Errno(0);
-
-O_RDONLY   :: 0x00000;
-O_WRONLY   :: 0x00001;
-O_RDWR     :: 0x00002;
-O_CREATE   :: 0x00040;
-O_EXCL     :: 0x00080;
-O_NOCTTY   :: 0x00100;
-O_TRUNC    :: 0x00200;
-O_NONBLOCK :: 0x00800;
-O_APPEND   :: 0x00400;
-O_SYNC     :: 0x01000;
-O_ASYNC    :: 0x02000;
-O_CLOEXEC  :: 0x80000;
-
-stdout: Handle;
-stderr: Handle;
-stdin: Handle;
-
-
-write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
-	return 0, 0;
-}
-read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
-	return 0, 0;
-}
-open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
-	return 0, 0;
-}
-close :: proc(fd: Handle) -> Errno {
-	return 0;
-}
-seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
-	return 0, 0;
-}
-current_thread_id :: proc "contextless" () -> int {
-	return 0;
-}
-
-
-file_size :: proc(fd: Handle) -> (i64, Errno) {
-	return 0, 0;
-}
-
-
-
-heap_alloc :: proc(size: int) -> rawptr {
-	return nil;
-}
-heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
-	if new_size == 0 {
-		heap_free(ptr);
-		return nil;
-	}
-	if ptr == nil {
-		return heap_alloc(new_size);
-	}
-
-	return nil;
-}
-heap_free :: proc(ptr: rawptr) {
-	if ptr == nil {
-		return;
-	}
-}

+ 97 - 0
core/os/os_wasi.odin

@@ -0,0 +1,97 @@
+package os
+
+import "core:sys/wasm/wasi"
+
+Handle :: distinct i32
+Errno :: distinct i32
+
+ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
+
+O_RDONLY   :: 0x00000
+O_WRONLY   :: 0x00001
+O_RDWR     :: 0x00002
+O_CREATE   :: 0x00040
+O_EXCL     :: 0x00080
+O_NOCTTY   :: 0x00100
+O_TRUNC    :: 0x00200
+O_NONBLOCK :: 0x00800
+O_APPEND   :: 0x00400
+O_SYNC     :: 0x01000
+O_ASYNC    :: 0x02000
+O_CLOEXEC  :: 0x80000
+
+stdin:  Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	iovs := wasi.ciovec_t(data)
+	n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
+	return int(n), Errno(err)
+}
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	iovs := wasi.iovec_t(data)
+	n, err := wasi.fd_read(wasi.fd_t(fd), {iovs})
+	return int(n), Errno(err)
+}
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+	iovs := wasi.ciovec_t(data)
+	n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
+	return int(n), Errno(err)
+}
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+	iovs := wasi.iovec_t(data)
+	n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
+	return int(n), Errno(err)
+}
+open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
+	return 0, -1
+}
+close :: proc(fd: Handle) -> Errno {
+	err := wasi.fd_close(wasi.fd_t(fd))
+	return Errno(err)
+}
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+	n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence))
+	return i64(n), Errno(err)
+}
+current_thread_id :: proc "contextless" () -> int {
+	return 0
+}
+
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+	stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
+	if err != nil {
+		return 0, Errno(err)
+	}
+	return i64(stat.size), 0
+}
+
+
+
+heap_alloc :: proc(size: int) -> rawptr {
+	return nil
+}
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	if new_size == 0 {
+		heap_free(ptr)
+		return nil
+	}
+	if ptr == nil {
+		return heap_alloc(new_size)
+	}
+
+	return nil
+}
+heap_free :: proc(ptr: rawptr) {
+	if ptr == nil {
+		return
+	}
+}
+
+
+exit :: proc "contextless" (code: int) -> ! {
+	wasi.proc_exit(wasi.exitcode_t(code))
+}

+ 2 - 2
core/os/stream.odin

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

+ 1 - 0
core/runtime/default_allocators_general.odin

@@ -1,5 +1,6 @@
 //+build !windows
 //+build !freestanding
+//+build !wasi
 package runtime
 
 when ODIN_DEFAULT_TO_NIL_ALLOCATOR {

+ 32 - 0
core/runtime/default_allocators_wasi.odin

@@ -0,0 +1,32 @@
+//+build wasi
+package runtime
+
+default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+                               size, alignment: int,
+                               old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	switch mode {
+	case .Alloc:
+		return nil, .Out_Of_Memory
+	case .Free:
+		return nil, .None
+	case .Free_All:
+		return nil, .Mode_Not_Implemented
+	case .Resize:
+		if size == 0 {
+			return nil, .None
+		}
+		return nil, .Out_Of_Memory
+	case .Query_Features:
+		return nil, .Mode_Not_Implemented
+	case .Query_Info:
+		return nil, .Mode_Not_Implemented
+	}
+	return nil, .None
+}
+
+default_allocator :: proc() -> Allocator {
+	return Allocator{
+		procedure = default_allocator_proc,
+		data = nil,
+	}
+}

+ 164 - 153
core/runtime/default_temporary_allocator.odin

@@ -1,191 +1,202 @@
 package runtime
 
-@(private)
-byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
-	return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}
-}
-
-
 DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
 
 
-Default_Temp_Allocator :: struct {
-	data:               []byte,
-	curr_offset:        int,
-	prev_allocation:    rawptr,
-	backup_allocator:   Allocator,
-	leaked_allocations: [dynamic][]byte,
-}
-
-default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
-	s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
-	s.curr_offset = 0
-	s.prev_allocation = nil
-	s.backup_allocator = backup_allocator
-	s.leaked_allocations.allocator = backup_allocator
-}
-
-default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
-	if s == nil {
-		return
+when ODIN_OS == "freestanding" {
+	Default_Temp_Allocator :: struct {
 	}
-	for ptr in s.leaked_allocations {
-		free(raw_data(ptr), s.backup_allocator)
+	
+	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
 	}
-	delete(s.leaked_allocations)
-	delete(s.data, s.backup_allocator)
-	s^ = {}
-}
-
-@(private)
-default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
-	size := size
-	size = align_forward_int(size, alignment)
-
-	switch {
-	case s.curr_offset+size <= len(s.data):
-		start := uintptr(raw_data(s.data))
-		ptr := start + uintptr(s.curr_offset)
-		ptr = align_forward_uintptr(ptr, uintptr(alignment))
-		mem_zero(rawptr(ptr), size)
-
-		s.prev_allocation = rawptr(ptr)
-		offset := int(ptr - start)
-		s.curr_offset = offset + size
-		return byte_slice(rawptr(ptr), size), .None
-
-	case size <= len(s.data):
-		start := uintptr(raw_data(s.data))
-		ptr := align_forward_uintptr(start, uintptr(alignment))
-		mem_zero(rawptr(ptr), size)
-
-		s.prev_allocation = rawptr(ptr)
-		offset := int(ptr - start)
-		s.curr_offset = offset + size
-		return byte_slice(rawptr(ptr), size), .None
+	
+	default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
 	}
-	a := s.backup_allocator
-	if a.procedure == nil {
-		a = context.allocator
-		s.backup_allocator = a
+	
+	default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+	                                    size, alignment: int,
+	                                    old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+		return nil, nil		
 	}
-
-	data, err := mem_alloc_bytes(size, alignment, a, loc)
-	if err != nil {
-		return data, err
+} else {
+	Default_Temp_Allocator :: struct {
+		data:               []byte,
+		curr_offset:        int,
+		prev_allocation:    rawptr,
+		backup_allocator:   Allocator,
+		leaked_allocations: [dynamic][]byte,
 	}
-	if s.leaked_allocations == nil {
-		s.leaked_allocations = make([dynamic][]byte, a)
+	
+	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
+		s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
+		s.curr_offset = 0
+		s.prev_allocation = nil
+		s.backup_allocator = backup_allocator
+		s.leaked_allocations.allocator = backup_allocator
 	}
-	append(&s.leaked_allocations, data)
 
-	// TODO(bill): Should leaks be notified about?
-	if logger := context.logger; logger.lowest_level <= .Warning {
-		if logger.procedure != nil {
-			logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
+	default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
+		if s == nil {
+			return
+		}
+		for ptr in s.leaked_allocations {
+			free(raw_data(ptr), s.backup_allocator)
+		}
+		delete(s.leaked_allocations)
+		delete(s.data, s.backup_allocator)
+		s^ = {}
+	}
+
+	@(private)
+	default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+		size := size
+		size = align_forward_int(size, alignment)
+
+		switch {
+		case s.curr_offset+size <= len(s.data):
+			start := uintptr(raw_data(s.data))
+			ptr := start + uintptr(s.curr_offset)
+			ptr = align_forward_uintptr(ptr, uintptr(alignment))
+			mem_zero(rawptr(ptr), size)
+
+			s.prev_allocation = rawptr(ptr)
+			offset := int(ptr - start)
+			s.curr_offset = offset + size
+			return byte_slice(rawptr(ptr), size), .None
+
+		case size <= len(s.data):
+			start := uintptr(raw_data(s.data))
+			ptr := align_forward_uintptr(start, uintptr(alignment))
+			mem_zero(rawptr(ptr), size)
+
+			s.prev_allocation = rawptr(ptr)
+			offset := int(ptr - start)
+			s.curr_offset = offset + size
+			return byte_slice(rawptr(ptr), size), .None
+		}
+		a := s.backup_allocator
+		if a.procedure == nil {
+			a = context.allocator
+			s.backup_allocator = a
 		}
-	}
 
-	return data, .None
-}
+		data, err := mem_alloc_bytes(size, alignment, a, loc)
+		if err != nil {
+			return data, err
+		}
+		if s.leaked_allocations == nil {
+			s.leaked_allocations = make([dynamic][]byte, a)
+		}
+		append(&s.leaked_allocations, data)
 
-@(private)
-default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
-	if old_memory == nil {
-		return .None
+		// TODO(bill): Should leaks be notified about?
+		if logger := context.logger; logger.lowest_level <= .Warning {
+			if logger.procedure != nil {
+				logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
+			}
+		}
+
+		return data, .None
 	}
 
-	start := uintptr(raw_data(s.data))
-	end := start + uintptr(len(s.data))
-	old_ptr := uintptr(old_memory)
+	@(private)
+	default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
+		if old_memory == nil {
+			return .None
+		}
 
-	if s.prev_allocation == old_memory {
-		s.curr_offset = int(uintptr(s.prev_allocation) - start)
-		s.prev_allocation = nil
-		return .None
-	}
+		start := uintptr(raw_data(s.data))
+		end := start + uintptr(len(s.data))
+		old_ptr := uintptr(old_memory)
 
-	if start <= old_ptr && old_ptr < end {
-		// NOTE(bill): Cannot free this pointer but it is valid
-		return .None
-	}
+		if s.prev_allocation == old_memory {
+			s.curr_offset = int(uintptr(s.prev_allocation) - start)
+			s.prev_allocation = nil
+			return .None
+		}
 
-	if len(s.leaked_allocations) != 0 {
-		for data, i in s.leaked_allocations {
-			ptr := raw_data(data)
-			if ptr == old_memory {
-				free(ptr, s.backup_allocator)
-				ordered_remove(&s.leaked_allocations, i)
-				return .None
-			}
+		if start <= old_ptr && old_ptr < end {
+			// NOTE(bill): Cannot free this pointer but it is valid
+			return .None
 		}
-	}
-	return .Invalid_Pointer
-	// panic("invalid pointer passed to default_temp_allocator");
-}
 
-@(private)
-default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
-	s.curr_offset = 0
-	s.prev_allocation = nil
-	for data in s.leaked_allocations {
-		free(raw_data(data), s.backup_allocator)
+		if len(s.leaked_allocations) != 0 {
+			for data, i in s.leaked_allocations {
+				ptr := raw_data(data)
+				if ptr == old_memory {
+					free(ptr, s.backup_allocator)
+					ordered_remove(&s.leaked_allocations, i)
+					return .None
+				}
+			}
+		}
+		return .Invalid_Pointer
+		// panic("invalid pointer passed to default_temp_allocator");
 	}
-	clear(&s.leaked_allocations)
-}
 
-@(private)
-default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
-	begin := uintptr(raw_data(s.data))
-	end := begin + uintptr(len(s.data))
-	old_ptr := uintptr(old_memory)
-	if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
-		if old_ptr+uintptr(size) < end {
-			s.curr_offset = int(old_ptr-begin)+size
-			return byte_slice(old_memory, size), .None
+	@(private)
+	default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
+		s.curr_offset = 0
+		s.prev_allocation = nil
+		for data in s.leaked_allocations {
+			free(raw_data(data), s.backup_allocator)
 		}
+		clear(&s.leaked_allocations)
+	}
+
+	@(private)
+	default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+		begin := uintptr(raw_data(s.data))
+		end := begin + uintptr(len(s.data))
+		old_ptr := uintptr(old_memory)
+		if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
+			if old_ptr+uintptr(size) < end {
+				s.curr_offset = int(old_ptr-begin)+size
+				return byte_slice(old_memory, size), .None
+			}
+		}
+		data, err := default_temp_allocator_alloc(s, size, alignment, loc)
+		if err == .None {
+			copy(data, byte_slice(old_memory, old_size))
+			err = default_temp_allocator_free(s, old_memory, loc)
+		}
+		return data, err
 	}
-	data, err := default_temp_allocator_alloc(s, size, alignment, loc)
-	if err == .None {
-		copy(data, byte_slice(old_memory, old_size))
-		err = default_temp_allocator_free(s, old_memory, loc)
-	}
-	return data, err
-}
 
-default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
-                                    size, alignment: int,
-                                    old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+	default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+	                                    size, alignment: int,
+	                                    old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
 
-	s := (^Default_Temp_Allocator)(allocator_data)
+		s := (^Default_Temp_Allocator)(allocator_data)
 
-	if s.data == nil {
-		default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
-	}
+		if s.data == nil {
+			default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
+		}
+
+		switch mode {
+		case .Alloc:
+			data, err = default_temp_allocator_alloc(s, size, alignment, loc)
+		case .Free:
+			err = default_temp_allocator_free(s, old_memory, loc)
 
-	switch mode {
-	case .Alloc:
-		data, err = default_temp_allocator_alloc(s, size, alignment, loc)
-	case .Free:
-		err = default_temp_allocator_free(s, old_memory, loc)
+		case .Free_All:
+			default_temp_allocator_free_all(s, loc)
 
-	case .Free_All:
-		default_temp_allocator_free_all(s, loc)
+		case .Resize:
+			data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
 
-	case .Resize:
-		data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
+		case .Query_Features:
+			set := (^Allocator_Mode_Set)(old_memory)
+			if set != nil {
+				set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+			}
 
-	case .Query_Features:
-		set := (^Allocator_Mode_Set)(old_memory)
-		if set != nil {
-			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+		case .Query_Info:
+			// Nothing to give
 		}
 
-	case .Query_Info:
-		// Nothing to give
+		return
 	}
-
-	return
 }
 
 default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
@@ -193,4 +204,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
 		procedure = default_temp_allocator_proc,
 		data = allocator,
 	}
-}
+}

+ 94 - 0
core/runtime/internal.odin

@@ -2,6 +2,11 @@ package runtime
 
 import "core:intrinsics"
 
+@(private)
+byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
+	return ([^]byte)(data)[:max(len, 0)]
+}
+
 bswap_16 :: proc "contextless" (x: u16) -> u16 {
 	return x>>8 | x<<8
 }
@@ -833,3 +838,92 @@ fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 	x := i64(a)
 	return i128(x)
 }
+
+
+
+
+@(link_name="__umodti3")
+umodti3 :: proc "c" (a, b: u128) -> u128 {
+	r: u128 = ---
+	_ = udivmod128(a, b, &r)
+	return r
+}
+
+
+@(link_name="__udivmodti4")
+udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
+	return udivmod128(a, b, rem)
+}
+
+@(link_name="__udivti3")
+udivti3 :: proc "c" (a, b: u128) -> u128 {
+	return udivmodti4(a, b, nil)
+}
+
+
+@(link_name="__modti3")
+modti3 :: proc "c" (a, b: i128) -> i128 {
+	s_a := a >> (128 - 1)
+	s_b := b >> (128 - 1)
+	an := (a ~ s_a) - s_a
+	bn := (b ~ s_b) - s_b
+
+	r: u128 = ---
+	_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
+	return (transmute(i128)r ~ s_a) - s_a
+}
+
+
+@(link_name="__divmodti4")
+divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
+	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
+	return transmute(i128)u
+}
+
+@(link_name="__divti3")
+divti3 :: proc "c" (a, b: i128) -> i128 {
+	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
+	return transmute(i128)u
+}
+
+
+@(link_name="__fixdfti")
+fixdfti :: proc(a: u64) -> i128 {
+	significandBits :: 52
+	typeWidth       :: (size_of(u64)*8)
+	exponentBits    :: (typeWidth - significandBits - 1)
+	maxExponent     :: ((1 << exponentBits) - 1)
+	exponentBias    :: (maxExponent >> 1)
+
+	implicitBit     :: (u64(1) << significandBits)
+	significandMask :: (implicitBit - 1)
+	signBit         :: (u64(1) << (significandBits + exponentBits))
+	absMask         :: (signBit - 1)
+	exponentMask    :: (absMask ~ significandMask)
+
+	// Break a into sign, exponent, significand
+	aRep := a
+	aAbs := aRep & absMask
+	sign := i128(-1 if aRep & signBit != 0 else 1)
+	exponent := u64((aAbs >> significandBits) - exponentBias)
+	significand := u64((aAbs & significandMask) | implicitBit)
+
+	// If exponent is negative, the result is zero.
+	if exponent < 0 {
+		return 0
+	}
+
+	// If the value is too large for the integer type, saturate.
+	if exponent >= size_of(i128) * 8 {
+		return max(i128) if sign == 1 else min(i128)
+	}
+
+	// If 0 <= exponent < significandBits, right shift to get the result.
+	// Otherwise, shift left.
+	if exponent < significandBits {
+		return sign * i128(significand >> (significandBits - exponent))
+	} else {
+		return sign * (i128(significand) << (exponent - significandBits))
+	}
+
+}

+ 0 - 88
core/runtime/internal_linux.odin

@@ -1,89 +1 @@
 package runtime
-
-import "core:intrinsics"
-
-@(link_name="__umodti3")
-umodti3 :: proc "c" (a, b: u128) -> u128 {
-	r: u128 = ---
-	_ = udivmod128(a, b, &r)
-	return r
-}
-
-
-@(link_name="__udivmodti4")
-udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
-	return udivmod128(a, b, rem)
-}
-
-@(link_name="__udivti3")
-udivti3 :: proc "c" (a, b: u128) -> u128 {
-	return udivmodti4(a, b, nil)
-}
-
-
-@(link_name="__modti3")
-modti3 :: proc "c" (a, b: i128) -> i128 {
-	s_a := a >> (128 - 1)
-	s_b := b >> (128 - 1)
-	an := (a ~ s_a) - s_a
-	bn := (b ~ s_b) - s_b
-
-	r: u128 = ---
-	_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
-	return (transmute(i128)r ~ s_a) - s_a
-}
-
-
-@(link_name="__divmodti4")
-divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
-	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
-	return transmute(i128)u
-}
-
-@(link_name="__divti3")
-divti3 :: proc "c" (a, b: i128) -> i128 {
-	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
-	return transmute(i128)u
-}
-
-
-@(link_name="__fixdfti")
-fixdfti :: proc(a: u64) -> i128 {
-	significandBits :: 52
-	typeWidth       :: (size_of(u64)*8)
-	exponentBits    :: (typeWidth - significandBits - 1)
-	maxExponent     :: ((1 << exponentBits) - 1)
-	exponentBias    :: (maxExponent >> 1)
-
-	implicitBit     :: (u64(1) << significandBits)
-	significandMask :: (implicitBit - 1)
-	signBit         :: (u64(1) << (significandBits + exponentBits))
-	absMask         :: (signBit - 1)
-	exponentMask    :: (absMask ~ significandMask)
-
-	// Break a into sign, exponent, significand
-	aRep := a
-	aAbs := aRep & absMask
-	sign := i128(-1 if aRep & signBit != 0 else 1)
-	exponent := u64((aAbs >> significandBits) - exponentBias)
-	significand := u64((aAbs & significandMask) | implicitBit)
-
-	// If exponent is negative, the result is zero.
-	if exponent < 0 {
-		return 0
-	}
-
-	// If the value is too large for the integer type, saturate.
-	if exponent >= size_of(i128) * 8 {
-		return max(i128) if sign == 1 else min(i128)
-	}
-
-	// If 0 <= exponent < significandBits, right shift to get the result.
-	// Otherwise, shift left.
-	if exponent < significandBits {
-		return sign * i128(significand >> (significandBits - exponent))
-	} else {
-		return sign * (i128(significand) << (exponent - significandBits))
-	}
-
-}

+ 0 - 88
core/runtime/internal_windows.odin

@@ -1,89 +1 @@
 package runtime
-
-import "core:intrinsics"
-
-@(link_name="__umodti3")
-umodti3 :: proc "c" (a, b: u128) -> u128 {
-	r: u128 = ---
-	_ = udivmod128(a, b, &r)
-	return r
-}
-
-
-@(link_name="__udivmodti4")
-udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
-	return udivmod128(a, b, rem)
-}
-
-@(link_name="__udivti3")
-udivti3 :: proc "c" (a, b: u128) -> u128 {
-	return udivmodti4(a, b, nil)
-}
-
-
-@(link_name="__modti3")
-modti3 :: proc "c" (a, b: i128) -> i128 {
-	s_a := a >> (128 - 1)
-	s_b := b >> (128 - 1)
-	an := (a ~ s_a) - s_a
-	bn := (b ~ s_b) - s_b
-
-	r: u128 = ---
-	_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
-	return (transmute(i128)r ~ s_a) - s_a
-}
-
-
-@(link_name="__divmodti4")
-divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
-	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
-	return transmute(i128)u
-}
-
-@(link_name="__divti3")
-divti3 :: proc "c" (a, b: i128) -> i128 {
-	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
-	return transmute(i128)u
-}
-
-
-@(link_name="__fixdfti")
-fixdfti :: proc(a: u64) -> i128 {
-	significandBits :: 52
-	typeWidth       :: (size_of(u64)*8)
-	exponentBits    :: (typeWidth - significandBits - 1)
-	maxExponent     :: ((1 << exponentBits) - 1)
-	exponentBias    :: (maxExponent >> 1)
-
-	implicitBit     :: (u64(1) << significandBits)
-	significandMask :: (implicitBit - 1)
-	signBit         :: (u64(1) << (significandBits + exponentBits))
-	absMask         :: (signBit - 1)
-	exponentMask    :: (absMask ~ significandMask)
-
-	// Break a into sign, exponent, significand
-	aRep := a
-	aAbs := aRep & absMask
-	sign := i128(-1 if aRep & signBit != 0 else 1)
-	exponent := (aAbs >> significandBits) - exponentBias
-	significand := (aAbs & significandMask) | implicitBit
-
-	// If exponent is negative, the result is zero.
-	if exponent < 0 {
-		return 0
-	}
-
-	// If the value is too large for the integer type, saturate.
-	if exponent >= size_of(i128) * 8 {
-		return max(i128) if sign == 1 else min(i128)
-	}
-
-	// If 0 <= exponent < significandBits, right shift to get the result.
-	// Otherwise, shift left.
-	if exponent < significandBits {
-		return sign * i128(significand >> (significandBits - exponent))
-	} else {
-		return sign * (i128(significand) << (exponent - significandBits))
-	}
-
-}

+ 1 - 2
core/runtime/os_specific_any.odin

@@ -1,5 +1,4 @@
-//+build !freestanding
-//+build !windows
+//+build !freestanding !wasi !windows
 package runtime
 
 import "core:os"

+ 10 - 0
core/runtime/os_specific_wasi.odin

@@ -0,0 +1,10 @@
+//+build wasi
+package runtime
+
+import "core:sys/wasm/wasi"
+
+_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
+	data := (wasi.ciovec_t)(data)
+	n, err := wasi.fd_write(1, {data})
+	return int(n), _OS_Errno(err)
+}

+ 34 - 7
core/runtime/procs.odin

@@ -1,12 +1,39 @@
 package runtime
 
-memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
-	if ptr != nil && len != 0 {
-		b := byte(val)
-		p := ([^]byte)(ptr)
-		for i in 0..<len {
-			p[i] = b
+when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
+	@(link_name="memset")
+	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
+		if ptr != nil && len != 0 {
+			b := byte(val)
+			p := ([^]byte)(ptr)
+			for i in 0..<len {
+				p[i] = b
+			}
 		}
+		return ptr
+	}
+	
+	@(link_name="memmove")
+	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
+		if dst != src {
+			d, s := ([^]byte)(dst), ([^]byte)(src)
+			d_end, s_end := d[len:], s[len:]
+			for i := len-1; i >= 0; i -= 1 {
+				d[i] = s[i]
+			}
+		}
+		return dst
+		
+	}
+} else {
+	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
+		if ptr != nil && len != 0 {
+			b := byte(val)
+			p := ([^]byte)(ptr)
+			for i in 0..<len {
+				p[i] = b
+			}
+		}
+		return ptr
 	}
-	return ptr
 }

+ 7 - 0
core/runtime/procs_wasm32.odin

@@ -0,0 +1,7 @@
+//+build wasm32
+package runtime
+
+@(link_name="__ashlti3")
+__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
+	return a
+}

+ 1899 - 0
core/sys/wasm/wasi/wasi_api.odin

@@ -0,0 +1,1899 @@
+//+build wasm32
+package sys_wasi
+
+foreign import wasi "wasi_snapshot_preview1"
+
+DIRCOOKIE_START :: u64(0)
+size_t :: uint
+
+filesize_t :: distinct u64
+timestamp_t :: distinct u64
+
+clockid_t :: distinct u32
+CLOCK_MONOTONIC          :: clockid_t(0)
+CLOCK_PROCESS_CPUTIME_ID :: clockid_t(1)
+CLOCK_REALTIME           :: clockid_t(2)
+CLOCK_THREAD_CPUTIME_ID  :: clockid_t(3)
+
+errno_t :: enum u16 {
+	// No error occurred. System call completed successfully.
+	SUCCESS = 0,
+	// Argument list too long.
+	TOOBIG = 1,
+	// Permission denied.
+	ACCESS = 2,
+	// Address in use.
+	ADDRINUSE = 3,
+	// Address not available.
+	ADDRNOTAVAIL = 4,
+	// Address family not supported.
+	AFNOSUPPORT = 5,
+	// Resource unavailable, or operation would block.
+	AGAIN = 6,
+	// Connection already in progress.
+	ALREADY = 7,
+	// Bad file descriptor.
+	BADF = 8,
+	// Bad message.
+	BADMSG = 9,
+	// Device or resource busy.
+	BUSY = 10,
+	// Operation canceled.
+	CANCELED = 11,
+	// No child processes.
+	CHILD = 12,
+	// Connection aborted.
+	CONNABORTED = 13,
+	// Connection refused.
+	CONNREFUSED = 14,
+	// Connection reset.
+	CONNRESET = 15,
+	// Resource deadlock would occur.
+	DEADLK = 16,
+	// Destination address required.
+	DESTADDRREQ = 17,
+	// Mathematics argument out of domain of function.
+	DOM = 18,
+	// Reserved.
+	DQUOT = 19,
+	// File exists.
+	EXIST = 20,
+	// Bad address.
+	FAULT = 21,
+	// File too large.
+	FBIG = 22,
+	// Host is unreachable.
+	HOSTUNREACH = 23,
+	// Identifier removed.
+	IDRM = 24,
+	// Illegal byte sequence.
+	ILSEQ = 25,
+	// Operation in progress.
+	INPROGRESS = 26,
+	// Interrupted function.
+	INTR = 27,
+	// Invalid argument.
+	INVAL = 28,
+	// I/O error.
+	IO = 29,
+	// Socket is connected.
+	ISCONN = 30,
+	// Is a directory.
+	ISDIR = 31,
+	// Too many levels of symbolic links.
+	LOOP = 32,
+	// File descriptor value too large.
+	MFILE = 33,
+	// Too many links.
+	MLINK = 34,
+	// Message too large.
+	MSGSIZE = 35,
+	// Reserved.
+	MULTIHOP = 36,
+	// Filename too long.
+	NAMETOOLONG = 37,
+	// Network is down.
+	NETDOWN = 38,
+	// Connection aborted by network.
+	NETRESET = 39,
+	// Network unreachable.
+	NETUNREACH = 40,
+	// Too many files open in system.
+	NFILE = 41,
+	// No buffer space available.
+	NOBUFS = 42,
+	// No such device.
+	NODEV = 43,
+	// No such file or directory.
+	NOENT = 44,
+	// Executable file format error.
+	NOEXEC = 45,
+	// No locks available.
+	NOLCK = 46,
+	// Reserved.
+	NOLINK = 47,
+	// Not enough space.
+	NOMEM = 48,
+	// No message of the desired type.
+	NOMSG = 49,
+	// Protocol not available.
+	NOPROTOOPT = 50,
+	// No space left on device.
+	NOSPC = 51,
+	// Function not supported.
+	NOSYS = 52,
+	// The socket is not connected.
+	NOTCONN = 53,
+	// Not a directory or a symbolic link to a directory.
+	NOTDIR = 54,
+	// Directory not empty.
+	NOTEMPTY = 55,
+	// State not recoverable.
+	NOTRECOVERABLE = 56,
+	// Not a socket.
+	NOTSOCK = 57,
+	// Not supported, or operation not supported on socket.
+	NOTSUP = 58,
+	// Inappropriate I/O control operation.
+	NOTTY = 59,
+	// No such device or address.
+	NXIO = 60,
+	// Value too large to be stored in data type.
+	OVERFLOW = 61,
+	// Previous owner died.
+	OWNERDEAD = 62,
+	// Operation not permitted.
+	PERM = 63,
+	// Broken pipe.
+	PIPE = 64,
+	// Protocol error.
+	PROTO = 65,
+	// Protocol not supported.
+	PROTONOSUPPORT = 66,
+	// Protocol wrong type for socket.
+	PROTOTYPE = 67,
+	// Result too large.
+	RANGE = 68,
+	// Read-only file system.
+	ROFS = 69,
+	// Invalid seek.
+	SPIPE = 70,
+	// No such process.
+	SRCH = 71,
+	// Reserved.
+	STALE = 72,
+	// Connection timed out.
+	TIMEDOUT = 73,
+	// Text file busy.
+	TXTBSY = 74,
+	// Cross-device link.
+	XDEV = 75,
+	// Extension: Capabilities insufficient.
+	NOTCAPABLE = 76,
+}
+
+
+rights_t :: distinct bit_set[rights_flag_t; u64]
+rights_flag_t :: enum u64 {
+	/**
+	 * The right to invoke `fd_datasync`.
+	 * If `path_open` is set, includes the right to invoke
+	 * `path_open` with `fdflags::dsync`.
+	 */
+	FD_DATASYNC = 0,
+
+	/**
+	 * The right to invoke `fd_read` and `sock_recv`.
+	 * If `rights::fd_seek` is set, includes the right to invoke `fd_pread`.
+	 */
+	FD_READ = 1,
+
+	/**
+	 * The right to invoke `fd_seek`. This flag implies `rights::fd_tell`.
+	 */
+	FD_SEEK = 2,
+
+	/**
+	 * The right to invoke `fd_fdstat_set_flags`.
+	 */
+	FD_FDSTAT_SET_FLAGS = 3,
+
+	/**
+	 * The right to invoke `fd_sync`.
+	 * If `path_open` is set, includes the right to invoke
+	 * `path_open` with `fdflags::rsync` and `fdflags::dsync`.
+	 */
+	FD_SYNC = 4,
+
+	/**
+	 * The right to invoke `fd_seek` in such a way that the file offset
+	 * remains unaltered (i.e., `whence::cur` with offset zero), or to
+	 * invoke `fd_tell`.
+	 */
+	FD_TELL = 5,
+
+	/**
+	 * The right to invoke `fd_write` and `sock_send`.
+	 * If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`.
+	 */
+	FD_WRITE = 6,
+
+	/**
+	 * The right to invoke `fd_advise`.
+	 */
+	FD_ADVISE = 7,
+
+	/**
+	 * The right to invoke `fd_allocate`.
+	 */
+	FD_ALLOCATE = 8,
+
+	/**
+	 * The right to invoke `path_create_directory`.
+	 */
+	PATH_CREATE_DIRECTORY = 9,
+
+	/**
+	 * If `path_open` is set, the right to invoke `path_open` with `oflags::creat`.
+	 */
+	PATH_CREATE_FILE = 10,
+
+	/**
+	 * The right to invoke `path_link` with the file descriptor as the
+	 * source directory.
+	 */
+	PATH_LINK_SOURCE = 11,
+
+	/**
+	 * The right to invoke `path_link` with the file descriptor as the
+	 * target directory.
+	 */
+	PATH_LINK_TARGET = 12,
+
+	/**
+	 * The right to invoke `path_open`.
+	 */
+	PATH_OPEN = 13,
+
+	/**
+	 * The right to invoke `fd_readdir`.
+	 */
+	FD_READDIR = 14,
+
+	/**
+	 * The right to invoke `path_readlink`.
+	 */
+	PATH_READLINK = 15,
+
+	/**
+	 * The right to invoke `path_rename` with the file descriptor as the source directory.
+	 */
+	PATH_RENAME_SOURCE = 16,
+
+	/**
+	 * The right to invoke `path_rename` with the file descriptor as the target directory.
+	 */
+	PATH_RENAME_TARGET = 17,
+
+	/**
+	 * The right to invoke `path_filestat_get`.
+	 */
+	PATH_FILESTAT_GET = 18,
+
+	/**
+	 * The right to change a file's size (there is no `path_filestat_set_size`).
+	 * If `path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`.
+	 */
+	PATH_FILESTAT_SET_SIZE = 19,
+
+	/**
+	 * The right to invoke `path_filestat_set_times`.
+	 */
+	PATH_FILESTAT_SET_TIMES = 20,
+
+	/**
+	 * The right to invoke `fd_filestat_get`.
+	 */
+	FD_FILESTAT_GET = 21,
+
+	/**
+	 * The right to invoke `fd_filestat_set_size`.
+	 */
+	FD_FILESTAT_SET_SIZE = 22,
+
+	/**
+	 * The right to invoke `fd_filestat_set_times`.
+	 */
+	FD_FILESTAT_SET_TIMES = 23,
+
+	/**
+	 * The right to invoke `path_symlink`.
+	 */
+	PATH_SYMLINK = 24,
+
+	/**
+	 * The right to invoke `path_remove_directory`.
+	 */
+	PATH_REMOVE_DIRECTORY = 25,
+
+	/**
+	 * The right to invoke `path_unlink_file`.
+	 */
+	PATH_UNLINK_FILE = 26,
+
+	/**
+	 * If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`.
+	 * If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`.
+	 */
+	POLL_FD_READWRITE = 27,
+
+	/**
+	 * The right to invoke `sock_shutdown`.
+	 */
+	SOCK_SHUTDOWN = 28,
+}
+
+fd_t :: distinct i32
+
+// iovec_t :: struct {
+// 	buf: [^]u8,
+// 	buf_len: size_t,
+// }
+// ciovec_t :: struct {
+// 	buf: [^]u8,
+// 	buf_len: size_t,
+// }
+iovec_t :: []byte
+ciovec_t :: []byte
+
+
+filedelta_t :: distinct i64
+
+whence_t :: enum u8 {
+	SET = 0,
+	CUR = 1,
+	END = 2,
+}
+
+dircookie_t :: distinct u64
+dirnamlen_t :: distinct u32
+inode_t :: distinct u64
+
+filetype_t :: enum u8 {
+	/**
+	 * The type of the file descriptor or file is unknown or is different from any of the other types specified.
+	 */
+	UNKNOWN = 0,
+
+	/**
+	 * The file descriptor or file refers to a block device inode.
+	 */
+	BLOCK_DEVICE = 1,
+
+	/**
+	 * The file descriptor or file refers to a character device inode.
+	 */
+	CHARACTER_DEVICE = 2,
+
+	/**
+	 * The file descriptor or file refers to a directory inode.
+	 */
+	DIRECTORY = 3,
+
+	/**
+	 * The file descriptor or file refers to a regular file inode.
+	 */
+	REGULAR_FILE = 4,
+
+	/**
+	 * The file descriptor or file refers to a datagram socket.
+	 */
+	SOCKET_DGRAM = 5,
+
+	/**
+	 * The file descriptor or file refers to a byte-stream socket.
+	 */
+	SOCKET_STREAM = 6,
+
+	/**
+	 * The file refers to a symbolic link inode.
+	 */
+	SYMBOLIC_LINK = 7,
+}
+
+dirent_t :: struct {
+	d_next:   dircookie_t,
+	d_ino:    inode_t,
+	d_namlen: dirnamlen_t,
+	d_type:   filetype_t,
+}
+
+advice_t :: enum u8 {
+	/**
+	 * The application has no advice to give on its behavior with respect to the specified data.
+	 */
+	NORMAL = 0,
+
+	/**
+	 * The application expects to access the specified data sequentially from lower offsets to higher offsets.
+	 */
+	SEQUENTIAL = 1,
+
+	/**
+	 * The application expects to access the specified data in a random order.
+	 */
+	RANDOM = 2,
+
+	/**
+	 * The application expects to access the specified data in the near future.
+	 */
+	WILLNEED = 3,
+
+	/**
+	 * The application expects that it will not access the specified data in the near future.
+	 */
+	DONTNEED = 4,
+
+	/**
+	 * The application expects to access the specified data once and then not reuse it thereafter.
+	 */
+	NOREUSE = 5,
+}
+
+fdflags_t :: distinct bit_set[fdflag_t; u16]
+fdflag_t :: enum u16 {
+	/**
+	 * Append mode: Data written to the file is always appended to the file's end.
+	 */
+	APPEND = 0,
+
+	/**
+	 * Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized.
+	 */
+	DSYNC = 1,
+
+	/**
+	 * Non-blocking mode.
+	 */
+	NONBLOCK = 2,
+
+	/**
+	 * Synchronized read I/O operations.
+	 */
+	RSYNC = 3,
+
+	/**
+	 * Write according to synchronized I/O file integrity completion. In
+	 * addition to synchronizing the data stored in the file, the implementation
+	 * may also synchronously update the file's metadata.
+	 */
+	SYNC = 4,
+}
+
+fdstat_t :: struct {
+	/**
+	 * File type.
+	 */
+	fs_filetype: filetype_t,
+
+	/**
+	 * File descriptor flags.
+	 */
+	fs_flags: fdflags_t,
+
+	/**
+	 * Rights that apply to this file descriptor.
+	 */
+	fs_rights_base: rights_t,
+
+	/**
+	 * Maximum set of rights that may be installed on new file descriptors that
+	 * are created through this file descriptor, e.g., through `path_open`.
+	 */
+	fs_rights_inheriting: rights_t,
+}
+
+device_t :: distinct u64
+
+
+fstflags_t :: distinct bit_set[fstflag_t; u16]
+fstflag_t :: enum u16 {
+	/**
+	 * Adjust the last data access timestamp to the value stored in `filestat::atim`.
+	 */
+	ATIM = 0,
+
+	/**
+	 * Adjust the last data access timestamp to the time of clock `clockid::realtime`.
+	 */
+	ATIM_NOW = 1,
+
+	/**
+	 * Adjust the last data modification timestamp to the value stored in `filestat::mtim`.
+	 */
+	MTIM = 2,
+
+	/**
+	 * Adjust the last data modification timestamp to the time of clock `clockid::realtime`.
+	 */
+	MTIM_NOW = 3,
+
+}
+
+lookupflags_t :: distinct bit_set[lookupflag_t; u32]
+lookupflag_t :: enum u32 {
+	/**
+	 * As long as the resolved path corresponds to a symbolic link, it is expanded.
+	 */
+	SYMLINK_FOLLOW = 0,
+}
+
+oflags_t :: distinct bit_set[oflag_t; u16]
+oflag_t :: enum u16 {
+	/**
+	 * Create file if it does not exist.
+	 */
+	CREATE = 0,
+
+	/**
+	 * Fail if not a directory.
+	 */
+	DIRECTORY = 1,
+
+	/**
+	 * Fail if file already exists.
+	 */
+	EXCL = 2,
+
+	/**
+	 * Truncate file to size 0.
+	 */
+	TRUNC = 3,
+}
+
+linkcount_t :: distinct u64
+
+filestat_t :: struct {
+	/**
+	 * Device ID of device containing the file.
+	 */
+	dev: device_t,
+
+	/**
+	 * File serial number.
+	 */
+	ino: inode_t,
+
+	/**
+	 * File type.
+	 */
+	filetype: filetype_t,
+
+	/**
+	 * Number of hard links to the file.
+	 */
+	nlink: linkcount_t,
+
+	/**
+	 * For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link.
+	 */
+	size: filesize_t,
+
+	/**
+	 * Last data access timestamp.
+	 */
+	atim: timestamp_t,
+
+	/**
+	 * Last data modification timestamp.
+	 */
+	mtim: timestamp_t,
+
+	/**
+	 * Last file status change timestamp.
+	 */
+	ctim: timestamp_t,
+}
+
+userdata_t :: distinct u64
+
+eventtype_t :: enum u8 {
+	/**
+	 * The time value of clock `subscription_clock::id` has
+	 * reached timestamp `subscription_clock::timeout`.
+	 */
+	CLOCK = 0,
+
+	/**
+	 * File descriptor `subscription_fd_readwrite::file_descriptor` has data
+	 * available for reading. This event always triggers for regular files.
+	 */
+	FD_READ = 1,
+
+	/**
+	 * File descriptor `subscription_fd_readwrite::file_descriptor` has capacity
+	 * available for writing. This event always triggers for regular files.
+	 */
+	FD_WRITE = 2,
+}
+
+eventrwflags_t :: distinct bit_set[eventrwflag_t; u16]
+eventrwflag_t :: enum u16 {
+	/**
+	 * The peer of this socket has closed or disconnected.
+	 */
+	FD_READWRITE_HANGUP = 0,
+}
+
+event_fd_readwrite_t :: struct {
+	/**
+	 * The number of bytes available for reading or writing.
+	 */
+	nbytes: filesize_t,
+
+	/**
+	 * The state of the file descriptor.
+	 */
+	flags: eventrwflags_t,
+}
+
+event_t :: struct {
+	/**
+	 * User-provided value that got attached to `subscription::userdata`.
+	 */
+	userdata: userdata_t,
+
+	/**
+	 * If non-zero, an error that occurred while processing the subscription request.
+	 */
+	error: errno_t,
+
+	/**
+	 * The type of event that occured
+	 */
+	type: eventtype_t,
+
+	/**
+	 * The contents of the event, if it is an `eventtype::fd_read` or
+	 * `eventtype::fd_write`. `eventtype::clock` events ignore this field.
+	 */
+	fd_readwrite: event_fd_readwrite_t,
+}
+
+subclockflags_t :: distinct bit_set[subclockflag_t; u16]
+subclockflag_t :: enum u16 {
+	/**
+	 * If set, treat the timestamp provided in
+	 * `subscription_clock::timeout` as an absolute timestamp of clock
+	 * `subscription_clock::id`. If clear, treat the timestamp
+	 * provided in `subscription_clock::timeout` relative to the
+	 * current time value of clock `subscription_clock::id`.
+	 */
+	SUBSCRIPTION_CLOCK_ABSTIME = 0,
+
+}
+
+subscription_clock_t :: struct {
+	/**
+	 * The clock against which to compare the timestamp.
+	 */
+	id: clockid_t,
+
+	/**
+	 * The absolute or relative timestamp.
+	 */
+	timeout: timestamp_t,
+
+	/**
+	 * The amount of time that the implementation may wait additionally
+	 * to coalesce with other events.
+	 */
+	precision: timestamp_t,
+
+	/**
+	 * Flags specifying whether the timeout is absolute or relative
+	 */
+	flags: subclockflags_t,
+}
+
+subscription_fd_readwrite_t :: struct {
+	/**
+	 * The file descriptor on which to wait for it to become ready for reading or writing.
+	 */
+	file_descriptor: fd_t,
+}
+
+subscription_t :: struct {
+	/**
+	 * User-provided value that is attached to the subscription in the
+	 * implementation and returned through `event::userdata`.
+	 */
+	userdata: userdata_t,
+
+	/**
+	 * The type of the event to which to subscribe, and its contents
+	 */
+	using contents: struct {
+		tag: u8,
+		using u: struct #raw_union {
+			clock: subscription_clock_t,
+			fd_read: subscription_fd_readwrite_t,
+			fd_write: subscription_fd_readwrite_t,
+		},
+	},
+}
+
+exitcode_t :: distinct u32
+
+signal_t :: enum u8 {
+	/**
+	 * No signal. Note that POSIX has special semantics for `kill(pid, 0)`,
+	 * so this value is reserved.
+	 */
+	NONE = 0,
+
+	/**
+	 * Hangup.
+	 * Action: Terminates the process.
+	 */
+	HUP = 1,
+
+	/**
+	 * Terminate interrupt signal.
+	 * Action: Terminates the process.
+	 */
+	INT = 2,
+
+	/**
+	 * Terminal quit signal.
+	 * Action: Terminates the process.
+	 */
+	QUIT = 3,
+
+	/**
+	 * Illegal instruction.
+	 * Action: Terminates the process.
+	 */
+	ILL = 4,
+
+	/**
+	 * Trace/breakpoint trap.
+	 * Action: Terminates the process.
+	 */
+	TRAP = 5,
+
+	/**
+	 * Process abort signal.
+	 * Action: Terminates the process.
+	 */
+	ABRT = 6,
+
+	/**
+	 * Access to an undefined portion of a memory object.
+	 * Action: Terminates the process.
+	 */
+	BUS = 7,
+
+	/**
+	 * Erroneous arithmetic operation.
+	 * Action: Terminates the process.
+	 */
+	FPE = 8,
+
+	/**
+	 * Kill.
+	 * Action: Terminates the process.
+	 */
+	KILL = 9,
+
+	/**
+	 * User-defined signal 1.
+	 * Action: Terminates the process.
+	 */
+	USR1 = 10,
+
+	/**
+	 * Invalid memory reference.
+	 * Action: Terminates the process.
+	 */
+	SEGV = 11,
+
+	/**
+	 * User-defined signal 2.
+	 * Action: Terminates the process.
+	 */
+	USR2 = 12,
+
+	/**
+	 * Write on a pipe with no one to read it.
+	 * Action: Ignored.
+	 */
+	PIPE = 13,
+
+	/**
+	 * Alarm clock.
+	 * Action: Terminates the process.
+	 */
+	ALRM = 14,
+
+	/**
+	 * Termination signal.
+	 * Action: Terminates the process.
+	 */
+	TERM = 15,
+
+	/**
+	 * Child process terminated, stopped, or continued.
+	 * Action: Ignored.
+	 */
+	CHLD = 16,
+
+	/**
+	 * Continue executing, if stopped.
+	 * Action: Continues executing, if stopped.
+	 */
+	CONT = 17,
+
+	/**
+	 * Stop executing.
+	 * Action: Stops executing.
+	 */
+	STOP = 18,
+
+	/**
+	 * Terminal stop signal.
+	 * Action: Stops executing.
+	 */
+	TSTP = 19,
+
+	/**
+	 * Background process attempting read.
+	 * Action: Stops executing.
+	 */
+	TTIN = 20,
+
+	/**
+	 * Background process attempting write.
+	 * Action: Stops executing.
+	 */
+	TTOU = 21,
+
+	/**
+	 * High bandwidth data is available at a socket.
+	 * Action: Ignored.
+	 */
+	URG = 22,
+
+	/**
+	 * CPU time limit exceeded.
+	 * Action: Terminates the process.
+	 */
+	XCPU = 23,
+
+	/**
+	 * File size limit exceeded.
+	 * Action: Terminates the process.
+	 */
+	XFSZ = 24,
+
+	/**
+	 * Virtual timer expired.
+	 * Action: Terminates the process.
+	 */
+	VTALRM = 25,
+
+	/**
+	 * Profiling timer expired.
+	 * Action: Terminates the process.
+	 */
+	PROF = 26,
+
+	/**
+	 * Window changed.
+	 * Action: Ignored.
+	 */
+	WINCH = 27,
+
+	/**
+	 * I/O possible.
+	 * Action: Terminates the process.
+	 */
+	POLL = 28,
+
+	/**
+	 * Power failure.
+	 * Action: Terminates the process.
+	 */
+	PWR = 29,
+
+	/**
+	 * Bad system call.
+	 * Action: Terminates the process.
+	 */
+	SYS = 30,
+}
+
+
+riflags_t :: distinct bit_set[riflag_t; u16]
+riflag_t :: enum u16 {
+	/**
+	 * Returns the message without removing it from the socket's receive queue.
+	 */
+	RECV_PEEK = 0,
+
+	/**
+	 * On byte-stream sockets, block until the full amount of data can be returned.
+	 */
+	RECV_WAITALL = 1,
+}
+
+roflags_t :: distinct bit_set[roflag_t; u16]
+roflag_t :: enum u16 {
+	/**
+	 * Returned by `sock_recv`: Message data has been truncated.
+	 */
+	RECV_DATA_TRUNCATED = 0,
+}
+
+siflags_t :: distinct bit_set[siflag_t; u16]
+siflag_t :: enum u16 {
+}
+
+
+sdflags_t :: distinct bit_set[sdflag_t; u8]
+sdflag_t :: enum u8 {
+	/**
+	 * Disables further receive operations.
+	 */
+	RD = 0,
+
+	/**
+	 * Disables further send operations.
+	 */
+	WR = 1,
+}
+
+preopentype_t :: enum u8 {
+	DIR = 0,
+}
+
+prestat_dir_t :: struct {
+	pr_name_len: size_t,
+}
+
+prestat_t :: struct {
+	tag: u8,
+	using u: struct {
+		dir: prestat_dir_t,
+	},
+}
+
+@(default_calling_convention="c")
+foreign wasi {
+	/**
+	 * Read command-line argument data.
+	 * The size of the array should match that returned by `args_sizes_get`
+	 */
+	args_get :: proc(
+		argv: [^]cstring,
+		argv_buf: [^]byte,
+	) -> errno_t ---
+	/**
+	 * Read environment variable data.
+	 * The sizes of the buffers should match that returned by `environ_sizes_get`.
+	 */
+	environ_get :: proc(
+		environ: [^]cstring,
+		environ_buf: [^]byte,
+	) -> errno_t ---
+	/**
+	 * Provide file advisory information on a file descriptor.
+	 * Note: This is similar to `posix_fadvise` in POSIX.
+	 */
+	fd_advise :: proc(
+		fd: fd_t,
+		/**
+		 * The offset within the file to which the advisory applies.
+		 */
+		offset: filesize_t,
+		/**
+		 * The length of the region to which the advisory applies.
+		 */
+		len: filesize_t,
+		/**
+		 * The advice.
+		 */
+		advice: advice_t,
+	) -> errno_t ---
+	/**
+	 * Force the allocation of space in a file.
+	 * Note: This is similar to `posix_fallocate` in POSIX.
+	 */
+	fd_allocate :: proc(
+		fd: fd_t,
+		/**
+		 * The offset at which to start the allocation.
+		 */
+		offset: filesize_t,
+		/**
+		 * The length of the area that is allocated.
+		 */
+		len: filesize_t,
+	) -> errno_t ---
+	/**
+	 * Close a file descriptor.
+	 * Note: This is similar to `close` in POSIX.
+	 */
+	fd_close :: proc(
+		fd: fd_t,
+	) -> errno_t ---
+	/**
+	 * Synchronize the data of a file to disk.
+	 * Note: This is similar to `fdatasync` in POSIX.
+	 */
+	fd_datasync :: proc(
+		fd: fd_t,
+	) -> errno_t ---
+	/**
+	 * Adjust the flags associated with a file descriptor.
+	 * Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
+	 */
+	fd_fdstat_set_flags :: proc(
+		fd: fd_t,
+		/**
+		 * The desired values of the file descriptor flags.
+		 */
+		flags: fdflags_t,
+	) -> errno_t ---
+	/**
+	 * Adjust the rights associated with a file descriptor.
+	 * This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights
+	 */
+	fd_fdstat_set_rights :: proc(
+		fd: fd_t,
+		/**
+		 * The desired rights of the file descriptor.
+		 */
+		fs_rights_base: rights_t,
+		fs_rights_inheritin: rights_t,
+	) -> errno_t ---
+	/**
+	 * Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
+	 * Note: This is similar to `ftruncate` in POSIX.
+	 */
+	fd_filestat_set_size :: proc(
+		fd: fd_t,
+		/**
+		 * The desired file size.
+		 */
+		size: filesize_t,
+	) -> errno_t ---
+	/**
+	 * Adjust the timestamps of an open file or directory.
+	 * Note: This is similar to `futimens` in POSIX.
+	 */
+	fd_filestat_set_times :: proc(
+		fd: fd_t,
+		/**
+		 * The desired values of the data access timestamp.
+		 */
+		atim: timestamp_t,
+		/**
+		 * The desired values of the data modification timestamp.
+		 */
+		mtim: timestamp_t,
+		/**
+		 * A bitmask indicating which timestamps to adjust.
+		 */
+		fst_flags: fstflags_t,
+	) -> errno_t ---
+	/**
+	 * Atomically replace a file descriptor by renumbering another file descriptor.
+	 * Due to the strong focus on thread safety, this environment does not provide
+	 * a mechanism to duplicate or renumber a file descriptor to an arbitrary
+	 * number, like `dup2()`. This would be prone to race conditions, as an actual
+	 * file descriptor with the same number could be allocated by a different
+	 * thread at the same time.
+	 * This function provides a way to atomically renumber file descriptors, which
+	 * would disappear if `dup2()` were to be removed entirely.
+	 */
+	fd_renumber :: proc(
+		fd: fd_t,
+		/**
+		 * The file descriptor to overwrite.
+		 */
+		to: fd_t,
+	) -> errno_t ---
+	/**
+	 * Synchronize the data and metadata of a file to disk.
+	 * Note: This is similar to `fsync` in POSIX.
+	 */
+	fd_sync :: proc(
+		f: fd_t,
+	) -> errno_t ---
+	/**
+	 * Terminate the process normally. An exit code of 0 indicates successful
+	 * termination of the program. The meanings of other values is dependent on
+	 * the environment.
+	 */
+	proc_exit :: proc(
+		/**
+		 * The exit code returned by the process.
+		 */
+		rval: exitcode_t,
+	) -> ! ---
+	/**
+	 * Send a signal to the process of the calling thread.
+	 * Note: This is similar to `raise` in POSIX.
+	 */
+	proc_raise :: proc(
+		/**
+		 * The signal condition to trigger.
+		 */
+		sig: signal_t,
+	) -> errno_t ---
+	/**
+	 * Temporarily yield execution of the calling thread.
+	 * Note: This is similar to `sched_yield` in POSIX.
+	 */
+	sched_yield :: proc() -> errno_t ---
+	/**
+	 * Shut down socket send and receive channels.
+	 * Note: This is similar to `shutdown` in POSIX.
+	 */
+	sock_shutdown :: proc(
+		fd: fd_t,
+		/**
+		 * Which channels on the socket to shut down.
+		 */
+		how: sdflags_t,
+	) -> errno_t ---
+}
+
+/**
+ * Return command-line argument data sizes.
+ * @return
+ * Returns the number of arguments and the size of the argument string
+ * data, or an error.
+ */
+args_sizes_get :: proc "c" () -> (num_args, size_of_args: size_t, err: errno_t) {
+	err = wasi_args_sizes_get(&num_args, &size_of_args)
+	return
+}
+/**
+ * Return environment variable data sizes.
+ * @return
+ * Returns the number of environment variable arguments and the size of the
+ * environment variable data.
+ */
+environ_sizes_get :: proc "c" () -> (num_envs, size_of_envs: size_t, err: errno_t) {
+	err = wasi_environ_sizes_get(&num_envs, &size_of_envs)
+	return
+}
+/**
+ * Return the resolution of a clock.
+ * Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
+ * return `errno::inval`.
+ * Note: This is similar to `clock_getres` in POSIX.
+ * @return
+ * The resolution of the clock, or an error if one happened.
+ */
+clock_res_get :: proc "c" (
+	/**
+	 * The clock for which to return the resolution.
+	 */
+	id: clockid_t,
+) -> (ts: timestamp_t, err: errno_t) {
+	err = wasi_clock_res_get(id, &ts)
+	return
+}
+/**
+ * Return the time value of a clock.
+ * Note: This is similar to `clock_gettime` in POSIX.
+ * @return
+ * The time value of the clock.
+ */
+clock_time_get :: proc "c" (
+	/**
+	 * The clock for which to return the time.
+	 */
+	id: clockid_t,
+	/**
+	 * The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
+	 */
+	precision: timestamp_t,
+) -> (ts: timestamp_t, err: errno_t) {
+	err = wasi_clock_time_get(id, precision, &ts)
+	return
+}
+/**
+ * Get the attributes of a file descriptor.
+ * Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
+ * @return
+ * The buffer where the file descriptor's attributes are stored.
+ */
+fd_fdstat_get :: proc "c" (
+	fd: fd_t,
+) -> (stat: fdstat_t, err: errno_t) {
+	err = wasi_fd_fdstat_get(fd, &stat)
+	return
+}
+/**
+ * Return the attributes of an open file.
+ * @return
+ * The buffer where the file's attributes are stored.
+ */
+fd_filestat_get :: proc "c" (
+	fd: fd_t,
+) -> (stat: filestat_t, err: errno_t) {
+	err = wasi_fd_filestat_get(fd, &stat)
+	return
+}
+
+
+
+
+/**
+ * Read from a file descriptor, without using and updating the file descriptor's offset.
+ * Note: This is similar to `preadv` in POSIX.
+ * @return
+ * The number of bytes read.
+ */
+fd_pread :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors in which to store data.
+	 */
+	iovs: []iovec_t,
+	/**
+	 * The offset within the file at which to read.
+	 */
+	offset: filesize_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n)
+	return
+}
+/**
+ * Return a description of the given preopened file descriptor.
+ * @return
+ * The buffer where the description is stored.
+ */
+fd_prestat_get :: proc "c" (
+	fd: fd_t,
+) -> (desc: prestat_t, err: errno_t) {
+	err = wasi_fd_prestat_get(fd, &desc)
+	return
+}
+/**
+ * Write to a file descriptor, without using and updating the file descriptor's offset.
+ * Note: This is similar to `pwritev` in POSIX.
+ * @return
+ * The number of bytes written.
+ */
+fd_pwrite :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors from which to retrieve data.
+	 */
+	iovs: []ciovec_t,
+	/**
+	 * The offset within the file at which to write.
+	 */
+	offset: filesize_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n)
+	return
+}
+/**
+ * Read from a file descriptor.
+ * Note: This is similar to `readv` in POSIX.
+ * @return
+ * The number of bytes read.
+ */
+fd_read :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors to which to store data.
+	 */
+	iovs: []iovec_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n)
+	return
+}
+/**
+ * Read directory entries from a directory.
+ * When successful, the contents of the output buffer consist of a sequence of
+ * directory entries. Each directory entry consists of a `dirent` object,
+ * followed by `dirent::d_namlen` bytes holding the name of the directory
+ * entry.
+ * This function fills the output buffer as much as possible, potentially
+ * truncating the last directory entry. This allows the caller to grow its
+ * read buffer size in case it's too small to fit a single large directory
+ * entry, or skip the oversized directory entry.
+ * @return
+ * The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached.
+ */
+fd_readdir :: proc "c" (
+	fd: fd_t,
+	/**
+	 * The buffer where directory entries are stored
+	 */
+	buf: []byte,
+	/**
+	 * The location within the directory to start reading
+	 */
+	cookie: dircookie_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n)
+	return
+}
+/**
+ * Move the offset of a file descriptor.
+ * Note: This is similar to `lseek` in POSIX.
+ * @return
+ * The new offset of the file descriptor, relative to the start of the file.
+ */
+fd_seek :: proc "c" (
+	fd: fd_t,
+	/**
+	 * The number of bytes to move.
+	 */
+	offset: filedelta_t,
+	/**
+	 * The base from which the offset is relative.
+	 */
+	whence: whence_t,
+) -> (new_offset: filesize_t, err: errno_t) {
+	err = wasi_fd_seek(fd, offset, whence, &new_offset)
+	return
+}
+/**
+ * Return the current offset of a file descriptor.
+ * Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
+ * @return
+ * The current offset of the file descriptor, relative to the start of the file.
+ */
+fd_tell :: proc "c" (
+	fd: fd_t,
+) -> (offset: filesize_t, err: errno_t) {
+	err = wasi_fd_tell(fd, &offset)
+	return
+}
+/**
+ * Write to a file descriptor.
+ * Note: This is similar to `writev` in POSIX.
+ */
+fd_write :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors from which to retrieve data.
+	 */
+	iovs: []ciovec_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n)
+	return
+}
+/**
+ * Return the attributes of a file or directory.
+ * Note: This is similar to `stat` in POSIX.
+ * @return
+ * The buffer where the file's attributes are stored.
+ */
+path_filestat_get :: proc "c" (
+	fd: fd_t,
+	/**
+	 * Flags determining the method of how the path is resolved.
+	 */
+	flags: lookupflags_t,
+	/**
+	 * The path of the file or directory to inspect.
+	 */
+	path: string,
+) -> (offset: filestat_t, err: errno_t) {
+	err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset)
+	return
+}
+/**
+ * Open a file or directory.
+ * The returned file descriptor is not guaranteed to be the lowest-numbered
+ * file descriptor not currently open; it is randomized to prevent
+ * applications from depending on making assumptions about indexes, since this
+ * is error-prone in multi-threaded contexts. The returned file descriptor is
+ * guaranteed to be less than 2**31.
+ * Note: This is similar to `openat` in POSIX.
+ * @return
+ * The file descriptor of the file that has been opened.
+ */
+path_open :: proc "c" (
+	fd: fd_t,
+	/**
+	 * Flags determining the method of how the path is resolved.
+	 */
+	dirflags: lookupflags_t,
+	/**
+	 * The relative path of the file or directory to open, relative to the
+	 * `path_open::fd` directory.
+	 */
+	path: string,
+	/**
+	 * The method by which to open the file.
+	 */
+	oflags: oflags_t,
+	/**
+	 * The initial rights of the newly created file descriptor. The
+	 * implementation is allowed to return a file descriptor with fewer rights
+	 * than specified, if and only if those rights do not apply to the type of
+	 * file being opened.
+	 * The *base* rights are rights that will apply to operations using the file
+	 * descriptor itself, while the *inheriting* rights are rights that apply to
+	 * file descriptors derived from it.
+	 */
+	fs_rights_base: rights_t,
+	fs_rights_inheriting: rights_t,
+	fdflags: fdflags_t,
+) -> (file: fd_t, err: errno_t) {
+	err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
+	return
+}
+/**
+ * Read the contents of a symbolic link.
+ * Note: This is similar to `readlinkat` in POSIX.
+ * @return
+ * The number of bytes placed in the buffer.
+ */
+path_readlink :: proc "c" (
+	fd: fd_t,
+	/**
+	 * The path of the symbolic link from which to read.
+	 */
+	path: string,
+	/**
+	 * The buffer to which to write the contents of the symbolic link.
+	 */
+	buf: []u8,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n)
+	return
+}
+/**
+ * Concurrently poll for the occurrence of a set of events.
+ * @return
+ * The number of events stored.
+ */
+poll_oneoff :: proc "c" (
+	/**
+	 * The events to which to subscribe.
+	 */
+	subscription_in: ^subscription_t,
+	/**
+	 * The events that have occurred.
+	 */
+	event_out: ^event_t,
+	/**
+	 * Both the number of subscriptions and events.
+	 */
+	nsubscriptions: size_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_poll_oneoff(subscription_in, event_out, nsubscriptions, &n)
+	return
+}
+/**
+ * Receive a message from a socket.
+ * Note: This is similar to `recv` in POSIX, though it also supports reading
+ * the data into multiple buffers in the manner of `readv`.
+ * @return
+ * Number of bytes stored in ri_data and message flags.
+ */
+sock_recv :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors to which to store data.
+	 */
+	ri_data: []iovec_t,
+	/**
+	 * Message flags.
+	 */
+	ri_flags: riflags_t,
+) -> (n: size_t, flags: roflags_t, err: errno_t) {
+	err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags)
+	return
+}
+/**
+ * Send a message on a socket.
+ * Note: This is similar to `send` in POSIX, though it also supports writing
+ * the data from multiple buffers in the manner of `writev`.
+ * @return
+ * Number of bytes transmitted.
+ */
+sock_send :: proc "c" (
+	fd: fd_t,
+	/**
+	 * List of scatter/gather vectors to which to retrieve data
+	 */
+	si_data: []ciovec_t,
+	/**
+	 * Message flags.
+	 */
+	si_flags: siflags_t,
+) -> (n: size_t, err: errno_t) {
+	err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n)
+	return
+}
+
+/**
+ * Return a description of the given preopened file descriptor.
+ */
+fd_prestat_dir_name :: proc(
+	fd: fd_t,
+	/**
+	 * A buffer into which to write the preopened directory name.
+	 */
+	path: string,
+) -> errno_t {
+	return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path))
+}
+/**
+ * Create a directory.
+ * Note: This is similar to `mkdirat` in POSIX.
+ */
+path_create_directory :: proc(
+	fd: fd_t,
+	/**
+	 * The path at which to create the directory.
+	 */
+	path: string,
+) -> errno_t {
+	return wasm_path_create_directory(fd, raw_data(path), len(path))
+}
+/**
+ * Adjust the timestamps of a file or directory.
+ * Note: This is similar to `utimensat` in POSIX.
+ */
+path_filestat_set_times :: proc(
+	fd: fd_t,
+	/**
+	 * Flags determining the method of how the path is resolved.
+	 */
+	flags: lookupflags_t,
+	/**
+	 * The path of the file or directory to operate on.
+	 */
+	path: string,
+	/**
+	 * The desired values of the data access timestamp.
+	 */
+	atim: timestamp_t,
+	/**
+	 * The desired values of the data modification timestamp.
+	 */
+	mtim: timestamp_t,
+	/**
+	 * A bitmask indicating which timestamps to adjust.
+	 */
+	fst_flags: fstflags_t,
+) -> errno_t {
+	return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags)
+}
+/**
+ * Remove a directory.
+ * Return `errno::notempty` if the directory is not empty.
+ * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+ */
+path_remove_directory :: proc(
+	fd: fd_t,
+	/**
+	 * The path to a directory to remove.
+	 */
+	path: string,
+) -> errno_t {
+	return wasm_path_remove_directory(fd, raw_data(path), len(path))
+}
+/**
+ * Create a hard link.
+ * Note: This is similar to `linkat` in POSIX.
+ */
+path_link :: proc(
+	old_fd: fd_t,
+	/**
+	 * Flags determining the method of how the path is resolved.
+	 */
+	old_flags: lookupflags_t,
+	/**
+	 * The source path from which to link.
+	 */
+	old_path: string,
+	/**
+	 * The working directory at which the resolution of the new path starts.
+	 */
+	new_fd: fd_t,
+	/**
+	 * The destination path at which to create the hard link.
+	 */
+	new_path: string,
+) -> errno_t {
+	return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
+}
+
+/**
+ * Rename a file or directory.
+ * Note: This is similar to `renameat` in POSIX.
+ */
+path_rename :: proc(
+	fd: fd_t,
+	/**
+	 * The source path of the file or directory to rename.
+	 */
+	old_path: string,
+	/**
+	 * The working directory at which the resolution of the new path starts.
+	 */
+	new_fd: fd_t,
+	/**
+	 * The destination path to which to rename the file or directory.
+	 */
+	new_path: string,
+) -> errno_t {
+	return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
+}
+/**
+ * Create a symbolic link.
+ * Note: This is similar to `symlinkat` in POSIX.
+ */
+path_symlink :: proc(
+	/**
+	 * The contents of the symbolic link.
+	 */
+	old_path: string,
+	fd: fd_t,
+	/**
+	 * The destination path at which to create the symbolic link.
+	 */
+	new_path: string,
+) -> errno_t {
+	return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path))
+}
+/**
+ * Unlink a file.
+ * Return `errno::isdir` if the path refers to a directory.
+ * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+ */
+path_unlink_file :: proc(
+	fd: fd_t,
+	/**
+	 * The path to a file to unlink.
+	 */
+	path: string,
+) -> errno_t {
+	return wasm_path_unlink_file(fd, raw_data(path), len(path))
+}
+/**
+ * Write high-quality random data into a buffer.
+ * This function blocks when the implementation is unable to immediately
+ * provide sufficient high-quality random data.
+ * This function may execute slowly, so when large mounts of random data are
+ * required, it's advisable to use this function to seed a pseudo-random
+ * number generator, rather than to provide the random data directly.
+ */
+random_get :: proc(
+	/**
+	 * The buffer to fill with random data.
+	 */
+	buf: []u8,
+) -> errno_t {
+	return wasm_random_get(raw_data(buf), len(buf))
+}
+
+
+
+
+@(default_calling_convention="c")
+foreign wasi {
+	@(link_name="args_sizes_get")
+	wasi_args_sizes_get :: proc(
+		retptr0: ^size_t,
+		retptr1: ^size_t,
+	) -> errno_t ---
+	@(link_name="environ_sizes_get")
+	wasi_environ_sizes_get :: proc(
+		retptr0: ^size_t,
+		retptr1: ^size_t,
+	) -> errno_t ---
+	@(link_name="clock_res_get")
+	wasi_clock_res_get :: proc(
+		id: clockid_t,
+		retptr0: ^timestamp_t,
+	) -> errno_t ---
+	@(link_name="clock_time_get")
+	wasi_clock_time_get :: proc(
+		id: clockid_t,
+		precision: timestamp_t,
+		retptr0: ^timestamp_t,
+	) -> errno_t ---
+	@(link_name="fd_fdstat_get")
+	wasi_fd_fdstat_get :: proc(
+		fd: fd_t,
+		retptr0: ^fdstat_t,
+	) -> errno_t ---
+	@(link_name="fd_filestat_get")
+	wasi_fd_filestat_get :: proc(
+		fd: fd_t,
+		retptr0: ^filestat_t,
+	) -> errno_t ---
+	@(link_name="fd_pread")
+	wasi_fd_pread :: proc(
+		fd: fd_t,
+		iovs: [^]iovec_t,
+		iovs_len: size_t,
+		offset: filesize_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="fd_prestat_get")
+	wasi_fd_prestat_get :: proc(
+		fd: fd_t,
+		retptr0: ^prestat_t,
+	) -> errno_t ---
+	@(link_name="fd_pwrite")
+	wasi_fd_pwrite :: proc(
+		fd: fd_t,
+		iovs: [^]ciovec_t,
+		iovs_len: size_t,
+		offset: filesize_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="fd_read")
+	wasi_fd_read :: proc(
+		fd: fd_t,
+		iovs: [^]iovec_t,
+		iovs_len: size_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="fd_readdir")
+	wasi_fd_readdir :: proc(
+		fd: fd_t,
+		buf: [^]u8,
+		buf_len: size_t,
+		cookie: dircookie_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="fd_seek")
+	wasi_fd_seek :: proc(
+		fd: fd_t,
+		offset: filedelta_t,
+		whence: whence_t,
+		retptr0: ^filesize_t,
+	) -> errno_t ---
+	@(link_name="fd_tell")
+	wasi_fd_tell :: proc(
+		fd: fd_t,
+		retptr0: ^filesize_t,
+	) -> errno_t ---
+	@(link_name="fd_write")
+	wasi_fd_write :: proc(
+		fd: fd_t,
+		iovs: [^]ciovec_t,
+		iovs_len: size_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="path_filestat_get")
+	wasi_path_filestat_get :: proc(
+		fd: fd_t,
+		flags: lookupflags_t,
+		/**
+		 * The path of the file or directory to inspect.
+		 */
+		path: [^]u8,
+		path_len: size_t,
+		retptr0: ^filestat_t,
+	) -> errno_t ---
+	@(link_name="path_open")
+	wasi_path_open :: proc(
+		fd: fd_t,
+		dirflags: lookupflags_t,
+		path: [^]u8,
+		path_len: size_t,
+		oflags: oflags_t,
+		fs_rights_base: rights_t,
+		fs_rights_inheriting: rights_t,
+		fdflags: fdflags_t,
+		retptr: ^fd_t,
+	) -> errno_t ---
+	@(link_name="path_readlink")
+	wasi_path_readlink :: proc(
+		fd: fd_t,
+		path: [^]u8,
+		path_len: size_t,
+		buf: [^]u8,
+		buf_len: size_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="poll_oneoff")
+	wasi_poll_oneoff :: proc(
+		subscription_in: ^subscription_t,
+		event_out: ^event_t,
+		nsubscriptions: size_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="sock_recv")
+	wasi_sock_recv :: proc(
+		fd: fd_t,
+		ri_data: [^]iovec_t,
+		ri_data_len: size_t,
+		ri_flags: riflags_t,
+		retptr0: ^size_t,
+		retptr1: ^roflags_t,
+	) -> errno_t ---
+	@(link_name="sock_send")
+	wasi_sock_send :: proc(
+		fd: fd_t,
+		si_data: [^]ciovec_t,
+		si_data_len: size_t,
+		si_flags: siflags_t,
+		retptr0: ^size_t,
+	) -> errno_t ---
+	@(link_name="fd_prestat_dir_name")
+	wasm_fd_prestat_dir_name :: proc(
+		fd: fd_t,
+		path: [^]u8,
+		path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_create_directory")
+	wasm_path_create_directory :: proc(
+		fd: fd_t,
+		path: [^]u8,
+		path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_filestat_set_times")
+	wasm_path_filestat_set_times :: proc(
+		fd: fd_t,
+		flags: lookupflags_t,
+		path: [^]u8,
+		path_len: size_t,
+		atim: timestamp_t,
+		mtim: timestamp_t,
+		fst_flags: fstflags_t,
+	) -> errno_t ---
+	@(link_name="path_remove_directory")
+	wasm_path_remove_directory :: proc(
+		fd: fd_t,
+		path: [^]u8,
+		path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_link")
+	wasm_path_link :: proc(
+		old_fd: fd_t,
+		old_flags: lookupflags_t,
+		old_path: [^]u8,
+		old_path_len: size_t,
+		new_fd: fd_t,
+		new_path: [^]u8,
+		new_path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_rename")
+	wasm_path_rename :: proc(
+		fd: fd_t,
+		old_path: [^]u8,
+		old_path_len: size_t,
+		new_fd: fd_t,
+		new_path: [^]u8,
+		new_path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_symlink")
+	wasm_path_symlink :: proc(
+		old_path: [^]u8,
+		old_path_len: size_t,
+		fd: fd_t,
+		new_path: [^]u8,
+		new_path_len: size_t,
+	) -> errno_t ---
+	@(link_name="path_unlink_file")
+	wasm_path_unlink_file :: proc(
+		fd: fd_t,
+		path: [^]u8,
+		path_len: size_t,
+	) -> errno_t ---
+	@(link_name="random_get")
+	wasm_random_get :: proc(
+		buf: [^]u8,
+		buf_len: size_t,
+	) -> errno_t ---
+}

+ 58 - 6
src/build_settings.cpp

@@ -16,6 +16,8 @@ enum TargetOsKind {
 	TargetOs_linux,
 	TargetOs_essence,
 	TargetOs_freebsd,
+	
+	TargetOs_wasi,
 
 	TargetOs_freestanding,
 
@@ -29,6 +31,7 @@ enum TargetArchKind {
 	TargetArch_386,
 	TargetArch_arm64,
 	TargetArch_wasm32,
+	TargetArch_wasm64,
 
 	TargetArch_COUNT,
 };
@@ -49,6 +52,8 @@ String target_os_names[TargetOs_COUNT] = {
 	str_lit("linux"),
 	str_lit("essence"),
 	str_lit("freebsd"),
+	
+	str_lit("wasi"),
 
 	str_lit("freestanding"),
 };
@@ -59,6 +64,7 @@ String target_arch_names[TargetArch_COUNT] = {
 	str_lit("386"),
 	str_lit("arm64"),
 	str_lit("wasm32"),
+	str_lit("wasm64"),
 };
 
 String target_endian_names[TargetEndian_COUNT] = {
@@ -72,6 +78,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
 	TargetEndian_Little,
 	TargetEndian_Little,
 	TargetEndian_Little,
+	TargetEndian_Little,
 };
 
 #ifndef ODIN_VERSION_RAW
@@ -335,6 +342,26 @@ gb_global TargetMetrics target_freestanding_wasm32 = {
 	str_lit(""),
 };
 
+gb_global TargetMetrics target_freestanding_wasm64 = {
+	TargetOs_freestanding,
+	TargetArch_wasm64,
+	8,
+	16,
+	str_lit("wasm64-freestanding-js"),
+	str_lit(""),
+};
+
+gb_global TargetMetrics target_wasi_wasm32 = {
+	TargetOs_wasi,
+	TargetArch_wasm32,
+	4,
+	8,
+	str_lit("wasm32-wasi-js"),
+	str_lit(""),
+};
+
+
+
 
 
 struct NamedTargetMetrics {
@@ -353,6 +380,8 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("freebsd_386"),    &target_freebsd_386    },
 	{ str_lit("freebsd_amd64"),  &target_freebsd_amd64  },
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
+	// { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
+	{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
 };
 
 NamedTargetMetrics *selected_target_metrics;
@@ -458,11 +487,21 @@ bool find_library_collection_path(String name, String *path) {
 }
 
 bool is_arch_wasm(void) {
-	return build_context.metrics.arch == TargetArch_wasm32;
+	switch (build_context.metrics.arch) {
+	case TargetArch_wasm32:
+	case TargetArch_wasm64:
+		return true;
+	}
+	return false;
 }
 
 bool allow_check_foreign_filepath(void) {
-	return build_context.metrics.arch != TargetArch_wasm32;
+	switch (build_context.metrics.arch) {
+	case TargetArch_wasm32:
+	case TargetArch_wasm64:
+		return false;
+	}
+	return true;
 }
 
 
@@ -869,11 +908,24 @@ void init_build_context(TargetMetrics *cross_target) {
 			bc->link_flags = str_lit("-arch arm64 ");
 			break;
 		}
-
-	} else if (bc->metrics.arch == TargetArch_wasm32) {
-		bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined ");
+	} else if (is_arch_wasm()) {
+		gbString link_flags = gb_string_make(heap_allocator(), " ");
+		// link_flags = gb_string_appendc(link_flags, "--export-all ");
+		// link_flags = gb_string_appendc(link_flags, "--export-table ");
+		link_flags = gb_string_appendc(link_flags, "--allow-undefined ");
+		if (bc->metrics.arch == TargetArch_wasm64) {
+			link_flags = gb_string_appendc(link_flags, "-mwas64 ");
+		}
+		if (bc->metrics.os == TargetOs_freestanding) {
+			link_flags = gb_string_appendc(link_flags, "--no-entry ");
+		}
+		
+		bc->link_flags = make_string_c(link_flags);
+		
+		// Disallow on wasm
+		build_context.use_separate_modules = false;
 	} else {
-		gb_printf_err("Compiler Error: Unsupported architecture\n");;
+		gb_printf_err("Compiler Error: Unsupported architecture\n");
 		gb_exit(1);
 	}
 

+ 8 - 0
src/check_decl.cpp

@@ -899,6 +899,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 			mutex_unlock(&ctx->info->foreign_mutex);
 		}
 	}
+	
+	if (e->Procedure.link_name.len > 0 ) {
+		e->flags |= EntityFlag_CustomLinkName;
+	}
 }
 
 void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
@@ -990,6 +994,10 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
 			string_map_set(fp, key, e);
 		}
 	}
+	
+	if (e->Variable.link_name.len > 0) {
+		e->flags |= EntityFlag_CustomLinkName;
+	}
 
 	if (init_expr == nullptr) {
 		if (type_expr == nullptr) {

+ 3 - 0
src/checker.cpp

@@ -2011,6 +2011,9 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("gnu_h2f_ieee"),
 		str_lit("gnu_f2h_ieee"),
 		str_lit("extendhfsf2"),
+		
+		// WASM Specific
+		str_lit("__ashlti3"),
 	};
 	for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
 		force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);

+ 2 - 1
src/entity.cpp

@@ -74,9 +74,10 @@ enum EntityFlag : u64 {
 
 	EntityFlag_Test          = 1ull<<30,
 	EntityFlag_Init          = 1ull<<31,
+	
+	EntityFlag_CustomLinkName = 1ull<<40,
 
 	EntityFlag_Overridden    = 1ull<<63,
-
 };
 
 enum EntityState : u32 {

+ 82 - 5
src/llvm_abi.cpp

@@ -1039,6 +1039,75 @@ namespace lbAbiArm64 {
 	}
 }
 
+namespace lbAbiWasm32 {
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+		ft->ctx = c;
+		ft->args = compute_arg_types(c, arg_types, arg_count);
+		ft->ret = compute_return_type(c, return_type, return_is_defined);
+		ft->calling_convention = calling_convention;
+		return ft;
+	}
+
+	lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+		if (!is_return && type == LLVMIntTypeInContext(c, 128)) {
+			LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+			return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
+		}
+		
+		if (!is_return && lb_sizeof(type) > 8) {
+			return lb_arg_type_indirect(type, nullptr);
+		}
+
+		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);
+	}
+
+	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+		for (unsigned i = 0; i < arg_count; i++) {
+			LLVMTypeRef t = arg_types[i];
+			LLVMTypeKind kind = LLVMGetTypeKind(t);
+			i64 sz = lb_sizeof(t);
+			if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) {
+				if (sz == 0) {
+					args[i] = lb_arg_type_ignore(t);
+				} else {
+					args[i] = lb_arg_type_indirect(t, nullptr);
+				}
+			} else {
+				args[i] = non_struct(c, t, false);
+			}
+		}
+		return args;
+	}
+
+	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+		if (!return_is_defined) {
+			return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+		} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+			i64 sz = lb_sizeof(return_type);
+			switch (sz) {
+			case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+			case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+			}
+			LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+			return lb_arg_type_indirect(return_type, attr);
+		}
+		return non_struct(c, return_type, true);
+	}
+}
+
 
 LB_ABI_INFO(lb_get_abi_info) {
 	switch (calling_convention) {
@@ -1061,19 +1130,27 @@ LB_ABI_INFO(lb_get_abi_info) {
 		}
 	}
 
-	if (build_context.metrics.arch == TargetArch_amd64) {
+	switch (build_context.metrics.arch) {
+	case TargetArch_amd64:
 		if (build_context.metrics.os == TargetOs_windows) {
 			return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 		} else {
 			return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 		}
-	} else if (build_context.metrics.arch == TargetArch_386) {
+	case TargetArch_386:
 		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
-	} else if (build_context.metrics.arch == TargetArch_arm64) {
+	case TargetArch_arm64:
 		return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
-	} else if (build_context.metrics.arch == TargetArch_wasm32) {
-		return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	case TargetArch_wasm32:
+		// TODO(bill): implement wasm32's ABI correct 
+		// NOTE(bill): this ABI is only an issue for WASI compatibility
+		return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+	case TargetArch_wasm64:
+		// TODO(bill): implement wasm64's ABI correct 
+		// NOTE(bill): this ABI is only an issue for WASI compatibility
+		return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
 	}
+
 	GB_PANIC("Unsupported ABI");
 	return {};
 }

+ 35 - 7
src/llvm_backend.cpp

@@ -771,6 +771,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 	Type *results = alloc_type_tuple();
 
 	Type *t_ptr_cstring = alloc_type_pointer(t_cstring);
+	
+	bool call_cleanup = true;
 
 	bool has_args = false;
 	bool is_dll_main = false;
@@ -782,8 +784,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("hinstDLL"),   t_rawptr, false, true);
 		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"),  t_u32,    false, true);
 		params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
+		call_cleanup = false;
 	} else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) {
 		name = str_lit("mainCRTStartup");
+	} else if (is_arch_wasm()) {
+		name = str_lit("_start");
+		call_cleanup = false;
 	} else {
 		has_args = true;
 		slice_init(&params->Tuple.variables, permanent_allocator(), 2);
@@ -874,8 +880,10 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 	}
 
 	
-	lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime"));
-	lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, false);
+	if (call_cleanup) {
+		lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime"));
+		lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, false);
+	}
 	
 
 	if (is_dll_main) {
@@ -885,6 +893,19 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
 	}
 
 	lb_end_procedure_body(p);
+	
+
+	if (is_arch_wasm()) {
+		LLVMSetLinkage(p->value, LLVMDLLExportLinkage);
+		LLVMSetDLLStorageClass(p->value, LLVMDLLExportStorageClass);
+		LLVMSetVisibility(p->value, LLVMDefaultVisibility);
+		
+		char const *export_name = alloc_cstring(permanent_allocator(), p->name);
+		LLVMAddTargetDependentFunctionAttr(p->value, "wasm-export-name", export_name);
+	} else {
+		LLVMSetLinkage(p->value, LLVMExternalLinkage);
+	}
+	
 
 	if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
 		gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
@@ -1064,14 +1085,10 @@ struct lbLLVMModulePassWorkerData {
 };
 
 WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
-	GB_ASSERT(MULTITHREAD_OBJECT_GENERATION);
-
 	auto wd = cast(lbLLVMModulePassWorkerData *)data;
-
 	LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
 	lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
 	LLVMRunPassManager(module_pass_manager, wd->m->mod);
-
 	return 0;
 }
 
@@ -1148,6 +1165,7 @@ void lb_generate_code(lbGenerator *gen) {
 		LLVMInitializeAArch64Disassembler();
 		break;
 	case TargetArch_wasm32:
+	case TargetArch_wasm64:
 		LLVMInitializeWebAssemblyTargetInfo();
 		LLVMInitializeWebAssemblyTarget();
 		LLVMInitializeWebAssemblyTargetMC();
@@ -1660,6 +1678,8 @@ void lb_generate_code(lbGenerator *gen) {
 
 	for_array(i, gen->modules.entries) {
 		lbModule *m = gen->modules.entries[i].value;
+		
+		lb_run_remove_unused_function_pass(m->mod);
 
 		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
 		wd->m = m;
@@ -1737,8 +1757,16 @@ void lb_generate_code(lbGenerator *gen) {
 	}
 
 	TIME_SECTION("LLVM Object Generation");
+	
+	isize non_empty_module_count = 0;
+	for_array(j, gen->modules.entries) {
+		lbModule *m = gen->modules.entries[j].value;
+		if (!lb_is_module_empty(m)) {
+			non_empty_module_count += 1;
+		}
+	}
 
-	if (do_threading) {
+	if (do_threading && non_empty_module_count > 1) {
 		for_array(j, gen->modules.entries) {
 			lbModule *m = gen->modules.entries[j].value;
 			if (lb_is_module_empty(m)) {

+ 21 - 0
src/llvm_backend.hpp

@@ -585,3 +585,24 @@ enum : LLVMAttributeIndex {
 	LLVMAttributeIndex_FunctionIndex = ~0u,
 	LLVMAttributeIndex_FirstArgIndex = 1,
 };
+
+
+char const *llvm_linkage_strings[] = {
+	"external linkage",
+	"available externally linkage",
+	"link once any linkage",
+	"link once odr linkage",
+	"link once odr auto hide linkage",
+	"weak any linkage",
+	"weak odr linkage",
+	"appending linkage",
+	"internal linkage",
+	"private linkage",
+	"dllimport linkage",
+	"dllexport linkage",
+	"external weak linkage",
+	"ghost linkage",
+	"common linkage",
+	"linker private linkage",
+	"linker private weak linkage"
+};

+ 2 - 0
src/llvm_backend_expr.cpp

@@ -496,6 +496,7 @@ bool lb_is_matrix_simdable(Type *t) {
 		break;
 	case TargetArch_386:
 	case TargetArch_wasm32:
+	case TargetArch_wasm64:
 		// nope
 		return false;
 	}
@@ -513,6 +514,7 @@ bool lb_is_matrix_simdable(Type *t) {
 				return true;
 			case TargetArch_386:
 			case TargetArch_wasm32:
+			case TargetArch_wasm64:
 				return false;
 			}
 		}

+ 60 - 0
src/llvm_backend_opt.cpp

@@ -355,3 +355,63 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
 	// are not removed
 	lb_run_remove_dead_instruction_pass(p);
 }
+
+
+void lb_run_remove_unused_function_pass(LLVMModuleRef mod) {
+	isize removal_count = 0;
+	isize pass_count = 0;
+	isize const max_pass_count = 10;
+	// Custom remove dead function pass
+	for (; pass_count < max_pass_count; pass_count++) {
+		bool was_dead_function = false;	
+		for (LLVMValueRef func = LLVMGetFirstFunction(mod);
+		     func != nullptr;
+		     /**/
+		     ) {
+		     	LLVMValueRef curr_func = func;
+		     	func = LLVMGetNextFunction(func);
+		     	
+			LLVMUseRef first_use = LLVMGetFirstUse(curr_func);
+			if (first_use != nullptr)  {
+				continue;
+			}
+			String name = {};
+			name.text = cast(u8 *)LLVMGetValueName2(curr_func, cast(size_t *)&name.len);
+						
+			if (LLVMIsDeclaration(curr_func)) {
+				// Ignore for the time being
+				continue;
+			}
+			
+			if (name == "memset" ||
+			    name == "memmove" ||
+			    name == "memcpy") {
+				continue;
+			}
+			if (is_arch_wasm()) {
+				if (name == "__ashlti3") {
+					LLVMSetLinkage(curr_func, LLVMExternalLinkage);
+					continue;
+				}
+			}
+			
+			LLVMLinkage linkage = LLVMGetLinkage(curr_func);
+						
+			switch (linkage) {
+			case LLVMExternalLinkage:
+			case LLVMDLLImportLinkage:
+			case LLVMDLLExportLinkage:
+			default:
+				continue;
+			case LLVMInternalLinkage:
+				break;
+			}
+			LLVMDeleteFunction(curr_func);
+			was_dead_function = true;
+			removal_count += 1;
+		}
+		if (!was_dead_function) {
+			break;
+		}
+	}
+}

+ 10 - 3
src/llvm_backend_proc.cpp

@@ -195,13 +195,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 			// then it is very likely it is required by LLVM and thus cannot have internal linkage
 			if (entity->pkg != nullptr && entity->pkg->kind == Package_Runtime && p->body != nullptr) {
 				GB_ASSERT(entity->kind == Entity_Procedure);
-				if (entity->Procedure.link_name != "") {
-					LLVMSetLinkage(p->value, LLVMExternalLinkage);
+				String link_name = entity->Procedure.link_name;
+				if (entity->flags & EntityFlag_CustomLinkName && 
+				    link_name != "") {
+					if (string_starts_with(link_name, str_lit("__"))) {
+						LLVMSetLinkage(p->value, LLVMExternalLinkage);
+					} else {
+						LLVMSetLinkage(p->value, LLVMInternalLinkage);
+					}
 				}
 			}
 		}
 	}
-
+	
 	if (p->is_foreign) {
 		if (is_arch_wasm()) {
 			char const *import_name = alloc_cstring(permanent_allocator(), p->name);
@@ -217,6 +223,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 			LLVMAddTargetDependentFunctionAttr(p->value, "wasm-import-module", module_name);
 		}
 	}
+	
 
 	// NOTE(bill): offset==0 is the return value
 	isize offset = 1;

+ 1 - 0
src/llvm_backend_utility.cpp

@@ -1504,6 +1504,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
 			break;
 		case TargetArch_386:
 		case TargetArch_wasm32:
+		case TargetArch_wasm64:
 			is_possible = false;
 			break;
 		}

+ 3 - 4
src/main.cpp

@@ -135,13 +135,12 @@ i32 linker_stage(lbGenerator *gen) {
 
 	if (is_arch_wasm()) {
 		timings_start_section(timings, str_lit("wasm-ld"));
+
 		result = system_exec_command_line_app("wasm-ld",
-			"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm-obj\" -o \"%.*s.wasm\" %.*s %.*s",
+			"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
 			LIT(build_context.ODIN_ROOT),
 			LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
-		if (result) {
-			return result;
-		}
+		return result;
 	}
 
 	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {

+ 4 - 3
src/string.cpp

@@ -21,13 +21,14 @@ struct String {
 };
 // NOTE(bill): used for printf style arguments
 #define LIT(x) ((int)(x).len), (x).text
-#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1}
 #if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
-	#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
+	#define STR_LIT(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
 #else
-	#define str_lit(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1}
+	#define STR_LIT(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1}
 #endif
 
+#define str_lit(c_str) STR_LIT(c_str)
+
 // NOTE(bill): String16 is only used for Windows due to its file directories
 struct String16 {
 	wchar_t *text;