mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-25 06:21:35 +00:00
Merge branch 'master' into windows-llvm-13.0.0
This commit is contained in:
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -13,6 +13,9 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
timeout-minutes: 10
|
||||
@@ -39,6 +42,9 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
timeout-minutes: 10
|
||||
@@ -57,6 +63,9 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
5
Makefile
5
Makefile
@@ -23,6 +23,8 @@ ifeq ($(OS), Linux)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
@@ -40,6 +42,9 @@ all: debug demo
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
|
||||
report:
|
||||
./odin report
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package fmt
|
||||
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:reflect"
|
||||
import "core:runtime"
|
||||
@@ -62,38 +61,6 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
|
||||
}
|
||||
|
||||
|
||||
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint(w=w, args=args, sep=sep)
|
||||
}
|
||||
|
||||
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
}
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_type(w, info)
|
||||
}
|
||||
fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_typeid(w, id)
|
||||
}
|
||||
|
||||
// print* procedures return the number of bytes written
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
|
||||
// aprint* procedures return a string that was allocated with the current context
|
||||
// They must be freed accordingly
|
||||
aprint :: proc(args: ..any, sep := " ") -> string {
|
||||
|
||||
37
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) }
|
||||
@@ -39,6 +39,7 @@ sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
||||
|
||||
|
||||
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
|
||||
@@ -10,6 +10,15 @@ zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
intrinsics.mem_zero(data, len)
|
||||
return data
|
||||
}
|
||||
zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
// This routine tries to avoid the compiler optimizing away the call,
|
||||
// so that it is always executed. It is intended to provided
|
||||
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
|
||||
// memset_s call.
|
||||
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
|
||||
intrinsics.atomic_fence() // Prevent reordering
|
||||
return data
|
||||
}
|
||||
zero_item :: proc "contextless" (item: $P/^$T) {
|
||||
intrinsics.mem_zero(item, size_of(T))
|
||||
}
|
||||
|
||||
@@ -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
641
src/bug_report.cpp
Normal file
641
src/bug_report.cpp
Normal file
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
Gather and print platform and version info to help with reporting Odin bugs.
|
||||
*/
|
||||
|
||||
#if !defined(GB_COMPILER_MSVC)
|
||||
#if defined(GB_CPU_X86)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_LINUX)
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
|
||||
*/
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
void report_windows_product_type(DWORD ProductType) {
|
||||
switch (ProductType) {
|
||||
case PRODUCT_ULTIMATE:
|
||||
gb_printf("Ultimate");
|
||||
break;
|
||||
|
||||
case PRODUCT_HOME_BASIC:
|
||||
gb_printf("Home Basic");
|
||||
break;
|
||||
|
||||
case PRODUCT_HOME_PREMIUM:
|
||||
gb_printf("Home Premium");
|
||||
break;
|
||||
|
||||
case PRODUCT_ENTERPRISE:
|
||||
gb_printf("Enterprise");
|
||||
break;
|
||||
|
||||
case PRODUCT_HOME_BASIC_N:
|
||||
gb_printf("Home Basic N");
|
||||
break;
|
||||
|
||||
case PRODUCT_BUSINESS:
|
||||
gb_printf("Business");
|
||||
break;
|
||||
|
||||
case PRODUCT_STANDARD_SERVER:
|
||||
gb_printf("Standard Server");
|
||||
break;
|
||||
|
||||
case PRODUCT_DATACENTER_SERVER:
|
||||
gb_printf("Datacenter");
|
||||
break;
|
||||
|
||||
case PRODUCT_SMALLBUSINESS_SERVER:
|
||||
gb_printf("Windows Small Business Server");
|
||||
break;
|
||||
|
||||
case PRODUCT_ENTERPRISE_SERVER:
|
||||
gb_printf("Enterprise Server");
|
||||
break;
|
||||
|
||||
case PRODUCT_STARTER:
|
||||
gb_printf("Starter");
|
||||
break;
|
||||
|
||||
case PRODUCT_DATACENTER_SERVER_CORE:
|
||||
gb_printf("Datacenter Server Core");
|
||||
break;
|
||||
|
||||
case PRODUCT_STANDARD_SERVER_CORE:
|
||||
gb_printf("Server Standard Core");
|
||||
break;
|
||||
|
||||
case PRODUCT_ENTERPRISE_SERVER_CORE:
|
||||
gb_printf("Enterprise Server Core");
|
||||
break;
|
||||
|
||||
case PRODUCT_BUSINESS_N:
|
||||
gb_printf("Business N");
|
||||
break;
|
||||
|
||||
case PRODUCT_HOME_SERVER:
|
||||
gb_printf("Home Server");
|
||||
break;
|
||||
|
||||
case PRODUCT_SERVER_FOR_SMALLBUSINESS:
|
||||
gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
|
||||
break;
|
||||
|
||||
case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
|
||||
gb_printf("Small Business Server Premium");
|
||||
break;
|
||||
|
||||
case PRODUCT_HOME_PREMIUM_N:
|
||||
gb_printf("Home Premium N");
|
||||
break;
|
||||
|
||||
case PRODUCT_ENTERPRISE_N:
|
||||
gb_printf("Enterprise N");
|
||||
break;
|
||||
|
||||
case PRODUCT_ULTIMATE_N:
|
||||
gb_printf("Ultimate N");
|
||||
break;
|
||||
|
||||
case PRODUCT_HYPERV:
|
||||
gb_printf("HyperV");
|
||||
break;
|
||||
|
||||
case PRODUCT_STARTER_N:
|
||||
gb_printf("Starter N");
|
||||
break;
|
||||
|
||||
case PRODUCT_PROFESSIONAL:
|
||||
gb_printf("Professional");
|
||||
break;
|
||||
|
||||
case PRODUCT_PROFESSIONAL_N:
|
||||
gb_printf("Professional N");
|
||||
break;
|
||||
|
||||
case PRODUCT_UNLICENSED:
|
||||
gb_printf("Unlicensed");
|
||||
break;
|
||||
|
||||
default:
|
||||
gb_printf("Unknown Edition (%08x)", ProductType);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void odin_cpuid(int leaf, int result[]) {
|
||||
#if defined(GB_COMPILER_MSVC)
|
||||
__cpuid(result, leaf);
|
||||
#else
|
||||
__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void report_cpu_info() {
|
||||
gb_printf("\tCPU: ");
|
||||
|
||||
#if defined(GB_CPU_X86)
|
||||
|
||||
/*
|
||||
Get extended leaf info
|
||||
*/
|
||||
int cpu[4];
|
||||
|
||||
odin_cpuid(0x80000000, &cpu[0]);
|
||||
int number_of_extended_ids = cpu[0];
|
||||
|
||||
int brand[0x12] = {};
|
||||
|
||||
/*
|
||||
Read CPU brand if supported.
|
||||
*/
|
||||
if (number_of_extended_ids >= 0x80000004) {
|
||||
odin_cpuid(0x80000002, &brand[0]);
|
||||
odin_cpuid(0x80000003, &brand[4]);
|
||||
odin_cpuid(0x80000004, &brand[8]);
|
||||
|
||||
/*
|
||||
Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
|
||||
*/
|
||||
char * brand_name = (char *)&brand[0];
|
||||
for (; brand_name[0] == ' '; brand_name++) {}
|
||||
|
||||
gb_printf("%s\n", brand_name);
|
||||
} else {
|
||||
gb_printf("Unable to retrieve.\n");
|
||||
}
|
||||
|
||||
#elif defined(GB_CPU_ARM)
|
||||
/*
|
||||
TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
|
||||
*/
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
gb_printf("ARM64\n");
|
||||
#else
|
||||
gb_printf("ARM\n");
|
||||
#endif
|
||||
#else
|
||||
gb_printf("Unknown\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Report the amount of installed RAM.
|
||||
*/
|
||||
void report_ram_info() {
|
||||
|
||||
gb_printf("\tRAM: ");
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
GlobalMemoryStatusEx (&statex);
|
||||
|
||||
gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
|
||||
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
/*
|
||||
Retrieve RAM info using `sysinfo()`,
|
||||
*/
|
||||
struct sysinfo info;
|
||||
int result = sysinfo(&info);
|
||||
|
||||
if (result == 0x0) {
|
||||
gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
|
||||
} else {
|
||||
gb_printf("Unknown.\n");
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
uint64_t ram_amount;
|
||||
size_t val_size = sizeof(ram_amount);
|
||||
|
||||
int sysctls[] = { CTL_HW, HW_MEMSIZE };
|
||||
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
||||
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
||||
}
|
||||
#else
|
||||
gb_printf("Unknown.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
|
||||
*/
|
||||
void print_bug_report_help() {
|
||||
gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
|
||||
gb_printf("\tWebsite: https://odin-lang.org\n");
|
||||
gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n");
|
||||
/*
|
||||
Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
|
||||
gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
|
||||
*/
|
||||
gb_printf("\n\n");
|
||||
|
||||
gb_printf("Useful information to add to a bug report:\n\n");
|
||||
|
||||
gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
|
||||
|
||||
#ifdef NIGHTLY
|
||||
gb_printf("-nightly");
|
||||
#endif
|
||||
|
||||
#ifdef GIT_SHA
|
||||
gb_printf(":%s", GIT_SHA);
|
||||
#endif
|
||||
|
||||
gb_printf("\n");
|
||||
|
||||
/*
|
||||
Print OS information.
|
||||
*/
|
||||
gb_printf("\tOS: ");
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
/*
|
||||
NOTE(Jeroen):
|
||||
`GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
|
||||
`RtlGetVersion` will return the true version.
|
||||
|
||||
Rather than include the WinDDK, we ask the kernel directly.
|
||||
|
||||
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
|
||||
|
||||
*/
|
||||
OSVERSIONINFOEXW osvi;
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
|
||||
|
||||
typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
|
||||
typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
|
||||
RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
|
||||
GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
|
||||
|
||||
NTSTATUS status = {};
|
||||
DWORD ProductType = {};
|
||||
if (RtlGetVersion != nullptr) {
|
||||
status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
|
||||
}
|
||||
|
||||
if (RtlGetVersion == nullptr || status != 0x0) {
|
||||
gb_printf("Windows (Unknown Version)");
|
||||
} else {
|
||||
if (GetProductInfo != nullptr) {
|
||||
GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion);
|
||||
gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion);
|
||||
gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber);
|
||||
gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId);
|
||||
gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor);
|
||||
gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor);
|
||||
gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask);
|
||||
gb_printf("wProductType: %d\n", osvi.wProductType);
|
||||
}
|
||||
|
||||
gb_printf("Windows ");
|
||||
|
||||
switch (osvi.dwMajorVersion) {
|
||||
case 10:
|
||||
/*
|
||||
Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
|
||||
*/
|
||||
switch (osvi.wProductType) {
|
||||
case VER_NT_WORKSTATION: // Workstation
|
||||
if (osvi.dwBuildNumber < 22000) {
|
||||
gb_printf("10 ");
|
||||
} else {
|
||||
gb_printf("11 ");
|
||||
}
|
||||
|
||||
report_windows_product_type(ProductType);
|
||||
|
||||
break;
|
||||
default: // Server or Domain Controller
|
||||
switch(osvi.dwBuildNumber) {
|
||||
case 14393:
|
||||
gb_printf("2016 Server");
|
||||
break;
|
||||
case 17763:
|
||||
gb_printf("2019 Server");
|
||||
break;
|
||||
case 20348:
|
||||
gb_printf("2022 Server");
|
||||
break;
|
||||
default:
|
||||
gb_printf("Unknown Server");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (osvi.dwMinorVersion) {
|
||||
case 0:
|
||||
switch (osvi.wProductType) {
|
||||
case VER_NT_WORKSTATION:
|
||||
gb_printf("Windows Vista ");
|
||||
report_windows_product_type(ProductType);
|
||||
break;
|
||||
case 3:
|
||||
gb_printf("Windows Server 2008");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
switch (osvi.wProductType) {
|
||||
case VER_NT_WORKSTATION:
|
||||
gb_printf("Windows 7 ");
|
||||
report_windows_product_type(ProductType);
|
||||
break;
|
||||
case 3:
|
||||
gb_printf("Windows Server 2008 R2");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (osvi.wProductType) {
|
||||
case VER_NT_WORKSTATION:
|
||||
gb_printf("Windows 8 ");
|
||||
report_windows_product_type(ProductType);
|
||||
break;
|
||||
case 3:
|
||||
gb_printf("Windows Server 2012");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (osvi.wProductType) {
|
||||
case VER_NT_WORKSTATION:
|
||||
gb_printf("Windows 8.1 ");
|
||||
report_windows_product_type(ProductType);
|
||||
break;
|
||||
case 3:
|
||||
gb_printf("Windows Server 2012 R2");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch (osvi.dwMinorVersion) {
|
||||
case 0:
|
||||
gb_printf("Windows 2000");
|
||||
break;
|
||||
case 1:
|
||||
gb_printf("Windows XP");
|
||||
break;
|
||||
case 2:
|
||||
gb_printf("Windows Server 2003");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Grab Windows DisplayVersion (like 20H02)
|
||||
*/
|
||||
LPDWORD ValueType = {};
|
||||
DWORD UBR;
|
||||
char DisplayVersion[256];
|
||||
DWORD ValueSize = 256;
|
||||
|
||||
status = RegGetValue(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
TEXT("DisplayVersion"),
|
||||
RRF_RT_REG_SZ,
|
||||
ValueType,
|
||||
&DisplayVersion,
|
||||
&ValueSize
|
||||
);
|
||||
|
||||
if (status == 0x0) {
|
||||
gb_printf(" (version: %s)", &DisplayVersion);
|
||||
}
|
||||
|
||||
/*
|
||||
Now print build number.
|
||||
*/
|
||||
gb_printf(", build %d", osvi.dwBuildNumber);
|
||||
|
||||
ValueSize = sizeof(UBR);
|
||||
status = RegGetValue(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
TEXT("UBR"),
|
||||
RRF_RT_REG_DWORD,
|
||||
ValueType,
|
||||
&UBR,
|
||||
&ValueSize
|
||||
);
|
||||
|
||||
if (status == 0x0) {
|
||||
gb_printf(".%d", UBR);
|
||||
}
|
||||
gb_printf("\n");
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
/*
|
||||
Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
||||
*/
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
|
||||
defer (gb_file_free_contents(&release));
|
||||
|
||||
b32 found = 0;
|
||||
if (release.size) {
|
||||
char *start = (char *)release.data;
|
||||
char *end = (char *)release.data + release.size;
|
||||
const char *needle = "PRETTY_NAME=\"";
|
||||
isize needle_len = gb_strlen((needle));
|
||||
|
||||
char *c = start;
|
||||
for (; c < end; c++) {
|
||||
if (gb_strncmp(c, needle, needle_len) == 0) {
|
||||
found = 1;
|
||||
start = c + needle_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
for (c = start; c < end; c++) {
|
||||
if (*c == '"') {
|
||||
// Found the closing quote. Replace it with \0
|
||||
*c = 0;
|
||||
gb_printf("%s", (char *)start);
|
||||
break;
|
||||
} else if (*c == '\n') {
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
gb_printf("Unknown Linux Distro");
|
||||
}
|
||||
|
||||
/*
|
||||
Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
|
||||
*/
|
||||
char buffer[1024];
|
||||
uname((struct utsname *)&buffer[0]);
|
||||
|
||||
struct utsname *info;
|
||||
info = (struct utsname *)&buffer[0];
|
||||
|
||||
gb_printf(", %s %s\n", info->sysname, info->release);
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
b32 found = 1;
|
||||
uint32_t major, minor;
|
||||
|
||||
#define osx_version_buffer 100
|
||||
char buffer[osx_version_buffer];
|
||||
size_t buffer_size = osx_version_buffer - 1;
|
||||
#undef osx_version_buffer
|
||||
|
||||
int sysctls[] = { CTL_KERN, KERN_OSRELEASE };
|
||||
if (sysctl(sysctls, 2, buffer, &buffer_size, NULL, 0) == -1) {
|
||||
found = 0;
|
||||
} else {
|
||||
if (sscanf(buffer, "%u.%u", &major, &minor) != 2) {
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
switch (major) {
|
||||
case 21:
|
||||
/*
|
||||
macOS Big Sur and newer
|
||||
*/
|
||||
major -= 9;
|
||||
|
||||
gb_printf("macOS 12.0.%d Monterey\n", minor);
|
||||
|
||||
break;
|
||||
case 20:
|
||||
/*
|
||||
macOS Big Sur and newer
|
||||
*/
|
||||
major -= 9;
|
||||
|
||||
gb_printf("macOS 11.%d Big Sur\n", minor);
|
||||
|
||||
break;
|
||||
case 14:
|
||||
/*
|
||||
macOS 10.1.1 and newer
|
||||
*/
|
||||
major -= 4;
|
||||
|
||||
switch (minor) {
|
||||
case 1:
|
||||
gb_printf("macOS Puma 10.1\n");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gb_printf("macOS Jaguar 10.2\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gb_printf("macOS Panther 10.3\n");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
gb_printf("macOS Tiger 10.4\n");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
gb_printf("macOS Leopard 10.5\n");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
gb_printf("macOS Snow Leopard 10.6\n");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
gb_printf("macOS Lion 10.7\n");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
gb_printf("macOS Mountain Lion 10.8\n");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
gb_printf("macOS Mavericks 10.9\n");
|
||||
break;
|
||||
|
||||
case 10:
|
||||
gb_printf("macOS Yosemite 10.10\n");
|
||||
break;
|
||||
|
||||
case 11:
|
||||
gb_printf("macOS El Capitan 10.11\n");
|
||||
break;
|
||||
|
||||
case 12:
|
||||
gb_printf("macOS Sierra 10.12\n");
|
||||
break;
|
||||
|
||||
case 13:
|
||||
gb_printf("macOS High Sierra 10.13\n");
|
||||
break;
|
||||
|
||||
case 14:
|
||||
gb_printf("macOS Mojave 10.14\n");
|
||||
break;
|
||||
|
||||
case 15:
|
||||
gb_printf("macOS Catalina 10.15\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
gb_printf("macOS %d.%d\n", major, minor);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
gb_printf("macOS Unknown (kernel version %d.%d)\n", major, minor);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gb_printf("macOS: Unknown\n");
|
||||
}
|
||||
#else
|
||||
gb_printf("Unknown\n");
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
Now print CPU info.
|
||||
*/
|
||||
report_cpu_info();
|
||||
|
||||
/*
|
||||
And RAM info.
|
||||
*/
|
||||
report_ram_info();
|
||||
}
|
||||
@@ -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
|
||||
@@ -113,15 +120,16 @@ enum BuildModeKind {
|
||||
};
|
||||
|
||||
enum CommandKind : u32 {
|
||||
Command_run = 1<<0,
|
||||
Command_build = 1<<1,
|
||||
Command_check = 1<<3,
|
||||
Command_query = 1<<4,
|
||||
Command_doc = 1<<5,
|
||||
Command_version = 1<<6,
|
||||
Command_test = 1<<7,
|
||||
Command_run = 1<<0,
|
||||
Command_build = 1<<1,
|
||||
Command_check = 1<<3,
|
||||
Command_query = 1<<4,
|
||||
Command_doc = 1<<5,
|
||||
Command_version = 1<<6,
|
||||
Command_test = 1<<7,
|
||||
|
||||
Command_strip_semicolon = 1<<8,
|
||||
Command_bug_report = 1<<9,
|
||||
|
||||
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test|Command_strip_semicolon,
|
||||
Command__does_build = Command_run|Command_build|Command_test,
|
||||
@@ -335,6 +343,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 +381,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 +488,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 +909,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
operand->mode = Addressing_Value;
|
||||
} else if (name == "load") {
|
||||
if (ce->args.count != 1) {
|
||||
error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count);
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load' expects 1 argument, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -315,7 +320,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
|
||||
} else if (name == "load_or") {
|
||||
if (ce->args.count != 2) {
|
||||
error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load_or' expects 2 arguments, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2598,6 +2607,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
break;
|
||||
|
||||
case BuiltinProc_mem_zero:
|
||||
case BuiltinProc_mem_zero_volatile:
|
||||
{
|
||||
operand->mode = Addressing_NoValue;
|
||||
operand->type = t_invalid;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -921,26 +921,51 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
|
||||
}
|
||||
i64 lower = big_int_to_i64(&i);
|
||||
i64 upper = big_int_to_i64(&j);
|
||||
|
||||
|
||||
bool lower_changed = false;
|
||||
i64 bits = MAX_BITS;
|
||||
if (type->BitSet.underlying != nullptr) {
|
||||
bits = 8*type_size_of(type->BitSet.underlying);
|
||||
|
||||
if (lower > 0) {
|
||||
lower = 0;
|
||||
lower_changed = true;
|
||||
} else if (lower < 0) {
|
||||
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower);
|
||||
}
|
||||
}
|
||||
|
||||
i64 bits_required = upper-lower;
|
||||
switch (be->op.kind) {
|
||||
case Token_Ellipsis:
|
||||
case Token_RangeFull:
|
||||
bits_required += 1;
|
||||
break;
|
||||
}
|
||||
bool is_valid = true;
|
||||
|
||||
switch (be->op.kind) {
|
||||
case Token_Ellipsis:
|
||||
case Token_RangeFull:
|
||||
if (upper - lower >= bits) {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
|
||||
is_valid = false;
|
||||
}
|
||||
break;
|
||||
case Token_RangeHalf:
|
||||
if (upper - lower > bits) {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower));
|
||||
is_valid = false;
|
||||
}
|
||||
upper -= 1;
|
||||
break;
|
||||
}
|
||||
if (!is_valid) {
|
||||
if (lower_changed) {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
|
||||
} else {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
|
||||
}
|
||||
}
|
||||
|
||||
type->BitSet.elem = t;
|
||||
type->BitSet.lower = lower;
|
||||
type->BitSet.upper = upper;
|
||||
@@ -996,7 +1021,8 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
|
||||
}
|
||||
|
||||
GB_ASSERT(lower <= upper);
|
||||
|
||||
|
||||
bool lower_changed = false;
|
||||
i64 bits = MAX_BITS
|
||||
; if (bs->underlying != nullptr) {
|
||||
Type *u = check_type(c, bs->underlying);
|
||||
@@ -1008,17 +1034,31 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
|
||||
}
|
||||
type->BitSet.underlying = u;
|
||||
bits = 8*type_size_of(u);
|
||||
|
||||
if (lower > 0) {
|
||||
lower = 0;
|
||||
lower_changed = true;
|
||||
} else if (lower < 0) {
|
||||
gbString s = type_to_string(elem);
|
||||
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) of the element type '%s' when an underlying type is set", lower, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (upper - lower >= MAX_BITS) {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", MAX_BITS, (upper-lower+1));
|
||||
if (upper - lower >= bits) {
|
||||
i64 bits_required = upper-lower+1;
|
||||
if (lower_changed) {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
|
||||
} else {
|
||||
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
|
||||
}
|
||||
}
|
||||
|
||||
type->BitSet.lower = lower;
|
||||
type->BitSet.upper = upper;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -70,6 +70,7 @@ enum BuiltinProcId {
|
||||
BuiltinProc_mem_copy,
|
||||
BuiltinProc_mem_copy_non_overlapping,
|
||||
BuiltinProc_mem_zero,
|
||||
BuiltinProc_mem_zero_volatile,
|
||||
|
||||
BuiltinProc_ptr_offset,
|
||||
BuiltinProc_ptr_sub,
|
||||
@@ -322,6 +323,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("mem_zero"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("mem_zero_volatile"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("ptr_offset"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("ptr_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
@@ -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;
|
||||
@@ -1553,7 +1560,18 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
len = lb_emit_conv(p, len, t_int);
|
||||
|
||||
unsigned alignment = 1;
|
||||
lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment);
|
||||
lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment, false);
|
||||
return {};
|
||||
}
|
||||
case BuiltinProc_mem_zero_volatile:
|
||||
{
|
||||
lbValue ptr = lb_build_expr(p, ce->args[0]);
|
||||
lbValue len = lb_build_expr(p, ce->args[1]);
|
||||
ptr = lb_emit_conv(p, ptr, t_rawptr);
|
||||
len = lb_emit_conv(p, len, t_int);
|
||||
|
||||
unsigned alignment = 1;
|
||||
lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment, true);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment) {
|
||||
void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
|
||||
bool is_inlinable = false;
|
||||
|
||||
i64 const_len = 0;
|
||||
@@ -77,7 +77,7 @@ void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len
|
||||
args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
|
||||
args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
|
||||
args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
|
||||
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter
|
||||
args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
|
||||
|
||||
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
}
|
||||
@@ -93,7 +93,7 @@ void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alig
|
||||
{
|
||||
// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
|
||||
i32 sz = cast(i32)type_size_of(type);
|
||||
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment);
|
||||
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
38
src/main.cpp
38
src/main.cpp
@@ -64,6 +64,8 @@ gb_global Timings global_timings = {0};
|
||||
#include "microsoft_craziness.h"
|
||||
#endif
|
||||
|
||||
#include "bug_report.cpp"
|
||||
|
||||
|
||||
// NOTE(bill): 'name' is used in debugging and profiling modes
|
||||
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
@@ -96,8 +98,8 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
|
||||
wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||
if (CreateProcessW(nullptr, wcmd.text,
|
||||
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||
&start_info, &pi)) {
|
||||
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||
&start_info, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
||||
|
||||
@@ -135,13 +137,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) {
|
||||
@@ -212,7 +213,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
String lib = m->foreign_library_paths[i];
|
||||
GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
|
||||
gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" \"%.*s\"", LIT(lib));
|
||||
" \"%.*s\"", LIT(lib));
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
}
|
||||
@@ -221,7 +222,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
String lib = gen->default_module.foreign_library_paths[i];
|
||||
GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
|
||||
gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" \"%.*s\"", LIT(lib));
|
||||
" \"%.*s\"", LIT(lib));
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
@@ -320,7 +321,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -513,10 +514,6 @@ Array<String> setup_args(int argc, char const **argv) {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void print_usage_line(i32 indent, char const *fmt, ...) {
|
||||
while (indent --> 0) {
|
||||
gb_printf_err("\t");
|
||||
@@ -540,6 +537,7 @@ void usage(String argv0) {
|
||||
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
|
||||
print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
|
||||
print_usage_line(1, "version print version");
|
||||
print_usage_line(1, "report print information useful to reporting a bug");
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(0, "For more information of flags, apply the flag to see what is possible");
|
||||
print_usage_line(1, "-help");
|
||||
@@ -856,12 +854,12 @@ bool parse_build_flags(Array<String> args) {
|
||||
break;
|
||||
case BuildFlagParam_Boolean: {
|
||||
if (str_eq_ignore_case(param, str_lit("t")) ||
|
||||
str_eq_ignore_case(param, str_lit("true")) ||
|
||||
param == "1") {
|
||||
str_eq_ignore_case(param, str_lit("true")) ||
|
||||
param == "1") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (str_eq_ignore_case(param, str_lit("f")) ||
|
||||
str_eq_ignore_case(param, str_lit("false")) ||
|
||||
param == "0") {
|
||||
str_eq_ignore_case(param, str_lit("false")) ||
|
||||
param == "0") {
|
||||
value = exact_value_bool(false);
|
||||
} else {
|
||||
gb_printf_err("Invalid flag parameter for '%.*s' : '%.*s'\n", LIT(name), LIT(param));
|
||||
@@ -2373,6 +2371,10 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
|
||||
gb_printf("\n");
|
||||
return 0;
|
||||
} else if (command == "report") {
|
||||
build_context.command_kind = Command_bug_report;
|
||||
print_bug_report_help();
|
||||
return 0;
|
||||
} else {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
@@ -2396,7 +2398,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
// NOTE(bill): add 'shared' directory if it is not already set
|
||||
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
|
||||
add_library_collection(str_lit("shared"),
|
||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
2
vendor/sdl2/mixer/sdl_mixer.odin
vendored
2
vendor/sdl2/mixer/sdl_mixer.odin
vendored
@@ -94,7 +94,7 @@ SetError :: SDL.SetError
|
||||
GetError :: SDL.GetError
|
||||
ClearError :: SDL.ClearError
|
||||
|
||||
Mix_LoadWAV :: #force_inline proc "c" (file: cstring) -> ^Chunk {
|
||||
LoadWAV :: #force_inline proc "c" (file: cstring) -> ^Chunk {
|
||||
return LoadWAV_RW(SDL.RWFromFile(file, "rb"), true)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user