mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-06 21:17:40 +00:00
Merge pull request #1255 from odin-lang/wasi-wasm
`wasi_wasm32` support
This commit is contained in:
@@ -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
core/fmt/fmt_os.odin
Normal file
37
core/fmt/fmt_os.odin
Normal file
@@ -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) }
|
||||
@@ -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
core/os/os_wasi.odin
Normal file
97
core/os/os_wasi.odin
Normal file
@@ -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))
|
||||
}
|
||||
@@ -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,5 +1,6 @@
|
||||
//+build !windows
|
||||
//+build !freestanding
|
||||
//+build !wasi
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
|
||||
32
core/runtime/default_allocators_wasi.odin
Normal file
32
core/runtime/default_allocators_wasi.odin
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return data, .None
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||
if old_memory == nil {
|
||||
return .None
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start)
|
||||
|
||||
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
|
||||
return .None
|
||||
s.backup_allocator = backup_allocator
|
||||
s.leaked_allocations.allocator = backup_allocator
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return .None
|
||||
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^ = {}
|
||||
}
|
||||
|
||||
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
|
||||
@(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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// 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 .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)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if s.data == nil {
|
||||
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
|
||||
return data, .None
|
||||
}
|
||||
|
||||
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 .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}
|
||||
@(private)
|
||||
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||
if old_memory == nil {
|
||||
return .None
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Nothing to give
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start)
|
||||
s.prev_allocation = nil
|
||||
return .None
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Invalid_Pointer
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
}
|
||||
|
||||
return
|
||||
@(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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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 .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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,5 +1,4 @@
|
||||
//+build !freestanding
|
||||
//+build !windows
|
||||
//+build !freestanding !wasi !windows
|
||||
package runtime
|
||||
|
||||
import "core:os"
|
||||
|
||||
10
core/runtime/os_specific_wasi.odin
Normal file
10
core/runtime/os_specific_wasi.odin
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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
core/runtime/procs_wasm32.odin
Normal file
7
core/runtime/procs_wasm32.odin
Normal file
@@ -0,0 +1,7 @@
|
||||
//+build wasm32
|
||||
package runtime
|
||||
|
||||
@(link_name="__ashlti3")
|
||||
__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
|
||||
return a
|
||||
}
|
||||
1899
core/sys/wasm/wasi/wasi_api.odin
Normal file
1899
core/sys/wasm/wasi/wasi_api.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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(¶ms->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)) {
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user