mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 22:58:46 +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:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:runtime"
|
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
|
// aprint* procedures return a string that was allocated with the current context
|
||||||
// They must be freed accordingly
|
// They must be freed accordingly
|
||||||
aprint :: proc(args: ..any, sep := " ") -> string {
|
aprint :: proc(args: ..any, sep := " ") -> string {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
@@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
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))
|
fd := Handle(uintptr(s.stream_data))
|
||||||
os_err: Errno
|
os_err: Errno
|
||||||
n, os_err = read_at(fd, p, offset)
|
n, os_err = read_at(fd, p, offset)
|
||||||
@@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
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))
|
fd := Handle(uintptr(s.stream_data))
|
||||||
os_err: Errno
|
os_err: Errno
|
||||||
n, os_err = write_at(fd, p, offset)
|
n, os_err = write_at(fd, p, offset)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//+build !windows
|
//+build !windows
|
||||||
//+build !freestanding
|
//+build !freestanding
|
||||||
|
//+build !wasi
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||||
|
|||||||
@@ -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
|
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_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
|
||||||
|
|
||||||
|
|
||||||
Default_Temp_Allocator :: struct {
|
when ODIN_OS == "freestanding" {
|
||||||
data: []byte,
|
Default_Temp_Allocator :: struct {
|
||||||
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
|
|
||||||
}
|
}
|
||||||
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)
|
default_temp_allocator_destroy :: proc(s: ^Default_Temp_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 {
|
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
a = context.allocator
|
size, alignment: int,
|
||||||
s.backup_allocator = a
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
data, err := mem_alloc_bytes(size, alignment, a, loc)
|
Default_Temp_Allocator :: struct {
|
||||||
if err != nil {
|
data: []byte,
|
||||||
return data, err
|
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)
|
||||||
append(&s.leaked_allocations, data)
|
s.curr_offset = 0
|
||||||
|
|
||||||
// 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)
|
|
||||||
s.prev_allocation = nil
|
s.prev_allocation = nil
|
||||||
return .None
|
s.backup_allocator = backup_allocator
|
||||||
|
s.leaked_allocations.allocator = backup_allocator
|
||||||
}
|
}
|
||||||
|
|
||||||
if start <= old_ptr && old_ptr < end {
|
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||||
// NOTE(bill): Cannot free this pointer but it is valid
|
if s == nil {
|
||||||
return .None
|
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 {
|
@(private)
|
||||||
for data, i in s.leaked_allocations {
|
default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
ptr := raw_data(data)
|
size := size
|
||||||
if ptr == old_memory {
|
size = align_forward_int(size, alignment)
|
||||||
free(ptr, s.backup_allocator)
|
|
||||||
ordered_remove(&s.leaked_allocations, i)
|
switch {
|
||||||
return .None
|
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)
|
return data, .None
|
||||||
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 {
|
@(private)
|
||||||
case .Alloc:
|
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
|
if old_memory == nil {
|
||||||
case .Free:
|
return .None
|
||||||
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:
|
start := uintptr(raw_data(s.data))
|
||||||
// Nothing to give
|
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 {
|
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,
|
procedure = default_temp_allocator_proc,
|
||||||
data = allocator,
|
data = allocator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,11 @@ package runtime
|
|||||||
|
|
||||||
import "core:intrinsics"
|
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 {
|
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
||||||
return x>>8 | x<<8
|
return x>>8 | x<<8
|
||||||
}
|
}
|
||||||
@@ -833,3 +838,92 @@ fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
|
|||||||
x := i64(a)
|
x := i64(a)
|
||||||
return i128(x)
|
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
|
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
|
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 !freestanding !wasi !windows
|
||||||
//+build !windows
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
|||||||
@@ -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
@@ -1,12 +1,39 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
|
||||||
if ptr != nil && len != 0 {
|
@(link_name="memset")
|
||||||
b := byte(val)
|
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||||
p := ([^]byte)(ptr)
|
if ptr != nil && len != 0 {
|
||||||
for i in 0..<len {
|
b := byte(val)
|
||||||
p[i] = b
|
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
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//+build wasm32
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
@(link_name="__ashlti3")
|
||||||
|
__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+58
-6
@@ -16,6 +16,8 @@ enum TargetOsKind {
|
|||||||
TargetOs_linux,
|
TargetOs_linux,
|
||||||
TargetOs_essence,
|
TargetOs_essence,
|
||||||
TargetOs_freebsd,
|
TargetOs_freebsd,
|
||||||
|
|
||||||
|
TargetOs_wasi,
|
||||||
|
|
||||||
TargetOs_freestanding,
|
TargetOs_freestanding,
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ enum TargetArchKind {
|
|||||||
TargetArch_386,
|
TargetArch_386,
|
||||||
TargetArch_arm64,
|
TargetArch_arm64,
|
||||||
TargetArch_wasm32,
|
TargetArch_wasm32,
|
||||||
|
TargetArch_wasm64,
|
||||||
|
|
||||||
TargetArch_COUNT,
|
TargetArch_COUNT,
|
||||||
};
|
};
|
||||||
@@ -49,6 +52,8 @@ String target_os_names[TargetOs_COUNT] = {
|
|||||||
str_lit("linux"),
|
str_lit("linux"),
|
||||||
str_lit("essence"),
|
str_lit("essence"),
|
||||||
str_lit("freebsd"),
|
str_lit("freebsd"),
|
||||||
|
|
||||||
|
str_lit("wasi"),
|
||||||
|
|
||||||
str_lit("freestanding"),
|
str_lit("freestanding"),
|
||||||
};
|
};
|
||||||
@@ -59,6 +64,7 @@ String target_arch_names[TargetArch_COUNT] = {
|
|||||||
str_lit("386"),
|
str_lit("386"),
|
||||||
str_lit("arm64"),
|
str_lit("arm64"),
|
||||||
str_lit("wasm32"),
|
str_lit("wasm32"),
|
||||||
|
str_lit("wasm64"),
|
||||||
};
|
};
|
||||||
|
|
||||||
String target_endian_names[TargetEndian_COUNT] = {
|
String target_endian_names[TargetEndian_COUNT] = {
|
||||||
@@ -72,6 +78,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
|
|||||||
TargetEndian_Little,
|
TargetEndian_Little,
|
||||||
TargetEndian_Little,
|
TargetEndian_Little,
|
||||||
TargetEndian_Little,
|
TargetEndian_Little,
|
||||||
|
TargetEndian_Little,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef ODIN_VERSION_RAW
|
#ifndef ODIN_VERSION_RAW
|
||||||
@@ -335,6 +342,26 @@ gb_global TargetMetrics target_freestanding_wasm32 = {
|
|||||||
str_lit(""),
|
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 {
|
struct NamedTargetMetrics {
|
||||||
@@ -353,6 +380,8 @@ gb_global NamedTargetMetrics named_targets[] = {
|
|||||||
{ str_lit("freebsd_386"), &target_freebsd_386 },
|
{ str_lit("freebsd_386"), &target_freebsd_386 },
|
||||||
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
||||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
{ 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;
|
NamedTargetMetrics *selected_target_metrics;
|
||||||
@@ -458,11 +487,21 @@ bool find_library_collection_path(String name, String *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_arch_wasm(void) {
|
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) {
|
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 ");
|
bc->link_flags = str_lit("-arch arm64 ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (is_arch_wasm()) {
|
||||||
} else if (bc->metrics.arch == TargetArch_wasm32) {
|
gbString link_flags = gb_string_make(heap_allocator(), " ");
|
||||||
bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined ");
|
// 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 {
|
} else {
|
||||||
gb_printf_err("Compiler Error: Unsupported architecture\n");;
|
gb_printf_err("Compiler Error: Unsupported architecture\n");
|
||||||
gb_exit(1);
|
gb_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -899,6 +899,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
mutex_unlock(&ctx->info->foreign_mutex);
|
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) {
|
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);
|
string_map_set(fp, key, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e->Variable.link_name.len > 0) {
|
||||||
|
e->flags |= EntityFlag_CustomLinkName;
|
||||||
|
}
|
||||||
|
|
||||||
if (init_expr == nullptr) {
|
if (init_expr == nullptr) {
|
||||||
if (type_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_h2f_ieee"),
|
||||||
str_lit("gnu_f2h_ieee"),
|
str_lit("gnu_f2h_ieee"),
|
||||||
str_lit("extendhfsf2"),
|
str_lit("extendhfsf2"),
|
||||||
|
|
||||||
|
// WASM Specific
|
||||||
|
str_lit("__ashlti3"),
|
||||||
};
|
};
|
||||||
for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
|
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]);
|
force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
|
||||||
|
|||||||
+2
-1
@@ -74,9 +74,10 @@ enum EntityFlag : u64 {
|
|||||||
|
|
||||||
EntityFlag_Test = 1ull<<30,
|
EntityFlag_Test = 1ull<<30,
|
||||||
EntityFlag_Init = 1ull<<31,
|
EntityFlag_Init = 1ull<<31,
|
||||||
|
|
||||||
|
EntityFlag_CustomLinkName = 1ull<<40,
|
||||||
|
|
||||||
EntityFlag_Overridden = 1ull<<63,
|
EntityFlag_Overridden = 1ull<<63,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum EntityState : u32 {
|
enum EntityState : u32 {
|
||||||
|
|||||||
+82
-5
@@ -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) {
|
LB_ABI_INFO(lb_get_abi_info) {
|
||||||
switch (calling_convention) {
|
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) {
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
} else {
|
} else {
|
||||||
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
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);
|
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);
|
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
||||||
} else if (build_context.metrics.arch == TargetArch_wasm32) {
|
case TargetArch_wasm32:
|
||||||
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
|
// 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");
|
GB_PANIC("Unsupported ABI");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-7
@@ -771,6 +771,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
|
|||||||
Type *results = alloc_type_tuple();
|
Type *results = alloc_type_tuple();
|
||||||
|
|
||||||
Type *t_ptr_cstring = alloc_type_pointer(t_cstring);
|
Type *t_ptr_cstring = alloc_type_pointer(t_cstring);
|
||||||
|
|
||||||
|
bool call_cleanup = true;
|
||||||
|
|
||||||
bool has_args = false;
|
bool has_args = false;
|
||||||
bool is_dll_main = 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[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[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);
|
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) {
|
} else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) {
|
||||||
name = str_lit("mainCRTStartup");
|
name = str_lit("mainCRTStartup");
|
||||||
|
} else if (is_arch_wasm()) {
|
||||||
|
name = str_lit("_start");
|
||||||
|
call_cleanup = false;
|
||||||
} else {
|
} else {
|
||||||
has_args = true;
|
has_args = true;
|
||||||
slice_init(¶ms->Tuple.variables, permanent_allocator(), 2);
|
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"));
|
if (call_cleanup) {
|
||||||
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, false);
|
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) {
|
if (is_dll_main) {
|
||||||
@@ -885,6 +893,19 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lb_end_procedure_body(p);
|
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)) {
|
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
|
||||||
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
|
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) {
|
WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
||||||
GB_ASSERT(MULTITHREAD_OBJECT_GENERATION);
|
|
||||||
|
|
||||||
auto wd = cast(lbLLVMModulePassWorkerData *)data;
|
auto wd = cast(lbLLVMModulePassWorkerData *)data;
|
||||||
|
|
||||||
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
|
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
|
||||||
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
|
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
|
||||||
LLVMRunPassManager(module_pass_manager, wd->m->mod);
|
LLVMRunPassManager(module_pass_manager, wd->m->mod);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1148,6 +1165,7 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
LLVMInitializeAArch64Disassembler();
|
LLVMInitializeAArch64Disassembler();
|
||||||
break;
|
break;
|
||||||
case TargetArch_wasm32:
|
case TargetArch_wasm32:
|
||||||
|
case TargetArch_wasm64:
|
||||||
LLVMInitializeWebAssemblyTargetInfo();
|
LLVMInitializeWebAssemblyTargetInfo();
|
||||||
LLVMInitializeWebAssemblyTarget();
|
LLVMInitializeWebAssemblyTarget();
|
||||||
LLVMInitializeWebAssemblyTargetMC();
|
LLVMInitializeWebAssemblyTargetMC();
|
||||||
@@ -1660,6 +1678,8 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
|
|
||||||
for_array(i, gen->modules.entries) {
|
for_array(i, gen->modules.entries) {
|
||||||
lbModule *m = gen->modules.entries[i].value;
|
lbModule *m = gen->modules.entries[i].value;
|
||||||
|
|
||||||
|
lb_run_remove_unused_function_pass(m->mod);
|
||||||
|
|
||||||
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
|
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
|
||||||
wd->m = m;
|
wd->m = m;
|
||||||
@@ -1737,8 +1757,16 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TIME_SECTION("LLVM Object Generation");
|
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) {
|
for_array(j, gen->modules.entries) {
|
||||||
lbModule *m = gen->modules.entries[j].value;
|
lbModule *m = gen->modules.entries[j].value;
|
||||||
if (lb_is_module_empty(m)) {
|
if (lb_is_module_empty(m)) {
|
||||||
|
|||||||
@@ -585,3 +585,24 @@ enum : LLVMAttributeIndex {
|
|||||||
LLVMAttributeIndex_FunctionIndex = ~0u,
|
LLVMAttributeIndex_FunctionIndex = ~0u,
|
||||||
LLVMAttributeIndex_FirstArgIndex = 1,
|
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;
|
break;
|
||||||
case TargetArch_386:
|
case TargetArch_386:
|
||||||
case TargetArch_wasm32:
|
case TargetArch_wasm32:
|
||||||
|
case TargetArch_wasm64:
|
||||||
// nope
|
// nope
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -513,6 +514,7 @@ bool lb_is_matrix_simdable(Type *t) {
|
|||||||
return true;
|
return true;
|
||||||
case TargetArch_386:
|
case TargetArch_386:
|
||||||
case TargetArch_wasm32:
|
case TargetArch_wasm32:
|
||||||
|
case TargetArch_wasm64:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,3 +355,63 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
|
|||||||
// are not removed
|
// are not removed
|
||||||
lb_run_remove_dead_instruction_pass(p);
|
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
|
// 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) {
|
if (entity->pkg != nullptr && entity->pkg->kind == Package_Runtime && p->body != nullptr) {
|
||||||
GB_ASSERT(entity->kind == Entity_Procedure);
|
GB_ASSERT(entity->kind == Entity_Procedure);
|
||||||
if (entity->Procedure.link_name != "") {
|
String link_name = entity->Procedure.link_name;
|
||||||
LLVMSetLinkage(p->value, LLVMExternalLinkage);
|
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 (p->is_foreign) {
|
||||||
if (is_arch_wasm()) {
|
if (is_arch_wasm()) {
|
||||||
char const *import_name = alloc_cstring(permanent_allocator(), p->name);
|
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);
|
LLVMAddTargetDependentFunctionAttr(p->value, "wasm-import-module", module_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): offset==0 is the return value
|
// NOTE(bill): offset==0 is the return value
|
||||||
isize offset = 1;
|
isize offset = 1;
|
||||||
|
|||||||
@@ -1504,6 +1504,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
|
|||||||
break;
|
break;
|
||||||
case TargetArch_386:
|
case TargetArch_386:
|
||||||
case TargetArch_wasm32:
|
case TargetArch_wasm32:
|
||||||
|
case TargetArch_wasm64:
|
||||||
is_possible = false;
|
is_possible = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -135,13 +135,12 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
|
|
||||||
if (is_arch_wasm()) {
|
if (is_arch_wasm()) {
|
||||||
timings_start_section(timings, str_lit("wasm-ld"));
|
timings_start_section(timings, str_lit("wasm-ld"));
|
||||||
|
|
||||||
result = system_exec_command_line_app("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(build_context.ODIN_ROOT),
|
||||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
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) {
|
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
||||||
|
|||||||
+4
-3
@@ -21,13 +21,14 @@ struct String {
|
|||||||
};
|
};
|
||||||
// NOTE(bill): used for printf style arguments
|
// NOTE(bill): used for printf style arguments
|
||||||
#define LIT(x) ((int)(x).len), (x).text
|
#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
|
#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
|
#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
|
#endif
|
||||||
|
|
||||||
|
#define str_lit(c_str) STR_LIT(c_str)
|
||||||
|
|
||||||
// NOTE(bill): String16 is only used for Windows due to its file directories
|
// NOTE(bill): String16 is only used for Windows due to its file directories
|
||||||
struct String16 {
|
struct String16 {
|
||||||
wchar_t *text;
|
wchar_t *text;
|
||||||
|
|||||||
Reference in New Issue
Block a user