Merge remote-tracking branch 'upstream/master' into prototype-fmt

This commit is contained in:
Daniel Gavin
2021-04-23 10:24:05 +02:00
44 changed files with 1568 additions and 717 deletions
+15
View File
@@ -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 {
-3
View File
@@ -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 {
+11 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -6,6 +6,7 @@ Proc_Tag :: enum {
Bounds_Check,
No_Bounds_Check,
Optional_Ok,
Optional_Second,
}
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
-5
View File
@@ -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));
+4 -6
View File
@@ -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
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+4 -8
View File
@@ -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(");
+9 -6
View File
@@ -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() ---;
+44 -40
View File
@@ -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;
+13 -10
View File
@@ -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;
+40 -28
View File
@@ -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 {
+6 -3
View File
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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;
+9 -9
View File
@@ -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 {
+4 -8
View File
@@ -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(");
+15
View File
@@ -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 {
+1
View File
@@ -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);
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+6
View File
@@ -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);
+29
View File
@@ -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},
-4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+2 -1
View File
@@ -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);
+64 -5
View File
@@ -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
View File
@@ -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) {
+1
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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: