Merge branch 'master' into windows-llvm-13.0.0

This commit is contained in:
gingerBill
2021-11-01 12:11:57 +00:00
38 changed files with 3446 additions and 519 deletions

View File

@@ -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: |

View File

@@ -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

View File

@@ -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
View 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) }

View File

@@ -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) ---

View File

@@ -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))
}

View File

@@ -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
View 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))
}

View File

@@ -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)

View File

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

View 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,
}
}

View File

@@ -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,
}
}
}

View File

@@ -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))
}
}

View File

@@ -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))
}
}

View File

@@ -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))
}
}

View File

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

View 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)
}

View File

@@ -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
}

View File

@@ -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

641
src/bug_report.cpp Normal file
View 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();
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;
}
}
}
}
}

View File

@@ -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]);

View File

@@ -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},

View File

@@ -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 {

View File

@@ -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 {};
}

View File

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

View File

@@ -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"
};

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 {};
}

View File

@@ -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;
}

View File

@@ -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")));
}

View File

@@ -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;

View File

@@ -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)
}