mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-15 10:22:23 -07:00
Merge remote-tracking branch 'upstream/master' into prototype-fmt
This commit is contained in:
@@ -16,6 +16,21 @@ ptr_from_slice :: proc(str: []byte) -> ^byte {
|
||||
return d.data;
|
||||
}
|
||||
|
||||
truncate_to_byte :: proc(str: []byte, b: byte) -> []byte {
|
||||
n := index_byte(str, b);
|
||||
if n < 0 {
|
||||
n = len(str);
|
||||
}
|
||||
return str[:n];
|
||||
}
|
||||
truncate_to_rune :: proc(str: []byte, r: rune) -> []byte {
|
||||
n := index_rune(str, r);
|
||||
if n < 0 {
|
||||
n = len(str);
|
||||
}
|
||||
return str[:n];
|
||||
}
|
||||
|
||||
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||
compare :: proc(lhs, rhs: []byte) -> int {
|
||||
|
||||
@@ -1638,9 +1638,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
io.write_string(fi.writer, "intrinsics.x86_mmx<>");
|
||||
}
|
||||
io.write_byte(fi.writer, '<');
|
||||
defer io.write_byte(fi.writer, '>');
|
||||
for i in 0..<info.count {
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
package intrinsics
|
||||
|
||||
// Types
|
||||
|
||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
||||
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
@@ -13,8 +10,11 @@ soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
volatile_load :: proc(dst: ^$T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||
|
||||
// Atomics
|
||||
// Trapping
|
||||
debug_trap :: proc() ---
|
||||
trap :: proc() -> ! ---
|
||||
|
||||
// Atomics
|
||||
atomic_fence :: proc() ---
|
||||
atomic_fence_acq :: proc() ---
|
||||
atomic_fence_rel :: proc() ---
|
||||
@@ -89,9 +89,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
|
||||
cpu_relax :: proc() ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
|
||||
+96
-15
@@ -28,11 +28,20 @@ Allocator_Query_Info :: struct {
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Error :: runtime.Allocator_Error;
|
||||
/*
|
||||
Allocator_Error :: enum byte {
|
||||
None = 0,
|
||||
Out_Of_Memory = 1,
|
||||
Invalid_Pointer = 2,
|
||||
Invalid_Argument = 3,
|
||||
}
|
||||
*/
|
||||
Allocator_Proc :: runtime.Allocator_Proc;
|
||||
/*
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
|
||||
*/
|
||||
|
||||
Allocator :: runtime.Allocator;
|
||||
@@ -52,23 +61,49 @@ alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := contex
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil {
|
||||
return;
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return;
|
||||
return nil, nil;
|
||||
}
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
|
||||
return err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
@@ -77,18 +112,40 @@ resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_AL
|
||||
}
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
}
|
||||
return nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
ptr := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
return nil, err;
|
||||
}
|
||||
return nil, nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
|
||||
return set;
|
||||
}
|
||||
return nil;
|
||||
@@ -97,7 +154,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer;
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -218,4 +275,28 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
||||
free(old_memory, allocator, loc);
|
||||
return new_memory;
|
||||
}
|
||||
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if old_memory == nil {
|
||||
return alloc_bytes(new_size, alignment, allocator, loc);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
err := free_bytes(old_data, allocator, loc);
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_data, .None;
|
||||
}
|
||||
|
||||
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil || err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
runtime.copy(new_memory, old_data);
|
||||
free_bytes(old_data, allocator, loc);
|
||||
return new_memory, err;
|
||||
}
|
||||
|
||||
+154
-174
@@ -5,8 +5,8 @@ import "core:runtime"
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
@@ -47,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
arena := cast(^Arena)allocator_data;
|
||||
|
||||
switch mode {
|
||||
@@ -55,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset];
|
||||
@@ -63,7 +63,8 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
arena.offset += total_size;
|
||||
arena.peak_used = max(arena.peak_used, arena.offset);
|
||||
return zero(ptr, size);
|
||||
zero(ptr, size);
|
||||
return byte_slice(ptr, size), nil;
|
||||
|
||||
case .Free:
|
||||
// NOTE(bill): Free all at once
|
||||
@@ -73,20 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena.offset = 0;
|
||||
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
|
||||
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena));
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
@@ -109,9 +110,9 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
@@ -127,7 +128,7 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
|
||||
return;
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(ptr, s.backup_allocator);
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
@@ -136,7 +137,7 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
s := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
@@ -165,7 +166,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return rawptr(ptr);
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -175,7 +176,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return rawptr(ptr);
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
}
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
@@ -183,9 +184,12 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := alloc(size, alignment, a, loc);
|
||||
ptr, err := alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return ptr, err;
|
||||
}
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic]rawptr, a);
|
||||
s.leaked_allocations = make([dynamic][]byte, a);
|
||||
}
|
||||
append(&s.leaked_allocations, ptr);
|
||||
|
||||
@@ -195,7 +199,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return ptr, err;
|
||||
|
||||
case .Free:
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -205,30 +209,32 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for ptr, i in s.leaked_allocations {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data);
|
||||
if ptr == old_memory {
|
||||
free(ptr, s.backup_allocator);
|
||||
free_bytes(data, s.backup_allocator);
|
||||
ordered_remove(&s.leaked_allocations, i);
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("invalid pointer passed to default_temp_allocator");
|
||||
return nil, .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
|
||||
case .Free_All:
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for ptr in s.leaked_allocations {
|
||||
free(ptr, s.backup_allocator);
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
clear(&s.leaked_allocations);
|
||||
|
||||
@@ -238,26 +244,28 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
old_ptr := uintptr(old_memory);
|
||||
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size;
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
ptr := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
copy(ptr, old_memory, old_size);
|
||||
scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
|
||||
return ptr;
|
||||
data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
_, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
|
||||
@@ -301,18 +309,18 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator {
|
||||
|
||||
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.prev_offset = s.curr_offset;
|
||||
s.curr_offset += padding;
|
||||
@@ -326,7 +334,8 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -334,7 +343,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
@@ -346,20 +355,20 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
panic("Out of order stack allocator free");
|
||||
// panic("Out of order stack allocator free");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
s.curr_offset = old_offset;
|
||||
s.prev_offset = header.prev_offset;
|
||||
|
||||
|
||||
case .Free_All:
|
||||
s.prev_offset = 0;
|
||||
s.curr_offset = 0;
|
||||
@@ -369,7 +378,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -381,20 +390,22 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
ptr := raw_alloc(s, size, alignment);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
data, err := raw_alloc(s, size, alignment);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
}
|
||||
|
||||
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
|
||||
@@ -406,19 +417,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
zero(rawptr(curr_addr + uintptr(diff)), diff);
|
||||
}
|
||||
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -453,20 +464,20 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.offset += padding;
|
||||
|
||||
@@ -478,7 +489,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -486,19 +498,20 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
// panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
|
||||
@@ -514,41 +527,44 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
// panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Treat as a double free
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
ptr := raw_alloc(s, size, align);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
data, err := raw_alloc(s, size, align);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -579,42 +595,44 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
|
||||
|
||||
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
pool := (^Dynamic_Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return dynamic_pool_alloc(pool, size);
|
||||
return dynamic_pool_alloc_bytes(pool, size);
|
||||
case .Free:
|
||||
//
|
||||
return nil, nil;
|
||||
case .Free_All:
|
||||
dynamic_pool_free_all(pool);
|
||||
return nil, nil;
|
||||
case .Resize:
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
ptr := dynamic_pool_alloc(pool, size);
|
||||
copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
data, err := dynamic_pool_alloc_bytes(pool, size);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
info.size = pool.block_size;
|
||||
info.alignment = pool.alignment;
|
||||
return info;
|
||||
return byte_slice(info, size_of(info^)), nil;
|
||||
}
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -649,8 +667,14 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
data, err := dynamic_pool_alloc_bytes(pool, bytes);
|
||||
assert(err == nil);
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
|
||||
if block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it");
|
||||
}
|
||||
@@ -663,14 +687,17 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks);
|
||||
} else {
|
||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
data: []byte;
|
||||
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
new_block = raw_data(data);
|
||||
}
|
||||
|
||||
bytes_left = block_size;
|
||||
current_pos = new_block;
|
||||
current_block = new_block;
|
||||
return;
|
||||
}
|
||||
|
||||
n := bytes;
|
||||
@@ -678,26 +705,29 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
n += extra;
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, (^byte)(memory));
|
||||
append(&out_band_allocations, raw_data(memory));
|
||||
}
|
||||
return memory;
|
||||
return memory, err;
|
||||
}
|
||||
|
||||
if bytes_left < n {
|
||||
cycle_new_block(pool);
|
||||
err := cycle_new_block(pool);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||
bytes_left -= n;
|
||||
return memory;
|
||||
return byte_slice(memory, bytes), nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -730,7 +760,7 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
|
||||
|
||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
@@ -753,13 +783,13 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if set != nil {
|
||||
set^ = {.Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
@@ -770,70 +800,12 @@ panic_allocator :: proc() -> Allocator {
|
||||
}
|
||||
|
||||
|
||||
alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch alignment {
|
||||
case: return intrinsics.alloca(size, 2*align_of(uintptr));
|
||||
case 0: return intrinsics.alloca(size, 0);
|
||||
|
||||
case 1: return intrinsics.alloca(size, 1);
|
||||
case 2: return intrinsics.alloca(size, 2);
|
||||
case 4: return intrinsics.alloca(size, 4);
|
||||
case 8: return intrinsics.alloca(size, 8);
|
||||
case 16: return intrinsics.alloca(size, 16);
|
||||
case 32: return intrinsics.alloca(size, 32);
|
||||
case 64: return intrinsics.alloca(size, 64);
|
||||
case 128: return intrinsics.alloca(size, 128);
|
||||
case 256: return intrinsics.alloca(size, 256);
|
||||
case 512: return intrinsics.alloca(size, 512);
|
||||
case 1024: return intrinsics.alloca(size, 1024);
|
||||
case 2048: return intrinsics.alloca(size, 2048);
|
||||
case 4096: return intrinsics.alloca(size, 4096);
|
||||
case 8192: return intrinsics.alloca(size, 8192);
|
||||
case 16384: return intrinsics.alloca(size, 16384);
|
||||
case 32768: return intrinsics.alloca(size, 32768);
|
||||
case 65536: return intrinsics.alloca(size, 65536);
|
||||
}
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
|
||||
|
||||
case .Free:
|
||||
// Do nothing
|
||||
case .Free_All:
|
||||
// Do nothing
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
alloca_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = alloca_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
location: runtime.Source_Code_Location,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator_Bad_Free_Entry :: struct {
|
||||
memory: rawptr,
|
||||
@@ -864,7 +836,9 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
};
|
||||
}
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data);
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
@@ -872,23 +846,27 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
if entry, ok := data.allocation_map[info.pointer]; ok {
|
||||
info.size = entry.size;
|
||||
info.alignment = entry.alignment;
|
||||
return info;
|
||||
}
|
||||
info.pointer = nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
result: rawptr;
|
||||
result: []byte;
|
||||
err: Allocator_Error;
|
||||
if mode == .Free && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
});
|
||||
} else {
|
||||
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, flags, loc);
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return result, err;
|
||||
}
|
||||
}
|
||||
result_ptr := raw_data(result);
|
||||
|
||||
if data.allocation_map.allocator.procedure == nil {
|
||||
data.allocation_map.allocator = context.allocator;
|
||||
@@ -896,22 +874,24 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
case .Resize:
|
||||
if old_memory != result {
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
}
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
|
||||
@@ -925,13 +905,13 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
unreachable();
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result, err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1021,13 +1001,13 @@ small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) ->
|
||||
|
||||
|
||||
case .Query_Features:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
};
|
||||
return a;
|
||||
}
|
||||
|
||||
+6
-3
@@ -139,10 +139,13 @@ slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
|
||||
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
||||
}
|
||||
|
||||
slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{data = ptr, len = len};
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
|
||||
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
|
||||
@@ -6,6 +6,7 @@ Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Optional_Ok,
|
||||
Optional_Second,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||
|
||||
|
||||
@@ -212,11 +212,6 @@ Type_Flag_Bit_Set :: enum u32le {
|
||||
Underlying_Type = 4,
|
||||
}
|
||||
|
||||
Type_Flags_SimdVector :: distinct bit_set[Type_Flag_SimdVector; u32le];
|
||||
Type_Flag_SimdVector :: enum u32le {
|
||||
x86_mmx = 1,
|
||||
}
|
||||
|
||||
from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
|
||||
s: mem.Raw_Slice;
|
||||
s.data = rawptr(uintptr(base) + uintptr(a.offset));
|
||||
|
||||
@@ -1910,12 +1910,10 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
||||
ident := expect_token(p, .Ident);
|
||||
|
||||
switch ident.text {
|
||||
case "bounds_check":
|
||||
tags |= {.Bounds_Check};
|
||||
case "no_bounds_check":
|
||||
tags |= {.No_Bounds_Check};
|
||||
case "optional_ok":
|
||||
tags |= {.Optional_Ok};
|
||||
case "bounds_check": tags |= {.Bounds_Check};
|
||||
case "no_bounds_check": tags |= {.No_Bounds_Check};
|
||||
case "optional_ok": tags |= {.Optional_Ok};
|
||||
case "optional_second": tags |= {.Optional_Second};
|
||||
case:
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -133,8 +133,7 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
@@ -142,7 +141,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
@@ -159,13 +158,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
return mem.byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
@@ -174,9 +173,9 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
@@ -202,13 +201,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
heap_allocator :: proc() -> mem.Allocator {
|
||||
|
||||
@@ -34,8 +34,8 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
defer _unix_free(path_ptr);
|
||||
|
||||
path_cstr := cstring(path_ptr);
|
||||
path = strings.clone(string(path_cstr), allocator);
|
||||
return path, true;
|
||||
path_str := strings.clone(string(path_cstr), allocator);
|
||||
return path_str, true;
|
||||
}
|
||||
|
||||
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||
|
||||
@@ -565,14 +565,10 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
|
||||
n += _n(io.write_byte(w, ']'));
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
n += write_string(w, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
n += write_string(w, "#simd[");
|
||||
n += _n(io.write_i64(w, i64(info.count)));
|
||||
n += _n(io.write_byte(w, ']'));
|
||||
n += write_type(w, info.elem);
|
||||
}
|
||||
n += write_string(w, "#simd[");
|
||||
n += _n(io.write_i64(w, i64(info.count)));
|
||||
n += _n(io.write_byte(w, ']'));
|
||||
n += write_type(w, info.elem);
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
n += write_string(w, "#relative(");
|
||||
|
||||
@@ -144,7 +144,6 @@ Type_Info_Simd_Vector :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
count: int,
|
||||
is_x86_mmx: bool,
|
||||
};
|
||||
Type_Info_Relative_Pointer :: struct {
|
||||
pointer: ^Type_Info,
|
||||
@@ -252,7 +251,6 @@ Source_Code_Location :: struct {
|
||||
|
||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
|
||||
|
||||
|
||||
// Allocation Stuff
|
||||
Allocator_Mode :: enum byte {
|
||||
Alloc,
|
||||
@@ -271,9 +269,17 @@ Allocator_Query_Info :: struct {
|
||||
alignment: Maybe(int),
|
||||
}
|
||||
|
||||
Allocator_Error :: enum byte {
|
||||
None = 0,
|
||||
Out_Of_Memory = 1,
|
||||
Invalid_Pointer = 2,
|
||||
Invalid_Argument = 3,
|
||||
}
|
||||
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location: Source_Code_Location = #caller_location) -> rawptr;
|
||||
old_memory: rawptr, old_size: int,
|
||||
location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
|
||||
Allocator :: struct {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
@@ -426,9 +432,6 @@ typeid_base_without_enum :: typeid_core;
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.assume")
|
||||
assume :: proc(cond: bool) ---;
|
||||
|
||||
@(link_name="llvm.debugtrap")
|
||||
debug_trap :: proc() ---;
|
||||
|
||||
|
||||
@@ -127,26 +127,30 @@ free_all :: proc{mem_free_all};
|
||||
|
||||
|
||||
@builtin
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
mem_free(raw_data(str), allocator, loc);
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(str), allocator, loc);
|
||||
}
|
||||
@builtin
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
mem_free((^byte)(str), allocator, loc);
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free((^byte)(str), allocator, loc);
|
||||
}
|
||||
@builtin
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
mem_free(raw_data(array), array.allocator, loc);
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), array.allocator, loc);
|
||||
}
|
||||
@builtin
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
mem_free(raw_data(array), allocator, loc);
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), allocator, loc);
|
||||
}
|
||||
@builtin
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
raw := transmute(Raw_Map)m;
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc);
|
||||
mem_free(raw.entries.data, raw.entries.allocator, loc);
|
||||
err := delete_slice(raw.hashes, raw.entries.allocator, loc);
|
||||
err1 := mem_free(raw.entries.data, raw.entries.allocator, loc);
|
||||
if err == nil {
|
||||
err = err1;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,57 +167,57 @@ delete :: proc{
|
||||
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
|
||||
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
|
||||
@builtin
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil { ptr^ = T{}; }
|
||||
return ptr;
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
|
||||
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
|
||||
return (^T)(ptr), err;
|
||||
}
|
||||
|
||||
@builtin
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil { ptr^ = data; }
|
||||
return ptr;
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
|
||||
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
|
||||
res := (^T)(ptr);
|
||||
if ptr != nil && err != .Out_Of_Memory {
|
||||
res^ = data;
|
||||
}
|
||||
return res, err;
|
||||
}
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16;
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_slice_error_loc(loc, len);
|
||||
data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
|
||||
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc);
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return nil;
|
||||
return nil, err;
|
||||
}
|
||||
// mem_zero(data, size_of(E)*len);
|
||||
s := Raw_Slice{data, len};
|
||||
return transmute(T)s;
|
||||
s := Raw_Slice{raw_data(data), len};
|
||||
return transmute(T)s, err;
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_dynamic_array_error_loc(loc, len, cap);
|
||||
data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
data, err := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||
if data == nil && size_of(E) != 0 {
|
||||
s.len, s.cap = 0, 0;
|
||||
}
|
||||
// mem_zero(data, size_of(E)*cap);
|
||||
return transmute(T)s;
|
||||
return transmute(T)s, err;
|
||||
}
|
||||
|
||||
@builtin
|
||||
@@ -449,15 +453,15 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E);
|
||||
allocator := a.allocator;
|
||||
|
||||
new_data := allocator.procedure(
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, 0, loc,
|
||||
a.data, old_size, loc,
|
||||
);
|
||||
if new_data == nil {
|
||||
if new_data == nil || err != nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
a.data = new_data;
|
||||
a.data = raw_data(new_data);
|
||||
a.cap = capacity;
|
||||
return true;
|
||||
}
|
||||
@@ -483,15 +487,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E);
|
||||
allocator := a.allocator;
|
||||
|
||||
new_data := allocator.procedure(
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, 0, loc,
|
||||
a.data, old_size, loc,
|
||||
);
|
||||
if new_data == nil {
|
||||
if new_data == nil || err != nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
a.data = new_data;
|
||||
a.data = raw_data(new_data);
|
||||
a.len = length;
|
||||
a.cap = length;
|
||||
return true;
|
||||
|
||||
@@ -74,7 +74,7 @@ raw_soa_footer :: proc{
|
||||
|
||||
|
||||
@builtin
|
||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
|
||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
|
||||
if length <= 0 {
|
||||
return;
|
||||
}
|
||||
@@ -106,13 +106,15 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
||||
}
|
||||
assert(allocator.procedure != nil);
|
||||
|
||||
new_data := allocator.procedure(
|
||||
new_bytes: []byte;
|
||||
new_bytes, err = allocator.procedure(
|
||||
allocator.data, .Alloc, total_size, max_align,
|
||||
nil, 0, 0, loc,
|
||||
nil, 0, loc,
|
||||
);
|
||||
if new_data == nil {
|
||||
if new_bytes == nil || err != nil {
|
||||
return;
|
||||
}
|
||||
new_data := raw_data(new_bytes);
|
||||
|
||||
data := uintptr(&array);
|
||||
offset := 0;
|
||||
@@ -131,7 +133,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
|
||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
|
||||
return make_soa_aligned(T, length, align_of(E), allocator, loc);
|
||||
}
|
||||
|
||||
@@ -226,13 +228,14 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
||||
|
||||
old_data := (^rawptr)(array)^;
|
||||
|
||||
new_data := array.allocator.procedure(
|
||||
new_bytes, err := array.allocator.procedure(
|
||||
array.allocator.data, .Alloc, new_size, max_align,
|
||||
nil, old_size, 0, loc,
|
||||
nil, old_size, loc,
|
||||
);
|
||||
if new_data == nil {
|
||||
if new_bytes == nil || err != nil {
|
||||
return false;
|
||||
}
|
||||
new_data := raw_data(new_bytes);
|
||||
|
||||
|
||||
footer.cap = capacity;
|
||||
@@ -256,9 +259,9 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
||||
new_offset += type.size * capacity;
|
||||
}
|
||||
|
||||
array.allocator.procedure(
|
||||
_, err = array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, 0, loc,
|
||||
old_data, old_size, loc,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -5,8 +5,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
@@ -26,6 +26,11 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
|
||||
}
|
||||
}
|
||||
|
||||
@(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);
|
||||
|
||||
@@ -35,7 +40,7 @@ Default_Temp_Allocator :: struct {
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
@@ -51,7 +56,7 @@ default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
return;
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(ptr, s.backup_allocator);
|
||||
free(raw_data(ptr), s.backup_allocator);
|
||||
}
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
@@ -60,7 +65,7 @@ default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
s := (^Default_Temp_Allocator)(allocator_data);
|
||||
|
||||
@@ -84,7 +89,7 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return rawptr(ptr);
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -94,7 +99,7 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return rawptr(ptr);
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
}
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
@@ -102,11 +107,14 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := mem_alloc(size, alignment, a, loc);
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic]rawptr, a);
|
||||
data, err := mem_alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
append(&s.leaked_allocations, ptr);
|
||||
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 {
|
||||
@@ -115,11 +123,11 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return data, .None;
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -129,30 +137,32 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for ptr, i in s.leaked_allocations {
|
||||
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 nil;
|
||||
return nil, .None;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("invalid pointer passed to default_temp_allocator");
|
||||
return nil, .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
|
||||
case .Free_All:
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for ptr in s.leaked_allocations {
|
||||
free(ptr, s.backup_allocator);
|
||||
for data in s.leaked_allocations {
|
||||
free(raw_data(data), s.backup_allocator);
|
||||
}
|
||||
clear(&s.leaked_allocations);
|
||||
|
||||
@@ -163,26 +173,28 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
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 old_memory;
|
||||
return byte_slice(old_memory, size), .None;
|
||||
}
|
||||
}
|
||||
ptr := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
mem_copy(ptr, old_memory, old_size);
|
||||
default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
|
||||
return ptr;
|
||||
ptr, err := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||
if err == .None {
|
||||
copy(ptr, byte_slice(old_memory, old_size));
|
||||
_, err = default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||
}
|
||||
return ptr, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
|
||||
@@ -29,10 +29,13 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
new_size := cap * elem_size;
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
|
||||
new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc);
|
||||
if err != nil {
|
||||
return false;
|
||||
}
|
||||
if new_data != nil || elem_size == 0 {
|
||||
array.data = new_data;
|
||||
array.cap = cap;
|
||||
array.data = raw_data(new_data);
|
||||
array.cap = min(cap, len(new_data)/elem_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -173,8 +173,8 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
|
||||
old_size := array.len*size_of(T);
|
||||
new_size := new_count*size_of(T);
|
||||
|
||||
new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
|
||||
if new_data == nil {
|
||||
new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
|
||||
if new_data == nil || err != nil {
|
||||
return false;
|
||||
}
|
||||
array.data = new_data;
|
||||
|
||||
+32
-16
@@ -159,43 +159,59 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, 0, loc);
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc);
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc);
|
||||
return raw_data(data), err;
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return;
|
||||
return .None;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return;
|
||||
return .None;
|
||||
}
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, 0, loc);
|
||||
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) {
|
||||
new_data: []byte;
|
||||
switch {
|
||||
case allocator.procedure == nil:
|
||||
return nil;
|
||||
return;
|
||||
case new_size == 0:
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
return nil;
|
||||
new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc);
|
||||
case ptr == nil:
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc);
|
||||
case:
|
||||
new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
new_ptr = raw_data(new_data);
|
||||
return;
|
||||
}
|
||||
memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
|
||||
return memory_compare(a, b, n) == 0;
|
||||
|
||||
@@ -88,7 +88,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
@@ -97,7 +97,7 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
@@ -114,13 +114,13 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
return byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
aligned_free :: proc "contextless" (p: rawptr) {
|
||||
@@ -129,9 +129,9 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
@@ -157,13 +157,13 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
|
||||
@@ -347,14 +347,10 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
||||
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
print_string("intrinsics.x86_mmx");
|
||||
} else {
|
||||
print_string("#simd[");
|
||||
print_u64(u64(info.count));
|
||||
print_byte(']');
|
||||
print_type(info.elem);
|
||||
}
|
||||
print_string("#simd[");
|
||||
print_u64(u64(info.count));
|
||||
print_byte(']');
|
||||
print_type(info.elem);
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
print_string("#relative(");
|
||||
|
||||
@@ -33,6 +33,21 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring {
|
||||
return cstring(d.data);
|
||||
}
|
||||
|
||||
truncate_to_byte :: proc(str: string, b: byte) -> string {
|
||||
n := index_byte(str, b);
|
||||
if n < 0 {
|
||||
n = len(str);
|
||||
}
|
||||
return str[:n];
|
||||
}
|
||||
truncate_to_rune :: proc(str: string, r: rune) -> string {
|
||||
n := index_rune(str, r);
|
||||
if n < 0 {
|
||||
n = len(str);
|
||||
}
|
||||
return str[:n];
|
||||
}
|
||||
|
||||
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||
compare :: proc(lhs, rhs: string) -> int {
|
||||
|
||||
@@ -228,6 +228,7 @@ once_do :: proc(o: ^Once, fn: proc()) {
|
||||
}
|
||||
}
|
||||
|
||||
@(cold)
|
||||
_once_do_slow :: proc(o: ^Once, fn: proc()) {
|
||||
mutex_lock(&o.m);
|
||||
defer mutex_unlock(&o.m);
|
||||
|
||||
@@ -104,6 +104,7 @@ enum BuildModeKind {
|
||||
BuildMode_DynamicLibrary,
|
||||
BuildMode_Object,
|
||||
BuildMode_Assembly,
|
||||
BuildMode_LLVM_IR,
|
||||
};
|
||||
|
||||
enum CommandKind : u32 {
|
||||
@@ -113,7 +114,7 @@ enum CommandKind : u32 {
|
||||
Command_query = 1<<4,
|
||||
Command_doc = 1<<5,
|
||||
Command_version = 1<<6,
|
||||
Command_test = 1<<7,
|
||||
Command_test = 1<<7,
|
||||
|
||||
Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
|
||||
Command__does_build = Command_run|Command_build|Command_test,
|
||||
@@ -838,7 +839,7 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
bc->link_flags = str_lit("-arch arm64 ");
|
||||
break;
|
||||
}
|
||||
if (!bc->use_llvm_api) {
|
||||
if ((bc->command_kind & Command__does_build) != 0 && !bc->use_llvm_api) {
|
||||
gb_printf_err("The arm64 architecture is only supported with -llvm-api\n");;
|
||||
gb_exit(1);
|
||||
}
|
||||
|
||||
+39
-15
@@ -343,17 +343,31 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT TODO(bill)
|
||||
// Date: 2018-09-29
|
||||
// This assert fails on `using import` if the name of the alias is the same. What should be the expected behaviour?
|
||||
// Namespace collision or override? Overridding is the current behaviour
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
||||
// original entity was still used check checked, but the checking was only
|
||||
// relying on "constant" data such as the Entity.type and Entity.Constant.value
|
||||
//
|
||||
// using import "foo"
|
||||
// bar :: foo.bar;
|
||||
|
||||
// GB_ASSERT_MSG(found_entity == original_entity, "%.*s == %.*s", LIT(found_entity->token.string), LIT(new_entity->token.string));
|
||||
// Therefore two things can be done: the type can be assigned to state that it
|
||||
// has been "evaluated" and the variant data can be copied across
|
||||
|
||||
string_map_set(&found_scope->elements, original_name, new_entity);
|
||||
|
||||
original_entity->type = new_entity->type;
|
||||
|
||||
if (original_entity->identifier == nullptr) {
|
||||
original_entity->identifier = new_entity->identifier;
|
||||
}
|
||||
if (original_entity->identifier != nullptr &&
|
||||
original_entity->identifier->kind == Ast_Ident) {
|
||||
original_entity->identifier->Ident.entity = new_entity;
|
||||
}
|
||||
original_entity->flags |= EntityFlag_Overridden;
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
|
||||
// This is most likely NEVER required, but it does not at all hurt to keep
|
||||
isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
|
||||
isize size = gb_size_of(*original_entity) - offset;
|
||||
gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -374,6 +388,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
|
||||
|
||||
Operand operand = {};
|
||||
|
||||
Entity *other_entity = nullptr;
|
||||
if (init != nullptr) {
|
||||
Entity *entity = nullptr;
|
||||
if (init->kind == Ast_Ident) {
|
||||
@@ -412,7 +427,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
|
||||
GB_ASSERT(operand.proc_group->kind == Entity_ProcGroup);
|
||||
// NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope
|
||||
// Thank goodness I made entities a tagged union to allow for this implace patching
|
||||
// override_entity_in_scope(e, operand.proc_group);
|
||||
e->kind = Entity_ProcGroup;
|
||||
e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities);
|
||||
return;
|
||||
@@ -454,7 +468,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
|
||||
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -694,6 +707,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
e->flags |= EntityFlag_Cold;
|
||||
}
|
||||
|
||||
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
|
||||
|
||||
|
||||
switch (e->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
if (pl->inlining == ProcInlining_inline) {
|
||||
error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\"");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
e->Procedure.is_export = ac.is_export;
|
||||
e->deprecated_message = ac.deprecated_message;
|
||||
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
|
||||
@@ -718,11 +743,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
if (pt->calling_convention != ProcCC_Odin &&
|
||||
pt->calling_convention != ProcCC_Contextless) {
|
||||
if (pt->calling_convention != ProcCC_Odin) {
|
||||
error(e->token, "Procedure 'main' cannot have a custom calling convention");
|
||||
}
|
||||
pt->calling_convention = ProcCC_Contextless;
|
||||
pt->calling_convention = ProcCC_Odin;
|
||||
if (e->pkg->kind == Package_Init) {
|
||||
if (ctx->info->entry_point != nullptr) {
|
||||
error(e->token, "Redeclaration of the entry pointer procedure 'main'");
|
||||
@@ -846,7 +870,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
|
||||
void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
@@ -946,7 +970,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr,
|
||||
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
|
||||
void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
|
||||
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
|
||||
auto *pge = &pg_entity->ProcGroup;
|
||||
String proc_group_name = pg_entity->token.string;
|
||||
|
||||
+376
-114
@@ -79,15 +79,12 @@ void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e,
|
||||
Type * check_type (CheckerContext *c, Ast *expression);
|
||||
Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
|
||||
Type * make_optional_ok_type (Type *value, bool typed=true);
|
||||
void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
|
||||
Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
|
||||
Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
|
||||
Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
|
||||
void check_not_tuple (CheckerContext *c, Operand *operand);
|
||||
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
|
||||
gbString expr_to_string (Ast *expression);
|
||||
void check_entity_decl (CheckerContext *c, Entity *e, DeclInfo *decl, Type *named_type);
|
||||
void check_const_decl (CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
|
||||
void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
|
||||
void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
|
||||
bool check_is_terminating (Ast *node, String const &label);
|
||||
@@ -654,13 +651,27 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
|
||||
}
|
||||
|
||||
Ast *expr = unparen_expr(operand->expr);
|
||||
if (expr != nullptr && expr->kind == Ast_AutoCast) {
|
||||
Operand x = *operand;
|
||||
x.expr = expr->AutoCast.expr;
|
||||
bool ok = check_cast_internal(c, &x, type);
|
||||
if (ok) {
|
||||
return MAXIMUM_TYPE_DISTANCE;
|
||||
}
|
||||
if (expr != nullptr) {
|
||||
if (expr->kind == Ast_AutoCast) {
|
||||
Operand x = *operand;
|
||||
x.expr = expr->AutoCast.expr;
|
||||
bool ok = check_cast_internal(c, &x, type);
|
||||
if (ok) {
|
||||
return MAXIMUM_TYPE_DISTANCE;
|
||||
}
|
||||
} /*else if (expr->kind == Ast_CallExpr) {
|
||||
// NOTE(bill, 2021-04-19): Allow assignment of procedure calls with #optional_ok
|
||||
ast_node(ce, CallExpr, expr);
|
||||
Type *pt = base_type(type_of_expr(ce->proc));
|
||||
if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
|
||||
Operand x = *operand;
|
||||
x.type = pt->Proc.results->Tuple.variables[0]->type;
|
||||
i64 res = check_distance_between_types(c, &x, type);
|
||||
if (res >= 0) {
|
||||
return res+1;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -774,6 +785,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
|
||||
LIT(context_name));
|
||||
operand->mode = Addressing_Invalid;
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1714,12 +1727,14 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
|
||||
}
|
||||
|
||||
|
||||
void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
|
||||
void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
|
||||
GB_ASSERT(o->mode == Addressing_Constant);
|
||||
if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) {
|
||||
if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) {
|
||||
gbString a = expr_to_string(o->expr);
|
||||
gbString b = type_to_string(type);
|
||||
gbString c = type_to_string(o->type);
|
||||
defer(
|
||||
gb_string_free(c);
|
||||
gb_string_free(b);
|
||||
gb_string_free(a);
|
||||
o->mode = Addressing_Invalid;
|
||||
@@ -1729,12 +1744,12 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) {
|
||||
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||
error(o->expr, "'%s' truncated to '%s'", a, b);
|
||||
} else {
|
||||
error(o->expr, "Cannot convert '%s' to '%s'", a, b);
|
||||
check_assignment_error_suggestion(c, o, type);
|
||||
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
|
||||
check_assignment_error_suggestion(ctx, o, type);
|
||||
}
|
||||
} else {
|
||||
error(o->expr, "Cannot convert '%s' to '%s'", a, b);
|
||||
check_assignment_error_suggestion(c, o, type);
|
||||
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
|
||||
check_assignment_error_suggestion(ctx, o, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2224,6 +2239,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if (is_type_tuple(src)) {
|
||||
// Ast *expr = unparen_expr(operand->expr);
|
||||
// if (expr && expr->kind == Ast_CallExpr) {
|
||||
// // NOTE(bill, 2021-04-19): Allow casting procedure calls with #optional_ok
|
||||
// ast_node(ce, CallExpr, expr);
|
||||
// Type *pt = base_type(type_of_expr(ce->proc));
|
||||
// if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
|
||||
// if (pt->Proc.result_count > 0) {
|
||||
// Operand op = *operand;
|
||||
// op.type = pt->Proc.results->Tuple.variables[0]->type;
|
||||
// bool ok = check_is_castable_to(c, &op, y);
|
||||
// if (ok) {
|
||||
// ce->optional_ok_one = true;
|
||||
// }
|
||||
// return ok;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (is_constant && is_type_untyped(src) && is_type_string(src)) {
|
||||
if (is_type_u8_array(dst)) {
|
||||
String s = operand->value.value_string;
|
||||
@@ -2339,6 +2374,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
|
||||
if (is_type_rawptr(src) && is_type_proc(dst)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2728,31 +2764,27 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
||||
ExactValue a = x->value;
|
||||
ExactValue b = y->value;
|
||||
|
||||
// Type *type = base_type(x->type);
|
||||
Type *type = x->type;
|
||||
if (is_type_pointer(type)) {
|
||||
GB_ASSERT(op.kind == Token_Sub);
|
||||
i64 bytes = a.value_pointer - b.value_pointer;
|
||||
i64 diff = bytes/type_size_of(type);
|
||||
x->value = exact_value_pointer(diff);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_type_constant_type(type)) {
|
||||
if (!is_type_constant_type(x->type)) {
|
||||
#if 0
|
||||
gbString xt = type_to_string(x->type);
|
||||
gbString err_str = expr_to_string(node);
|
||||
error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str);
|
||||
gb_string_free(err_str);
|
||||
gb_string_free(xt);
|
||||
x->mode = Addressing_Invalid;
|
||||
#else
|
||||
// NOTE(bill, 2021-04-21): The above is literally a useless error message.
|
||||
// Why did I add it in the first place?!
|
||||
x->mode = Addressing_Value;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (op.kind == Token_Quo && is_type_integer(type)) {
|
||||
if (op.kind == Token_Quo && is_type_integer(x->type)) {
|
||||
op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
|
||||
}
|
||||
|
||||
if (is_type_bit_set(type)) {
|
||||
if (is_type_bit_set(x->type)) {
|
||||
switch (op.kind) {
|
||||
case Token_Add: op.kind = Token_Or; break;
|
||||
case Token_Sub: op.kind = Token_AndNot; break;
|
||||
@@ -2761,11 +2793,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
||||
|
||||
x->value = exact_binary_operator_value(op.kind, a, b);
|
||||
|
||||
if (is_type_typed(type)) {
|
||||
if (is_type_typed(x->type)) {
|
||||
if (node != nullptr) {
|
||||
x->expr = node;
|
||||
}
|
||||
check_is_expressible(c, x, type);
|
||||
check_is_expressible(c, x, x->type);
|
||||
}
|
||||
return;
|
||||
} else if (is_type_string(x->type)) {
|
||||
@@ -2797,8 +2829,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
||||
|
||||
|
||||
void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
|
||||
if (found == nullptr) {
|
||||
if (type != nullptr && type != t_invalid) {
|
||||
if (e->tav.type == nullptr || e->tav.type == t_invalid) {
|
||||
add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
ExprInfo old = *found;
|
||||
@@ -2865,6 +2903,7 @@ void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
|
||||
void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
gbString type_str = type_to_string(target_type);
|
||||
gbString from_type_str = type_to_string(operand->type);
|
||||
char const *extra_text = "";
|
||||
|
||||
if (operand->mode == Addressing_Constant) {
|
||||
@@ -2875,8 +2914,9 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
|
||||
}
|
||||
}
|
||||
}
|
||||
error(operand->expr, "Cannot convert '%s' to '%s'%s", expr_str, type_str, extra_text);
|
||||
error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
|
||||
|
||||
gb_string_free(from_type_str);
|
||||
gb_string_free(type_str);
|
||||
gb_string_free(expr_str);
|
||||
operand->mode = Addressing_Invalid;
|
||||
@@ -5717,8 +5757,131 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
operand->mode = Addressing_NoValue;
|
||||
break;
|
||||
|
||||
case BuiltinProc_trap:
|
||||
case BuiltinProc_debug_trap:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
}
|
||||
operand->mode = Addressing_NoValue;
|
||||
break;
|
||||
|
||||
case BuiltinProc_read_cycle_counter:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
}
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = t_i64;
|
||||
break;
|
||||
|
||||
case BuiltinProc_count_ones:
|
||||
case BuiltinProc_trailing_zeros:
|
||||
case BuiltinProc_reverse_bits:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
// continue anyway
|
||||
}
|
||||
{
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_integer_like(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
} else if (x.type == t_llvm_bool) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = default_type(x.type);
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_byte_swap:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
// continue anyway
|
||||
}
|
||||
{
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_integer_like(x.type) && !is_type_float(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
} else if (x.type == t_llvm_bool) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
}
|
||||
i64 sz = type_size_of(x.type);
|
||||
if (sz < 2) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz);
|
||||
gb_string_free(xts);
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = default_type(x.type);
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_overflow_add:
|
||||
case BuiltinProc_overflow_sub:
|
||||
case BuiltinProc_overflow_mul:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
// continue anyway
|
||||
}
|
||||
{
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
check_expr(c, &y, ce->args[1]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type);
|
||||
convert_to_typed(c, &x, y.type);
|
||||
if (is_type_untyped(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_integer(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
Type *ct = core_type(x.type);
|
||||
if (is_type_different_to_arch_endianness(ct)) {
|
||||
GB_ASSERT(ct->kind == Type_Basic);
|
||||
if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_atomic_fence:
|
||||
case BuiltinProc_atomic_fence_acq:
|
||||
@@ -5859,8 +6022,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
case BuiltinProc_fixed_point_div_sat:
|
||||
{
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
return false;
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
// continue anyway
|
||||
}
|
||||
|
||||
Operand x = {};
|
||||
@@ -5885,7 +6048,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
if (!are_types_identical(x.type, y.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
gbString yts = type_to_string(y.type);
|
||||
error(x.expr, "Mismatched types for %.*s, %s vs %s", LIT(builtin_procs[id].name), xts, yts);
|
||||
error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
|
||||
gb_string_free(yts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
@@ -5893,7 +6056,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
|
||||
if (!is_type_integer(x.type) || is_type_untyped(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer type for %.*s, got %s", LIT(builtin_procs[id].name), xts);
|
||||
error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
@@ -5903,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
if (z.mode != Addressing_Constant || !is_type_integer(z.type)) {
|
||||
error(z.expr, "Expected a constant integer for the scale in %.*s", LIT(builtin_procs[id].name));
|
||||
error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name));
|
||||
return false;
|
||||
}
|
||||
i64 n = exact_value_to_i64(z.value);
|
||||
if (n <= 0) {
|
||||
error(z.expr, "Scale parameter in %.*s must be positive, got %lld", LIT(builtin_procs[id].name), n);
|
||||
error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n);
|
||||
return false;
|
||||
}
|
||||
i64 sz = 8*type_size_of(x.type);
|
||||
if (n > sz) {
|
||||
error(z.expr, "Scale parameter in %.*s is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
|
||||
error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5923,6 +6086,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
break;
|
||||
|
||||
|
||||
case BuiltinProc_expect:
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||
// continue anyway
|
||||
}
|
||||
{
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
check_expr(c, &y, ce->args[1]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type);
|
||||
convert_to_typed(c, &x, y.type);
|
||||
if (!are_types_identical(x.type, y.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
gbString yts = type_to_string(y.type);
|
||||
error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts);
|
||||
gb_string_free(yts);
|
||||
gb_string_free(xts);
|
||||
*operand = x; // minimize error propagation
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_type_integer_like(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts);
|
||||
gb_string_free(xts);
|
||||
*operand = x;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (y.mode != Addressing_Constant) {
|
||||
error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name));
|
||||
}
|
||||
|
||||
if (x.mode == Addressing_Constant) {
|
||||
// NOTE(bill): just completely ignore this intrinsic entirely
|
||||
*operand = x;
|
||||
return true;
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = x.type;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
case BuiltinProc_type_base_type:
|
||||
if (operand->mode != Addressing_Type) {
|
||||
@@ -6462,17 +6678,38 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
|
||||
if (o.type == nullptr || o.type->kind != Type_Tuple) {
|
||||
if (lhs.count == 2 && rhs.count == 1 &&
|
||||
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
||||
Type *tuple = make_optional_ok_type(o.type);
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
bool do_normal = true;
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
|
||||
Operand val = o;
|
||||
Operand ok = o;
|
||||
val.mode = Addressing_Value;
|
||||
ok.mode = Addressing_Value;
|
||||
ok.type = t_untyped_bool;
|
||||
array_add(operands, val);
|
||||
array_add(operands, ok);
|
||||
Operand val0 = o;
|
||||
Operand val1 = o;
|
||||
val0.mode = Addressing_Value;
|
||||
val1.mode = Addressing_Value;
|
||||
val1.type = t_untyped_bool;
|
||||
|
||||
|
||||
if (expr->kind == Ast_CallExpr) {
|
||||
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
|
||||
if (is_type_proc(pt)) {
|
||||
do_normal = false;
|
||||
Type *tuple = pt->Proc.results;
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
|
||||
if (pt->Proc.result_count >= 2) {
|
||||
Type *t1 = tuple->Tuple.variables[1]->type;
|
||||
val1.type = t1;
|
||||
}
|
||||
expr->CallExpr.optional_ok_one = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_normal) {
|
||||
Type *tuple = make_optional_ok_type(o.type);
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
}
|
||||
|
||||
array_add(operands, val0);
|
||||
array_add(operands, val1);
|
||||
optional_ok = true;
|
||||
tuple_index += 2;
|
||||
} else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) {
|
||||
@@ -6493,27 +6730,12 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
|
||||
}
|
||||
} else {
|
||||
TypeTuple *tuple = &o.type->Tuple;
|
||||
if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) {
|
||||
GB_ASSERT(tuple->variables.count == 2);
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
if (expr->kind == Ast_CallExpr) {
|
||||
expr->CallExpr.optional_ok_one = true;
|
||||
}
|
||||
Operand val = o;
|
||||
val.type = tuple->variables[0]->type;
|
||||
val.mode = Addressing_Value;
|
||||
array_add(operands, val);
|
||||
tuple_index += tuple->variables.count;
|
||||
|
||||
add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
|
||||
} else {
|
||||
for_array(j, tuple->variables) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(operands, o);
|
||||
}
|
||||
|
||||
tuple_index += tuple->variables.count;
|
||||
for_array(j, tuple->variables) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(operands, o);
|
||||
}
|
||||
|
||||
tuple_index += tuple->variables.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6570,18 +6792,38 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
|
||||
if (o.type == nullptr || o.type->kind != Type_Tuple) {
|
||||
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
|
||||
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
||||
Type *tuple = make_optional_ok_type(o.type);
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
bool do_normal = true;
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
|
||||
Operand val = o;
|
||||
Operand ok = o;
|
||||
val.mode = Addressing_Value;
|
||||
ok.mode = Addressing_Value;
|
||||
// ok.type = t_bool;
|
||||
ok.type = t_untyped_bool;
|
||||
array_add(operands, val);
|
||||
array_add(operands, ok);
|
||||
Operand val0 = o;
|
||||
Operand val1 = o;
|
||||
val0.mode = Addressing_Value;
|
||||
val1.mode = Addressing_Value;
|
||||
val1.type = t_untyped_bool;
|
||||
|
||||
|
||||
if (expr->kind == Ast_CallExpr) {
|
||||
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
|
||||
if (is_type_proc(pt)) {
|
||||
do_normal = false;
|
||||
Type *tuple = pt->Proc.results;
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
|
||||
if (pt->Proc.result_count >= 2) {
|
||||
Type *t1 = tuple->Tuple.variables[1]->type;
|
||||
val1.type = t1;
|
||||
}
|
||||
expr->CallExpr.optional_ok_one = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_normal) {
|
||||
Type *tuple = make_optional_ok_type(o.type);
|
||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
||||
}
|
||||
|
||||
array_add(operands, val0);
|
||||
array_add(operands, val1);
|
||||
optional_ok = true;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
|
||||
} else {
|
||||
@@ -6590,30 +6832,13 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
|
||||
}
|
||||
} else {
|
||||
TypeTuple *tuple = &o.type->Tuple;
|
||||
if (o.mode == Addressing_OptionalOk && lhs_count == 1) {
|
||||
GB_ASSERT(tuple->variables.count == 2);
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
if (expr->kind == Ast_CallExpr) {
|
||||
expr->CallExpr.optional_ok_one = true;
|
||||
}
|
||||
Operand val = o;
|
||||
val.type = tuple->variables[0]->type;
|
||||
val.mode = Addressing_Value;
|
||||
array_add(operands, val);
|
||||
|
||||
isize count = tuple->variables.count;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
|
||||
|
||||
add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
|
||||
} else {
|
||||
for_array(j, tuple->variables) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(operands, o);
|
||||
}
|
||||
|
||||
isize count = tuple->variables.count;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
|
||||
for_array(j, tuple->variables) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(operands, o);
|
||||
}
|
||||
|
||||
isize count = tuple->variables.count;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6863,6 +7088,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
data->score = score;
|
||||
data->result_type = final_proc_type->Proc.results;
|
||||
data->gen_entity = gen_entity;
|
||||
add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {});
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -7080,6 +7306,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
||||
data->score = score;
|
||||
data->result_type = pt->results;
|
||||
data->gen_entity = gen_entity;
|
||||
add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {});
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -7333,7 +7560,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
|
||||
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
|
||||
|
||||
|
||||
CallArgumentData data = {};
|
||||
CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||
if (err != CallArgumentError_None) {
|
||||
@@ -7341,7 +7567,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
}
|
||||
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||
add_entity_use(c, ident, entity_to_use);
|
||||
|
||||
if (entity_to_use != nullptr) {
|
||||
update_expr_type(c, operand->expr, entity_to_use->type, true);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -7613,6 +7841,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||
add_entity_use(c, ident, entity_to_use);
|
||||
if (entity_to_use != nullptr) {
|
||||
update_expr_type(c, operand->expr, entity_to_use->type, true);
|
||||
}
|
||||
|
||||
if (data.gen_entity != nullptr) {
|
||||
Entity *e = data.gen_entity;
|
||||
@@ -7628,7 +7859,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
|
||||
decl->where_clauses_evaluated = true;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
} else {
|
||||
@@ -7644,6 +7874,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||
add_entity_use(c, ident, entity_to_use);
|
||||
if (entity_to_use != nullptr) {
|
||||
update_expr_type(c, operand->expr, entity_to_use->type, true);
|
||||
}
|
||||
|
||||
if (data.gen_entity != nullptr) {
|
||||
Entity *e = data.gen_entity;
|
||||
@@ -7659,10 +7892,10 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
|
||||
decl->where_clauses_evaluated = true;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
CallArgumentData data = {};
|
||||
data.result_type = t_invalid;
|
||||
return data;
|
||||
@@ -8147,6 +8380,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
||||
}
|
||||
|
||||
Type *pt = base_type(proc_type);
|
||||
if (pt == t_invalid) {
|
||||
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
|
||||
pt = type_of_expr(operand->expr->CallExpr.proc);
|
||||
}
|
||||
if (pt == t_invalid && data.gen_entity) {
|
||||
pt = data.gen_entity->type;
|
||||
}
|
||||
}
|
||||
|
||||
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
|
||||
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
|
||||
@@ -8175,7 +8416,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
||||
}
|
||||
|
||||
switch (inlining) {
|
||||
case ProcInlining_inline: {
|
||||
case ProcInlining_inline:
|
||||
if (proc != nullptr) {
|
||||
Entity *e = entity_from_expr(proc);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
@@ -8189,16 +8430,31 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ProcInlining_no_inline:
|
||||
break;
|
||||
}
|
||||
|
||||
operand->expr = call;
|
||||
|
||||
if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
|
||||
operand->mode = Addressing_OptionalOk;
|
||||
{
|
||||
if (proc_type == t_invalid) {
|
||||
// gb_printf_err("%s\n", expr_to_string(operand->expr));
|
||||
}
|
||||
Type *type = nullptr;
|
||||
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
|
||||
type = type_of_expr(operand->expr->CallExpr.proc);
|
||||
}
|
||||
if (type == nullptr) {
|
||||
type = pt;
|
||||
}
|
||||
type = base_type(type);
|
||||
if (type->kind == Type_Proc && type->Proc.optional_ok) {
|
||||
operand->mode = Addressing_OptionalOk;
|
||||
operand->type = type->Proc.results->Tuple.variables[0]->type;
|
||||
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
|
||||
operand->expr->CallExpr.optional_ok_one = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
|
||||
@@ -9179,9 +9435,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
if (!is_constant) {
|
||||
error(node, "Expected all constant elements for a simd vector");
|
||||
}
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
error(node, "Compound literals are not allowed with intrinsics.x86_mmx");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9822,7 +10075,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
return kind;
|
||||
}
|
||||
if (type_hint) {
|
||||
Type *type = type_of_expr(ac->expr);
|
||||
check_cast(c, o, type_hint);
|
||||
if (is_type_typed(type) && are_types_identical(type, type_hint)) {
|
||||
if (build_context.vet) {
|
||||
error(node, "Redundant 'auto_cast' applied to expression");
|
||||
} else {
|
||||
warning(node, "Redundant 'auto_cast' applied to expression");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
o->expr = node;
|
||||
return Expr_Expr;
|
||||
@@ -10563,7 +10825,7 @@ void check_not_tuple(CheckerContext *c, Operand *o) {
|
||||
if (o->type->kind == Type_Tuple) {
|
||||
isize count = o->type->Tuple.variables.count;
|
||||
error(o->expr,
|
||||
"%td-valued tuple found where single value expected", count);
|
||||
"%td-valued expression found where single value expected", count);
|
||||
o->mode = Addressing_Invalid;
|
||||
GB_ASSERT(count != 1);
|
||||
}
|
||||
|
||||
+16
-4
@@ -10,7 +10,19 @@ bool is_diverging_stmt(Ast *stmt) {
|
||||
String name = expr->CallExpr.proc->BasicDirective.name;
|
||||
return name == "panic";
|
||||
}
|
||||
Type *t = type_of_expr(expr->CallExpr.proc);
|
||||
Ast *proc = unparen_expr(expr->CallExpr.proc);
|
||||
TypeAndValue tv = proc->tav;
|
||||
if (tv.mode == Addressing_Builtin) {
|
||||
Entity *e = entity_of_node(proc);
|
||||
BuiltinProcId id = BuiltinProc_Invalid;
|
||||
if (e != nullptr) {
|
||||
id = cast(BuiltinProcId)e->Builtin.id;
|
||||
} else {
|
||||
id = BuiltinProc_DIRECTIVE;
|
||||
}
|
||||
return builtin_procs[id].diverging;
|
||||
}
|
||||
Type *t = tv.type;
|
||||
t = base_type(t);
|
||||
return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
|
||||
}
|
||||
@@ -1751,7 +1763,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
Type *cond_type = t->Tuple.variables[count-1]->type;
|
||||
if (!is_type_boolean(cond_type)) {
|
||||
gbString s = type_to_string(cond_type);
|
||||
error(operand.expr, "The final type of %td-valued tuple must be a boolean, got %s", count, s);
|
||||
error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
@@ -1762,14 +1774,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
|
||||
if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
|
||||
error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
|
||||
error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
+35
-7
@@ -2101,8 +2101,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type_Pointer: break;
|
||||
case Type_Proc: break; // NOTE(bill): Just a pointer
|
||||
case Type_Pointer:
|
||||
if (is_type_struct(bt->Pointer.elem)) {
|
||||
// Force to a raw pointer
|
||||
new_type = t_rawptr;
|
||||
}
|
||||
break;
|
||||
case Type_Proc:
|
||||
new_type = t_rawptr;
|
||||
break; // NOTE(bill): Just a pointer
|
||||
|
||||
// Odin specific
|
||||
case Type_Slice:
|
||||
@@ -2194,6 +2201,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
|
||||
return new_type;
|
||||
}
|
||||
|
||||
if (is_type_pointer(single_type)) {
|
||||
// NOTE(bill): Force a cast to prevent a possible type cycle
|
||||
return t_rawptr;
|
||||
}
|
||||
|
||||
if (build_context.ODIN_OS == "windows") {
|
||||
if (build_context.ODIN_ARCH == "amd64") {
|
||||
@@ -2450,6 +2461,24 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pt->tags & ProcTag_optional_second) {
|
||||
if (optional_ok) {
|
||||
error(proc_type_node, "A procedure type cannot have both an #optional_ok tag and #optional_second");
|
||||
}
|
||||
optional_ok = true;
|
||||
if (result_count != 2) {
|
||||
error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
|
||||
} else {
|
||||
bool ok = false;
|
||||
if (proc_type_node->file && proc_type_node->file->pkg) {
|
||||
ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
error(proc_type_node, "A procedure type with the #optional_second may only be allowed within 'package runtime'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type->Proc.node = proc_type_node;
|
||||
type->Proc.scope = c->scope;
|
||||
@@ -2590,12 +2619,11 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
|
||||
}
|
||||
|
||||
Type *make_optional_ok_type(Type *value, bool typed) {
|
||||
// LEAK TODO(bill): probably don't reallocate everything here and reuse the same one for the same type if possible
|
||||
gbAllocator a = heap_allocator();
|
||||
gbAllocator a = permanent_allocator();
|
||||
Type *t = alloc_type_tuple();
|
||||
array_init(&t->Tuple.variables, a, 0, 2);
|
||||
array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value, false, 0));
|
||||
array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1));
|
||||
array_init(&t->Tuple.variables, a, 2);
|
||||
t->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, value, false, 0);
|
||||
t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
+40
-20
@@ -783,15 +783,6 @@ void init_universal(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bill): Set the correct arch for this
|
||||
if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) {
|
||||
t_vector_x86_mmx = alloc_type(Type_SimdVector);
|
||||
t_vector_x86_mmx->SimdVector.is_x86_mmx = true;
|
||||
|
||||
Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx);
|
||||
add_global_entity(entity, intrinsics_pkg->scope);
|
||||
}
|
||||
|
||||
bool defined_values_double_declaration = false;
|
||||
for_array(i, bc->defined_values.entries) {
|
||||
char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key;
|
||||
@@ -1782,20 +1773,28 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||
str_lit("memory_equal"),
|
||||
str_lit("memory_compare"),
|
||||
str_lit("memory_compare_zero"),
|
||||
|
||||
str_lit("bswap_16"),
|
||||
str_lit("bswap_32"),
|
||||
str_lit("bswap_64"),
|
||||
str_lit("bswap_128"),
|
||||
|
||||
str_lit("bswap_f16"),
|
||||
str_lit("bswap_f32"),
|
||||
str_lit("bswap_f64"),
|
||||
};
|
||||
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]);
|
||||
}
|
||||
|
||||
if (!build_context.use_llvm_api) {
|
||||
String other_required_runtime_entities[] = {
|
||||
str_lit("bswap_16"),
|
||||
str_lit("bswap_32"),
|
||||
str_lit("bswap_64"),
|
||||
str_lit("bswap_128"),
|
||||
|
||||
str_lit("bswap_f16"),
|
||||
str_lit("bswap_f32"),
|
||||
str_lit("bswap_f64"),
|
||||
};
|
||||
|
||||
for (isize i = 0; i < gb_count_of(other_required_runtime_entities); i++) {
|
||||
force_add_dependency_entity(c, c->info.runtime_package->scope, other_required_runtime_entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.no_crt) {
|
||||
String required_no_crt_entities[] = {
|
||||
// NOTE(bill): Only if these exist
|
||||
@@ -2190,8 +2189,6 @@ Type *check_poly_path_pop(CheckerContext *c) {
|
||||
|
||||
|
||||
|
||||
void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
|
||||
|
||||
Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) {
|
||||
Array<Entity *> procs = {};
|
||||
if (o.mode == Addressing_ProcGroup) {
|
||||
@@ -2574,6 +2571,29 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (name == "optimization_mode") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
String mode = ev.value_string;
|
||||
if (mode == "none") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_None;
|
||||
} else if (mode == "minimal") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Minimal;
|
||||
} else if (mode == "size") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Size;
|
||||
} else if (mode == "speed") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Speed;
|
||||
} else {
|
||||
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
|
||||
error_line("\tnone\n");
|
||||
error_line("\tminimal\n");
|
||||
error_line("\tsize\n");
|
||||
error_line("\tspeed\n");
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected a string for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ struct BuiltinProc {
|
||||
bool variadic;
|
||||
ExprKind kind;
|
||||
BuiltinProcPkg pkg;
|
||||
bool diverging;
|
||||
};
|
||||
|
||||
|
||||
@@ -112,6 +113,7 @@ struct AttributeContext {
|
||||
String thread_local_model;
|
||||
String deprecated_message;
|
||||
DeferredProcedure deferred_procedure;
|
||||
u32 optimization_mode; // ProcedureOptimizationMode
|
||||
struct TypeAtomOpTable *atom_op_table;
|
||||
};
|
||||
|
||||
@@ -396,6 +398,10 @@ void check_add_import_decl(CheckerContext *c, Ast *decl);
|
||||
void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
|
||||
|
||||
|
||||
void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
|
||||
void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
|
||||
void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
|
||||
|
||||
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
|
||||
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes);
|
||||
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
||||
|
||||
@@ -41,6 +41,18 @@ enum BuiltinProcId {
|
||||
|
||||
BuiltinProc_alloca,
|
||||
BuiltinProc_cpu_relax,
|
||||
BuiltinProc_trap,
|
||||
BuiltinProc_debug_trap,
|
||||
BuiltinProc_read_cycle_counter,
|
||||
|
||||
BuiltinProc_count_ones,
|
||||
BuiltinProc_trailing_zeros,
|
||||
BuiltinProc_reverse_bits,
|
||||
BuiltinProc_byte_swap,
|
||||
|
||||
BuiltinProc_overflow_add,
|
||||
BuiltinProc_overflow_sub,
|
||||
BuiltinProc_overflow_mul,
|
||||
|
||||
BuiltinProc_volatile_store,
|
||||
BuiltinProc_volatile_load,
|
||||
@@ -122,6 +134,7 @@ enum BuiltinProcId {
|
||||
BuiltinProc_fixed_point_mul_sat,
|
||||
BuiltinProc_fixed_point_div_sat,
|
||||
|
||||
BuiltinProc_expect,
|
||||
|
||||
// Constant type tests
|
||||
|
||||
@@ -246,6 +259,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/true},
|
||||
{STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
|
||||
{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("overflow_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
@@ -327,6 +353,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("fixed_point_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
@@ -114,10 +114,6 @@ enum OdinDocTypeFlag_BitSet : u32 {
|
||||
OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4,
|
||||
};
|
||||
|
||||
enum OdinDocTypeFlag_SimdVector : u32 {
|
||||
OdinDocTypeFlag_BitSet_x86_mmx = 1<<1,
|
||||
};
|
||||
|
||||
enum {
|
||||
// constants
|
||||
OdinDocType_ElemsCap = 4,
|
||||
|
||||
+3
-7
@@ -724,13 +724,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
|
||||
break;
|
||||
case Type_SimdVector:
|
||||
doc_type.kind = OdinDocType_SimdVector;
|
||||
if (type->SimdVector.is_x86_mmx) {
|
||||
doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx;
|
||||
} else {
|
||||
doc_type.elem_count_len = 1;
|
||||
doc_type.elem_counts[0] = type->SimdVector.count;
|
||||
doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
|
||||
}
|
||||
doc_type.elem_count_len = 1;
|
||||
doc_type.elem_counts[0] = type->SimdVector.count;
|
||||
doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
|
||||
// TODO(bill):
|
||||
break;
|
||||
case Type_RelativePointer:
|
||||
|
||||
+40
-26
@@ -33,39 +33,41 @@ String const entity_strings[] = {
|
||||
};
|
||||
|
||||
enum EntityFlag : u64 {
|
||||
EntityFlag_Visited = 1<<0,
|
||||
EntityFlag_Used = 1<<1,
|
||||
EntityFlag_Using = 1<<2,
|
||||
EntityFlag_Field = 1<<3,
|
||||
EntityFlag_Param = 1<<4,
|
||||
EntityFlag_Result = 1<<5,
|
||||
EntityFlag_ArrayElem = 1<<6,
|
||||
EntityFlag_Ellipsis = 1<<7,
|
||||
EntityFlag_NoAlias = 1<<8,
|
||||
EntityFlag_TypeField = 1<<9,
|
||||
EntityFlag_Value = 1<<10,
|
||||
EntityFlag_Sret = 1<<11,
|
||||
EntityFlag_ByVal = 1<<12,
|
||||
EntityFlag_BitFieldValue = 1<<13,
|
||||
EntityFlag_PolyConst = 1<<14,
|
||||
EntityFlag_NotExported = 1<<15,
|
||||
EntityFlag_ConstInput = 1<<16,
|
||||
EntityFlag_Visited = 1ull<<0,
|
||||
EntityFlag_Used = 1ull<<1,
|
||||
EntityFlag_Using = 1ull<<2,
|
||||
EntityFlag_Field = 1ull<<3,
|
||||
EntityFlag_Param = 1ull<<4,
|
||||
EntityFlag_Result = 1ull<<5,
|
||||
EntityFlag_ArrayElem = 1ull<<6,
|
||||
EntityFlag_Ellipsis = 1ull<<7,
|
||||
EntityFlag_NoAlias = 1ull<<8,
|
||||
EntityFlag_TypeField = 1ull<<9,
|
||||
EntityFlag_Value = 1ull<<10,
|
||||
EntityFlag_Sret = 1ull<<11,
|
||||
EntityFlag_ByVal = 1ull<<12,
|
||||
EntityFlag_BitFieldValue = 1ull<<13,
|
||||
EntityFlag_PolyConst = 1ull<<14,
|
||||
EntityFlag_NotExported = 1ull<<15,
|
||||
EntityFlag_ConstInput = 1ull<<16,
|
||||
|
||||
EntityFlag_Static = 1<<17,
|
||||
EntityFlag_Static = 1ull<<17,
|
||||
|
||||
EntityFlag_ImplicitReference = 1<<18, // NOTE(bill): equivalent to `const &` in C++
|
||||
EntityFlag_ImplicitReference = 1ull<<18, // NOTE(bill): equivalent to `const &` in C++
|
||||
|
||||
EntityFlag_SoaPtrField = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
|
||||
EntityFlag_SoaPtrField = 1ull<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice
|
||||
|
||||
EntityFlag_ProcBodyChecked = 1<<20,
|
||||
EntityFlag_ProcBodyChecked = 1ull<<20,
|
||||
|
||||
EntityFlag_CVarArg = 1<<21,
|
||||
EntityFlag_AutoCast = 1<<22,
|
||||
EntityFlag_CVarArg = 1ull<<21,
|
||||
EntityFlag_AutoCast = 1ull<<22,
|
||||
|
||||
EntityFlag_Disabled = 1<<24,
|
||||
EntityFlag_Cold = 1<<25, // procedure is rarely called
|
||||
EntityFlag_Disabled = 1ull<<24,
|
||||
EntityFlag_Cold = 1ull<<25, // procedure is rarely called
|
||||
|
||||
EntityFlag_Test = 1<<30,
|
||||
EntityFlag_Test = 1ull<<30,
|
||||
|
||||
EntityFlag_Overridden = 1ull<<63,
|
||||
|
||||
};
|
||||
|
||||
@@ -97,6 +99,14 @@ enum EntityConstantFlags : u32 {
|
||||
EntityConstantFlag_ImplicitEnumValue = 1<<0,
|
||||
};
|
||||
|
||||
enum ProcedureOptimizationMode : u32 {
|
||||
ProcedureOptimizationMode_Default,
|
||||
ProcedureOptimizationMode_None,
|
||||
ProcedureOptimizationMode_Minimal,
|
||||
ProcedureOptimizationMode_Size,
|
||||
ProcedureOptimizationMode_Speed,
|
||||
};
|
||||
|
||||
// An Entity is a named "thing" in the language
|
||||
struct Entity {
|
||||
EntityKind kind;
|
||||
@@ -125,6 +135,9 @@ struct Entity {
|
||||
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
||||
// later entity kinds
|
||||
union {
|
||||
struct {
|
||||
u8 start;
|
||||
} Dummy;
|
||||
struct {
|
||||
ExactValue value;
|
||||
ParameterValue param_value;
|
||||
@@ -160,6 +173,7 @@ struct Entity {
|
||||
DeferredProcedure deferred_procedure;
|
||||
bool is_foreign;
|
||||
bool is_export;
|
||||
ProcedureOptimizationMode optimization_mode;
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
|
||||
+2
-2
@@ -491,8 +491,8 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
|
||||
#define USIZE_MIX U32_MIN
|
||||
#define USIZE_MAX U32_MAX
|
||||
|
||||
#define ISIZE_MIX S32_MIN
|
||||
#define ISIZE_MAX S32_MAX
|
||||
#define ISIZE_MIX I32_MIN
|
||||
#define ISIZE_MAX I32_MAX
|
||||
#elif defined(GB_ARCH_64_BIT)
|
||||
#define USIZE_MIX U64_MIN
|
||||
#define USIZE_MAX U64_MAX
|
||||
|
||||
+5
-8
@@ -8257,7 +8257,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ac, AutoCast, expr);
|
||||
return ir_build_expr(proc, ac->expr);
|
||||
irValue *value = ir_build_expr(proc, ac->expr);
|
||||
return ir_emit_conv(proc, value, tv.type);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ue, UnaryExpr, expr);
|
||||
@@ -12564,13 +12565,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
case Type_SimdVector:
|
||||
ir_emit_comment(proc, str_lit("Type_SimdVector"));
|
||||
tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr);
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true);
|
||||
} else {
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
|
||||
}
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count));
|
||||
break;
|
||||
|
||||
case Type_RelativePointer:
|
||||
|
||||
+3
-7
@@ -624,13 +624,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
}
|
||||
|
||||
case Type_SimdVector:
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
ir_write_str_lit(f, "x86_mmx");
|
||||
} else {
|
||||
ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
|
||||
ir_print_type(f, m, t->SimdVector.elem);
|
||||
ir_write_byte(f, '>');
|
||||
}
|
||||
ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
|
||||
ir_print_type(f, m, t->SimdVector.elem);
|
||||
ir_write_byte(f, '>');
|
||||
return;
|
||||
|
||||
case Type_RelativePointer:
|
||||
|
||||
+354
-116
@@ -391,11 +391,37 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
||||
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
||||
return;
|
||||
} else if (addr.kind == lbAddr_Context) {
|
||||
lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p));
|
||||
lbAddr next_addr = lb_add_local_generated(p, t_context, true);
|
||||
lb_addr_store(p, next_addr, old);
|
||||
lb_push_context_onto_stack(p, next_addr);
|
||||
lbValue next = lb_addr_get_ptr(p, next_addr);
|
||||
lbAddr old_addr = lb_find_or_generate_context_ptr(p);
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-22): reuse unused 'context' variables to minimize stack usage
|
||||
// This has to be done manually since the optimizer cannot determine when this is possible
|
||||
bool create_new = true;
|
||||
for_array(i, p->context_stack) {
|
||||
lbContextData *ctx_data = &p->context_stack[i];
|
||||
if (ctx_data->ctx.addr.value == old_addr.addr.value) {
|
||||
if (ctx_data->uses > 0) {
|
||||
create_new = true;
|
||||
} else if (p->scope_index > ctx_data->scope_index) {
|
||||
create_new = true;
|
||||
} else {
|
||||
// gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses);
|
||||
create_new = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lbValue next = {};
|
||||
if (create_new) {
|
||||
lbValue old = lb_addr_load(p, old_addr);
|
||||
lbAddr next_addr = lb_add_local_generated(p, t_context, true);
|
||||
lb_addr_store(p, next_addr, old);
|
||||
lb_push_context_onto_stack(p, next_addr);
|
||||
next = next_addr.addr;
|
||||
} else {
|
||||
next = old_addr.addr;
|
||||
}
|
||||
|
||||
if (addr.ctx.sel.index.count > 0) {
|
||||
lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel);
|
||||
@@ -623,6 +649,13 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
}
|
||||
} else if (addr.kind == lbAddr_Context) {
|
||||
lbValue a = addr.addr;
|
||||
for_array(i, p->context_stack) {
|
||||
lbContextData *ctx_data = &p->context_stack[i];
|
||||
if (ctx_data->ctx.addr.value == a.value) {
|
||||
ctx_data->uses += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), "");
|
||||
|
||||
if (addr.ctx.sel.index.count > 0) {
|
||||
@@ -1445,9 +1478,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
case Type_SimdVector:
|
||||
if (type->SimdVector.is_x86_mmx) {
|
||||
return LLVMX86MMXTypeInContext(ctx);
|
||||
}
|
||||
return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
|
||||
|
||||
case Type_RelativePointer:
|
||||
@@ -1899,9 +1929,6 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
break;
|
||||
|
||||
case Type_SimdVector:
|
||||
if (type->SimdVector.is_x86_mmx) {
|
||||
return LLVMDIBuilderCreateVectorType(m->debug_builder, 2, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, t_f64), nullptr, 0);
|
||||
}
|
||||
return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
|
||||
|
||||
case Type_RelativePointer: {
|
||||
@@ -2523,7 +2550,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
||||
p->type = entity->type;
|
||||
p->type_expr = decl->type_expr;
|
||||
p->body = pl->body;
|
||||
p->inlining = ProcInlining_none;
|
||||
p->inlining = pl->inlining;
|
||||
p->is_foreign = entity->Procedure.is_foreign;
|
||||
p->is_export = entity->Procedure.is_export;
|
||||
p->is_entry_point = false;
|
||||
@@ -2558,9 +2585,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
||||
LLVMSetFunctionCallConv(p->value, cc_kind);
|
||||
}
|
||||
|
||||
if (entity->flags & EntityFlag_Cold) {
|
||||
lb_add_attribute_to_proc(m, p->value, "cold");
|
||||
}
|
||||
|
||||
if (pt->Proc.diverging) {
|
||||
lb_add_attribute_to_proc(m, p->value, "noreturn");
|
||||
@@ -2575,6 +2599,28 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entity->flags & EntityFlag_Cold) {
|
||||
lb_add_attribute_to_proc(m, p->value, "cold");
|
||||
}
|
||||
|
||||
switch (entity->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
lb_add_attribute_to_proc(m, p->value, "optnone");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
lb_add_attribute_to_proc(m, p->value, "optnone");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Size:
|
||||
lb_add_attribute_to_proc(m, p->value, "optsize");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Speed:
|
||||
// TODO(bill): handle this correctly
|
||||
lb_add_attribute_to_proc(m, p->value, "optsize");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
||||
// // TODO(bill): Clean up this logic
|
||||
// if (build_context.metrics.os != TargetOs_js) {
|
||||
@@ -2624,26 +2670,16 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
||||
for (isize i = 0; i < pt->Proc.param_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
Type *original_type = e->type;
|
||||
Type *abi_type = pt->Proc.abi_compat_params[i];
|
||||
if (e->kind != Entity_Variable) continue;
|
||||
|
||||
if (i+1 == params->variables.count && pt->Proc.c_vararg) {
|
||||
continue;
|
||||
}
|
||||
if (is_type_tuple(abi_type)) {
|
||||
for_array(j, abi_type->Tuple.variables) {
|
||||
Type *tft = abi_type->Tuple.variables[j]->type;
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias");
|
||||
}
|
||||
}
|
||||
parameter_index += abi_type->Tuple.variables.count;
|
||||
} else {
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
|
||||
}
|
||||
parameter_index += 1;
|
||||
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
|
||||
}
|
||||
parameter_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7567,6 +7603,7 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p
|
||||
lbContextData *cd = array_add_and_get(&p->context_stack);
|
||||
cd->ctx = ctx_addr;
|
||||
cd->scope_index = -1;
|
||||
cd->uses = +1; // make sure it has been used already
|
||||
return cd;
|
||||
}
|
||||
|
||||
@@ -9031,6 +9068,145 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
}
|
||||
return {};
|
||||
|
||||
|
||||
case BuiltinProc_debug_trap:
|
||||
case BuiltinProc_trap:
|
||||
{
|
||||
char const *name = nullptr;
|
||||
switch (id) {
|
||||
case BuiltinProc_debug_trap: name = "llvm.debugtrap"; break;
|
||||
case BuiltinProc_trap: name = "llvm.trap"; break;
|
||||
}
|
||||
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
|
||||
|
||||
LLVMBuildCall(p->builder, ip, nullptr, 0, "");
|
||||
if (id == BuiltinProc_trap) {
|
||||
LLVMBuildUnreachable(p->builder);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
case BuiltinProc_read_cycle_counter:
|
||||
{
|
||||
char const *name = "llvm.readcyclecounter";
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
|
||||
res.type = tv.type;
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_trailing_zeros:
|
||||
{
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
x = lb_emit_conv(p, x, tv.type);
|
||||
|
||||
char const *name = "llvm.cttz";
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
|
||||
|
||||
LLVMValueRef args[2] = {};
|
||||
args[0] = x.value;
|
||||
args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx));
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
res.type = tv.type;
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_count_ones:
|
||||
case BuiltinProc_reverse_bits:
|
||||
{
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
x = lb_emit_conv(p, x, tv.type);
|
||||
|
||||
char const *name = nullptr;
|
||||
switch (id) {
|
||||
case BuiltinProc_count_ones: name = "llvm.ctpop"; break;
|
||||
case BuiltinProc_reverse_bits: name = "llvm.bitreverse"; break;
|
||||
}
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, tv.type)};
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
|
||||
|
||||
LLVMValueRef args[1] = {};
|
||||
args[0] = x.value;
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
res.type = tv.type;
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_byte_swap:
|
||||
{
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
x = lb_emit_conv(p, x, tv.type);
|
||||
return lb_emit_byte_swap(p, x, tv.type);
|
||||
}
|
||||
|
||||
case BuiltinProc_overflow_add:
|
||||
case BuiltinProc_overflow_sub:
|
||||
case BuiltinProc_overflow_mul:
|
||||
{
|
||||
Type *tuple = tv.type;
|
||||
GB_ASSERT(is_type_tuple(tuple));
|
||||
Type *type = tuple->Tuple.variables[0]->type;
|
||||
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
lbValue y = lb_build_expr(p, ce->args[1]);
|
||||
x = lb_emit_conv(p, x, type);
|
||||
y = lb_emit_conv(p, y, type);
|
||||
|
||||
char const *name = nullptr;
|
||||
if (is_type_unsigned(type)) {
|
||||
switch (id) {
|
||||
case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break;
|
||||
case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break;
|
||||
case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break;
|
||||
}
|
||||
} else {
|
||||
switch (id) {
|
||||
case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break;
|
||||
case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break;
|
||||
case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, type)};
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
|
||||
|
||||
LLVMValueRef args[2] = {};
|
||||
args[0] = x.value;
|
||||
args[1] = y.value;
|
||||
|
||||
Type *res_type = nullptr;
|
||||
{
|
||||
gbAllocator a = permanent_allocator();
|
||||
res_type = alloc_type_tuple();
|
||||
array_init(&res_type->Tuple.variables, a, 2);
|
||||
res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0);
|
||||
res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1);
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
res.type = res_type;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
case BuiltinProc_atomic_fence:
|
||||
LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
|
||||
return {};
|
||||
@@ -9304,6 +9480,30 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
res.type = platform_type;
|
||||
return lb_emit_conv(p, res, tv.type);
|
||||
}
|
||||
|
||||
case BuiltinProc_expect:
|
||||
{
|
||||
Type *t = default_type(tv.type);
|
||||
lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t);
|
||||
lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t);
|
||||
|
||||
char const *name = "llvm.expect";
|
||||
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, t)};
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
|
||||
|
||||
lbValue res = {};
|
||||
|
||||
LLVMValueRef args[2] = {};
|
||||
args[0] = x.value;
|
||||
args[1] = y.value;
|
||||
|
||||
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
res.type = t;
|
||||
return lb_emit_conv(p, res, t);
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
|
||||
@@ -9692,6 +9892,7 @@ bool lb_is_const_nil(lbValue value) {
|
||||
|
||||
String lb_get_const_string(lbModule *m, lbValue value) {
|
||||
GB_ASSERT(lb_is_const(value));
|
||||
GB_ASSERT(LLVMIsConstant(value.value));
|
||||
|
||||
Type *t = base_type(value.type);
|
||||
GB_ASSERT(are_types_identical(t, t_string));
|
||||
@@ -9743,43 +9944,43 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) {
|
||||
return found->value;
|
||||
}
|
||||
|
||||
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
|
||||
Type *vt = core_type(value.type);
|
||||
GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
|
||||
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
|
||||
GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
|
||||
|
||||
// TODO(bill): lb_emit_byte_swap
|
||||
lbValue res = {};
|
||||
res.type = platform_type;
|
||||
res.value = value.value;
|
||||
|
||||
int sz = cast(int)type_size_of(vt);
|
||||
if (sz > 1) {
|
||||
if (is_type_float(platform_type)) {
|
||||
String name = {};
|
||||
switch (sz) {
|
||||
case 2: name = str_lit("bswap_f16"); break;
|
||||
case 4: name = str_lit("bswap_f32"); break;
|
||||
case 8: name = str_lit("bswap_f64"); break;
|
||||
default: GB_PANIC("unhandled byteswap size"); break;
|
||||
}
|
||||
LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
|
||||
res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
|
||||
} else {
|
||||
GB_ASSERT(is_type_integer(platform_type));
|
||||
String name = {};
|
||||
switch (sz) {
|
||||
case 2: name = str_lit("bswap_16"); break;
|
||||
case 4: name = str_lit("bswap_32"); break;
|
||||
case 8: name = str_lit("bswap_64"); break;
|
||||
case 16: name = str_lit("bswap_128"); break;
|
||||
default: GB_PANIC("unhandled byteswap size"); break;
|
||||
}
|
||||
LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
|
||||
|
||||
res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
|
||||
}
|
||||
if (type_size_of(value.type) < 2) {
|
||||
return value;
|
||||
}
|
||||
|
||||
Type *original_type = value.type;
|
||||
if (is_type_float(original_type)) {
|
||||
i64 sz = type_size_of(original_type);
|
||||
Type *integer_type = nullptr;
|
||||
switch (sz) {
|
||||
case 2: integer_type = t_u16; break;
|
||||
case 4: integer_type = t_u32; break;
|
||||
case 8: integer_type = t_u64; break;
|
||||
}
|
||||
GB_ASSERT(integer_type != nullptr);
|
||||
value = lb_emit_transmute(p, value, integer_type);
|
||||
}
|
||||
|
||||
char const *name = "llvm.bswap";
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, value.type)};
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
|
||||
|
||||
LLVMValueRef args[1] = {};
|
||||
args[0] = value.value;
|
||||
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
res.type = value.type;
|
||||
|
||||
if (is_type_float(original_type)) {
|
||||
res = lb_emit_transmute(p, res, original_type);
|
||||
}
|
||||
res.type = end_type;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -10963,7 +11164,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ac, AutoCast, expr);
|
||||
return lb_build_expr(p, ac->expr);
|
||||
lbValue value = lb_build_expr(p, ac->expr);
|
||||
return lb_emit_conv(p, value, tv.type);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ue, UnaryExpr, expr);
|
||||
@@ -13355,15 +13557,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
|
||||
|
||||
LLVMValueRef vals[4] = {};
|
||||
LLVMValueRef vals[3] = {};
|
||||
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
vals[3] = lb_const_bool(m, t_bool, true).value;
|
||||
} else {
|
||||
vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
|
||||
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
|
||||
vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
|
||||
}
|
||||
vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
|
||||
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
|
||||
vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
|
||||
|
||||
lbValue res = {};
|
||||
res.type = type_deref(tag.type);
|
||||
@@ -13844,11 +14042,28 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
|
||||
|
||||
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
|
||||
LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod);
|
||||
LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod);
|
||||
LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod);
|
||||
defer (LLVMDisposePassManager(default_function_pass_manager));
|
||||
defer (LLVMDisposePassManager(function_pass_manager_minimal));
|
||||
defer (LLVMDisposePassManager(function_pass_manager_size));
|
||||
defer (LLVMDisposePassManager(function_pass_manager_speed));
|
||||
|
||||
LLVMInitializeFunctionPassManager(default_function_pass_manager);
|
||||
LLVMInitializeFunctionPassManager(function_pass_manager_minimal);
|
||||
LLVMInitializeFunctionPassManager(function_pass_manager_size);
|
||||
LLVMInitializeFunctionPassManager(function_pass_manager_speed);
|
||||
|
||||
lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level);
|
||||
lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0);
|
||||
lb_populate_function_pass_manager_specific(function_pass_manager_size, 1);
|
||||
lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2);
|
||||
|
||||
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
|
||||
LLVMFinalizeFunctionPassManager(function_pass_manager_minimal);
|
||||
LLVMFinalizeFunctionPassManager(function_pass_manager_size);
|
||||
LLVMFinalizeFunctionPassManager(function_pass_manager_speed);
|
||||
|
||||
|
||||
LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod);
|
||||
@@ -13988,6 +14203,47 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
}*/
|
||||
}
|
||||
|
||||
String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
|
||||
|
||||
TIME_SECTION("LLVM Procedure Generation");
|
||||
for_array(i, m->procedures_to_generate) {
|
||||
lbProcedure *p = m->procedures_to_generate[i];
|
||||
if (p->is_done) {
|
||||
continue;
|
||||
}
|
||||
if (p->body != nullptr) { // Build Procedure
|
||||
m->curr_procedure = p;
|
||||
lb_begin_procedure_body(p);
|
||||
lb_build_stmt(p, p->body);
|
||||
lb_end_procedure_body(p);
|
||||
p->is_done = true;
|
||||
m->curr_procedure = nullptr;
|
||||
}
|
||||
lb_end_procedure(p);
|
||||
|
||||
// Add Flags
|
||||
if (p->body != nullptr) {
|
||||
if (p->name == "memcpy" || p->name == "memmove" ||
|
||||
p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
|
||||
string_starts_with(p->name, str_lit("llvm.memcpy")) ||
|
||||
string_starts_with(p->name, str_lit("llvm.memmove"))) {
|
||||
p->flags |= lbProcedureFlag_WithoutMemcpyPass;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
|
||||
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
|
||||
LLVMDumpValue(p->value);
|
||||
gb_printf_err("\n\n\n\n");
|
||||
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
|
||||
gb_printf_err("LLVM Error: %s\n", llvm_error);
|
||||
}
|
||||
LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
|
||||
TIME_SECTION("LLVM DLL main");
|
||||
|
||||
@@ -14075,7 +14331,7 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
} else {
|
||||
lbValue *found = map_get(&m->values, hash_entity(entry_point));
|
||||
GB_ASSERT(found != nullptr);
|
||||
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
|
||||
lb_emit_call(p, *found, {});
|
||||
}
|
||||
|
||||
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
|
||||
@@ -14093,47 +14349,6 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
}
|
||||
|
||||
|
||||
String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll"));
|
||||
|
||||
TIME_SECTION("LLVM Procedure Generation");
|
||||
for_array(i, m->procedures_to_generate) {
|
||||
lbProcedure *p = m->procedures_to_generate[i];
|
||||
if (p->is_done) {
|
||||
continue;
|
||||
}
|
||||
if (p->body != nullptr) { // Build Procedure
|
||||
m->curr_procedure = p;
|
||||
lb_begin_procedure_body(p);
|
||||
lb_build_stmt(p, p->body);
|
||||
lb_end_procedure_body(p);
|
||||
p->is_done = true;
|
||||
m->curr_procedure = nullptr;
|
||||
}
|
||||
lb_end_procedure(p);
|
||||
|
||||
// Add Flags
|
||||
if (p->body != nullptr) {
|
||||
if (p->name == "memcpy" || p->name == "memmove" ||
|
||||
p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
|
||||
string_starts_with(p->name, str_lit("llvm.memcpy")) ||
|
||||
string_starts_with(p->name, str_lit("llvm.memmove"))) {
|
||||
p->flags |= lbProcedureFlag_WithoutMemcpyPass;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
|
||||
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
|
||||
LLVMDumpValue(p->value);
|
||||
gb_printf_err("\n\n\n\n");
|
||||
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
|
||||
gb_printf_err("LLVM Error: %s\n", llvm_error);
|
||||
}
|
||||
LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m->debug_builder != nullptr) {
|
||||
TIME_SECTION("LLVM Debug Info Complete Types");
|
||||
lb_debug_complete_types(m);
|
||||
@@ -14158,7 +14373,25 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
|
||||
LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
|
||||
} else {
|
||||
LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
|
||||
if (p->entity && p->entity->kind == Entity_Procedure) {
|
||||
switch (p->entity->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value);
|
||||
break;
|
||||
case ProcedureOptimizationMode_Size:
|
||||
LLVMRunFunctionPassManager(function_pass_manager_size, p->value);
|
||||
break;
|
||||
case ProcedureOptimizationMode_Speed:
|
||||
LLVMRunFunctionPassManager(function_pass_manager_speed, p->value);
|
||||
break;
|
||||
default:
|
||||
LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14220,13 +14453,18 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
return;
|
||||
}
|
||||
llvm_error = nullptr;
|
||||
if (build_context.keep_temp_files) {
|
||||
if (build_context.keep_temp_files ||
|
||||
build_context.build_mode == BuildMode_LLVM_IR) {
|
||||
TIME_SECTION("LLVM Print Module to File");
|
||||
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
|
||||
gb_printf_err("LLVM Error: %s\n", llvm_error);
|
||||
gb_exit(1);
|
||||
return;
|
||||
}
|
||||
if (build_context.build_mode == BuildMode_LLVM_IR) {
|
||||
gb_exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("LLVM Object Generation");
|
||||
|
||||
@@ -157,6 +157,7 @@ struct lbBranchBlocks {
|
||||
struct lbContextData {
|
||||
lbAddr ctx;
|
||||
isize scope_index;
|
||||
isize uses;
|
||||
};
|
||||
|
||||
enum lbParamPasskind {
|
||||
@@ -310,7 +311,7 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
|
||||
lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
|
||||
|
||||
lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
|
||||
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type);
|
||||
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
|
||||
void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
|
||||
lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
|
||||
lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
|
||||
|
||||
@@ -1,11 +1,41 @@
|
||||
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
|
||||
void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
|
||||
void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
|
||||
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level);
|
||||
|
||||
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||
LLVMAddConstantPropagationPass(fpm);
|
||||
LLVMAddEarlyCSEPass(fpm);
|
||||
|
||||
LLVMAddConstantPropagationPass(fpm);
|
||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddCFGSimplificationPass(fpm);
|
||||
}
|
||||
|
||||
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
|
||||
// NOTE(bill): Treat -opt:3 as if it was -opt:2
|
||||
// TODO(bill): Determine which opt definitions should exist in the first place
|
||||
optimization_level = gb_clamp(optimization_level, 0, 2);
|
||||
|
||||
if (!ignore_memcpy_pass) {
|
||||
if (ignore_memcpy_pass) {
|
||||
lb_basic_populate_function_pass_manager(fpm);
|
||||
return;
|
||||
} else if (optimization_level == 0) {
|
||||
LLVMAddMemCpyOptPass(fpm);
|
||||
lb_basic_populate_function_pass_manager(fpm);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
|
||||
LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
|
||||
LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
|
||||
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
|
||||
#else
|
||||
LLVMAddMemCpyOptPass(fpm);
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||
LLVMAddConstantPropagationPass(fpm);
|
||||
@@ -16,16 +46,45 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddCFGSimplificationPass(fpm);
|
||||
|
||||
// LLVMAddSLPVectorizePass(fpm);
|
||||
// LLVMAddLoopVectorizePass(fpm);
|
||||
LLVMAddSCCPPass(fpm);
|
||||
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddUnifyFunctionExitNodesPass(fpm);
|
||||
|
||||
LLVMAddCFGSimplificationPass(fpm);
|
||||
LLVMAddEarlyCSEPass(fpm);
|
||||
LLVMAddLowerExpectIntrinsicPass(fpm);
|
||||
#endif
|
||||
}
|
||||
|
||||
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) {
|
||||
// NOTE(bill): Treat -opt:3 as if it was -opt:2
|
||||
// TODO(bill): Determine which opt definitions should exist in the first place
|
||||
optimization_level = gb_clamp(optimization_level, 0, 2);
|
||||
|
||||
// LLVMAddScalarizerPass(fpm);
|
||||
// LLVMAddLoopIdiomPass(fpm);
|
||||
if (optimization_level == 0) {
|
||||
LLVMAddMemCpyOptPass(fpm);
|
||||
lb_basic_populate_function_pass_manager(fpm);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
|
||||
LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
|
||||
LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
|
||||
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
|
||||
#else
|
||||
LLVMAddMemCpyOptPass(fpm);
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||
LLVMAddConstantPropagationPass(fpm);
|
||||
LLVMAddEarlyCSEPass(fpm);
|
||||
|
||||
LLVMAddConstantPropagationPass(fpm);
|
||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
LLVMAddCFGSimplificationPass(fpm);
|
||||
|
||||
LLVMAddSCCPPass(fpm);
|
||||
|
||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||
|
||||
+50
-1
@@ -571,6 +571,7 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_OutFile,
|
||||
BuildFlag_OptimizationLevel,
|
||||
BuildFlag_OptimizationMode,
|
||||
BuildFlag_ShowTimings,
|
||||
BuildFlag_ShowUnused,
|
||||
BuildFlag_ShowUnusedWithLocation,
|
||||
@@ -687,6 +688,8 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
|
||||
add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
|
||||
@@ -885,8 +888,36 @@ bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
case BuildFlag_OptimizationLevel:
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
if (set_flags[BuildFlag_OptimizationMode]) {
|
||||
gb_printf_err("Mixture of -opt and -o is not allowed\n");
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
|
||||
break;
|
||||
case BuildFlag_OptimizationMode:
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
if (set_flags[BuildFlag_OptimizationLevel]) {
|
||||
gb_printf_err("Mixture of -opt and -o is not allowed\n");
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value.value_string == "minimal") {
|
||||
build_context.optimization_level = 0;
|
||||
} else if (value.value_string == "size") {
|
||||
build_context.optimization_level = 1;
|
||||
} else if (value.value_string == "speed") {
|
||||
build_context.optimization_level = 2;
|
||||
} else {
|
||||
gb_printf_err("Invalid optimization mode for -o:<string>, got %.*s\n", LIT(value.value_string));
|
||||
gb_printf_err("Valid optimization modes:\n");
|
||||
gb_printf_err("\tminimal\n");
|
||||
gb_printf_err("\tsize\n");
|
||||
gb_printf_err("\tspeed\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
case BuildFlag_ShowTimings:
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
build_context.show_timings = true;
|
||||
@@ -1109,8 +1140,16 @@ bool parse_build_flags(Array<String> args) {
|
||||
build_context.build_mode = BuildMode_Executable;
|
||||
} else if (str == "asm" || str == "assembly" || str == "assembler") {
|
||||
build_context.build_mode = BuildMode_Assembly;
|
||||
} else if (str == "llvm" || str == "llvm-ir") {
|
||||
build_context.build_mode = BuildMode_LLVM_IR;
|
||||
} else {
|
||||
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
|
||||
gb_printf_err("Valid build modes:\n");
|
||||
gb_printf_err("\tdll, shared\n");
|
||||
gb_printf_err("\tobj, object\n");
|
||||
gb_printf_err("\texe\n");
|
||||
gb_printf_err("\tasm, assembly, assembler\n");
|
||||
gb_printf_err("\tllvm, llvm-ir\n");
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
@@ -1612,6 +1651,10 @@ void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(1, "-all-packages");
|
||||
print_usage_line(2, "Generates documentation for all packages used in the current project");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-doc-format");
|
||||
print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)");
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
if (run_or_build) {
|
||||
@@ -1621,10 +1664,16 @@ void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-opt:<integer>");
|
||||
print_usage_line(2, "Set the optimization level for complication");
|
||||
print_usage_line(2, "Set the optimization level for compilation");
|
||||
print_usage_line(2, "Accepted values: 0, 1, 2, 3");
|
||||
print_usage_line(2, "Example: -opt:2");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-o:<string>");
|
||||
print_usage_line(2, "Set the optimization mode for compilation");
|
||||
print_usage_line(2, "Accepted values: minimal, size, speed");
|
||||
print_usage_line(2, "Example: -o:speed");
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
if (check) {
|
||||
|
||||
@@ -1813,6 +1813,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
|
||||
|
||||
if (false) {}
|
||||
ELSE_IF_ADD_TAG(optional_ok)
|
||||
ELSE_IF_ADD_TAG(optional_second)
|
||||
ELSE_IF_ADD_TAG(require_results)
|
||||
ELSE_IF_ADD_TAG(bounds_check)
|
||||
ELSE_IF_ADD_TAG(no_bounds_check)
|
||||
|
||||
@@ -203,6 +203,7 @@ enum ProcTag {
|
||||
|
||||
ProcTag_require_results = 1<<4,
|
||||
ProcTag_optional_ok = 1<<5,
|
||||
ProcTag_optional_second = 1<<6,
|
||||
};
|
||||
|
||||
enum ProcCallingConvention {
|
||||
@@ -338,6 +339,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
||||
Token ellipsis; \
|
||||
ProcInlining inlining; \
|
||||
bool optional_ok_one; \
|
||||
i32 builtin_id; \
|
||||
}) \
|
||||
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
|
||||
AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \
|
||||
|
||||
+21
-24
@@ -283,7 +283,6 @@ struct TypeProc {
|
||||
TYPE_KIND(SimdVector, struct { \
|
||||
i64 count; \
|
||||
Type *elem; \
|
||||
bool is_x86_mmx; \
|
||||
}) \
|
||||
TYPE_KIND(RelativePointer, struct { \
|
||||
Type *pointer_type; \
|
||||
@@ -460,8 +459,8 @@ gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
|
||||
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
|
||||
|
||||
{Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
|
||||
{Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
||||
{Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
|
||||
{Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
||||
|
||||
{Type_Basic, {Basic_rune, BasicFlag_Integer | BasicFlag_Rune, 4, STR_LIT("rune")}},
|
||||
|
||||
@@ -679,8 +678,6 @@ gb_global Type *t_source_code_location_ptr = nullptr;
|
||||
gb_global Type *t_map_hash = nullptr;
|
||||
gb_global Type *t_map_header = nullptr;
|
||||
|
||||
gb_global Type *t_vector_x86_mmx = nullptr;
|
||||
|
||||
|
||||
gb_global Type *t_equal_proc = nullptr;
|
||||
gb_global Type *t_hasher_proc = nullptr;
|
||||
@@ -1012,6 +1009,20 @@ bool is_type_integer(Type *t) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_integer_like(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
|
||||
}
|
||||
if (t->kind == Type_BitSet) {
|
||||
if (t->BitSet.underlying) {
|
||||
return is_type_integer_like(t->BitSet.underlying);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_type_unsigned(Type *t) {
|
||||
t = base_type(t);
|
||||
// t = core_type(t);
|
||||
@@ -1468,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) {
|
||||
if (t->kind == Type_BitSet) {
|
||||
t = bit_set_to_int(t);
|
||||
}
|
||||
GB_ASSERT(t->kind == Type_Basic);
|
||||
GB_ASSERT_MSG(t->kind == Type_Basic, "%s", type_to_string(t));
|
||||
|
||||
switch (t->Basic.kind) {
|
||||
// Endian Specific Types
|
||||
@@ -2148,12 +2159,8 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
|
||||
case Type_SimdVector:
|
||||
if (y->kind == Type_SimdVector) {
|
||||
if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) {
|
||||
if (x->SimdVector.is_x86_mmx) {
|
||||
return true;
|
||||
} else if (x->SimdVector.count == y->SimdVector.count) {
|
||||
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
|
||||
}
|
||||
if (x->SimdVector.count == y->SimdVector.count) {
|
||||
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2953,9 +2960,6 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
|
||||
}
|
||||
|
||||
case Type_SimdVector: {
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
return 8;
|
||||
}
|
||||
// align of
|
||||
i64 count = t->SimdVector.count;
|
||||
Type *elem = t->SimdVector.elem;
|
||||
@@ -3219,9 +3223,6 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
|
||||
}
|
||||
|
||||
case Type_SimdVector: {
|
||||
if (t->SimdVector.is_x86_mmx) {
|
||||
return 8;
|
||||
}
|
||||
i64 count = t->SimdVector.count;
|
||||
Type *elem = t->SimdVector.elem;
|
||||
return count * type_size_of_internal(elem, path);
|
||||
@@ -3656,12 +3657,8 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
break;
|
||||
|
||||
case Type_SimdVector:
|
||||
if (type->SimdVector.is_x86_mmx) {
|
||||
return gb_string_appendc(str, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
|
||||
str = write_type_to_string(str, type->SimdVector.elem);
|
||||
}
|
||||
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
|
||||
str = write_type_to_string(str, type->SimdVector.elem);
|
||||
break;
|
||||
|
||||
case Type_RelativePointer:
|
||||
|
||||
Reference in New Issue
Block a user