mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge remote-tracking branch 'upstream/master' into prototype-fmt
This commit is contained in:
@@ -16,6 +16,21 @@ ptr_from_slice :: proc(str: []byte) -> ^byte {
|
|||||||
return d.data;
|
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.
|
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||||
compare :: proc(lhs, rhs: []byte) -> int {
|
compare :: proc(lhs, rhs: []byte) -> int {
|
||||||
|
|||||||
@@ -1638,9 +1638,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Simd_Vector:
|
case runtime.Type_Info_Simd_Vector:
|
||||||
if info.is_x86_mmx {
|
|
||||||
io.write_string(fi.writer, "intrinsics.x86_mmx<>");
|
|
||||||
}
|
|
||||||
io.write_byte(fi.writer, '<');
|
io.write_byte(fi.writer, '<');
|
||||||
defer io.write_byte(fi.writer, '>');
|
defer io.write_byte(fi.writer, '>');
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|
||||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
|
||||||
|
|
||||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[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_load :: proc(dst: ^$T) -> T ---
|
||||||
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||||
|
|
||||||
// Atomics
|
// Trapping
|
||||||
|
debug_trap :: proc() ---
|
||||||
|
trap :: proc() -> ! ---
|
||||||
|
|
||||||
|
// Atomics
|
||||||
atomic_fence :: proc() ---
|
atomic_fence :: proc() ---
|
||||||
atomic_fence_acq :: proc() ---
|
atomic_fence_acq :: proc() ---
|
||||||
atomic_fence_rel :: proc() ---
|
atomic_fence_rel :: proc() ---
|
||||||
@@ -89,9 +89,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti
|
|||||||
|
|
||||||
// Instructions
|
// 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
|
// Constant type tests
|
||||||
|
|
||||||
|
|||||||
+96
-15
@@ -28,11 +28,20 @@ Allocator_Query_Info :: struct {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Allocator_Error :: runtime.Allocator_Error;
|
||||||
|
/*
|
||||||
|
Allocator_Error :: enum byte {
|
||||||
|
None = 0,
|
||||||
|
Out_Of_Memory = 1,
|
||||||
|
Invalid_Pointer = 2,
|
||||||
|
Invalid_Argument = 3,
|
||||||
|
}
|
||||||
|
*/
|
||||||
Allocator_Proc :: runtime.Allocator_Proc;
|
Allocator_Proc :: runtime.Allocator_Proc;
|
||||||
/*
|
/*
|
||||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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;
|
Allocator :: runtime.Allocator;
|
||||||
@@ -52,23 +61,49 @@ alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := contex
|
|||||||
if allocator.procedure == nil {
|
if allocator.procedure == nil {
|
||||||
return 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) {
|
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
if ptr == nil {
|
if size == 0 {
|
||||||
return;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
if allocator.procedure == 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) {
|
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
if allocator.procedure != nil {
|
if ptr == nil {
|
||||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
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 {
|
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 new_size == 0 {
|
||||||
if ptr != nil {
|
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;
|
return nil;
|
||||||
} else if ptr == 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) {
|
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||||
if allocator.procedure != nil {
|
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 set;
|
||||||
}
|
}
|
||||||
return nil;
|
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) {
|
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||||
props.pointer = pointer;
|
props.pointer = pointer;
|
||||||
if allocator.procedure != nil {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -218,4 +275,28 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
|||||||
free(old_memory, allocator, loc);
|
free(old_memory, allocator, loc);
|
||||||
return new_memory;
|
return new_memory;
|
||||||
}
|
}
|
||||||
|
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
old_memory := raw_data(old_data);
|
||||||
|
old_size := len(old_data);
|
||||||
|
if old_memory == nil {
|
||||||
|
return alloc_bytes(new_size, alignment, allocator, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_size == 0 {
|
||||||
|
err := free_bytes(old_data, allocator, loc);
|
||||||
|
return nil, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_size == old_size {
|
||||||
|
return old_data, .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
|
||||||
|
if new_memory == nil || err != nil {
|
||||||
|
return nil, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.copy(new_memory, old_data);
|
||||||
|
free_bytes(old_data, allocator, loc);
|
||||||
|
return new_memory, err;
|
||||||
|
}
|
||||||
|
|||||||
+154
-174
@@ -5,8 +5,8 @@ import "core:runtime"
|
|||||||
|
|
||||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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) {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
nil_allocator :: proc() -> Allocator {
|
nil_allocator :: proc() -> Allocator {
|
||||||
@@ -47,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
|||||||
|
|
||||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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;
|
arena := cast(^Arena)allocator_data;
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
@@ -55,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
total_size := size + alignment;
|
total_size := size + alignment;
|
||||||
|
|
||||||
if arena.offset + total_size > len(arena.data) {
|
if arena.offset + total_size > len(arena.data) {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
#no_bounds_check end := &arena.data[arena.offset];
|
#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));
|
ptr := align_forward(end, uintptr(alignment));
|
||||||
arena.offset += total_size;
|
arena.offset += total_size;
|
||||||
arena.peak_used = max(arena.peak_used, arena.offset);
|
arena.peak_used = max(arena.peak_used, arena.offset);
|
||||||
return zero(ptr, size);
|
zero(ptr, size);
|
||||||
|
return byte_slice(ptr, size), nil;
|
||||||
|
|
||||||
case .Free:
|
case .Free:
|
||||||
// NOTE(bill): Free all at once
|
// NOTE(bill): Free all at once
|
||||||
@@ -73,20 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
arena.offset = 0;
|
arena.offset = 0;
|
||||||
|
|
||||||
case .Resize:
|
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:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
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 {
|
Scratch_Allocator :: struct {
|
||||||
data: []byte,
|
data: []byte,
|
||||||
curr_offset: int,
|
curr_offset: int,
|
||||||
prev_allocation: rawptr,
|
prev_allocation: rawptr,
|
||||||
backup_allocator: Allocator,
|
backup_allocator: Allocator,
|
||||||
leaked_allocations: [dynamic]rawptr,
|
leaked_allocations: [dynamic][]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
for ptr in s.leaked_allocations {
|
for ptr in s.leaked_allocations {
|
||||||
free(ptr, s.backup_allocator);
|
free_bytes(ptr, s.backup_allocator);
|
||||||
}
|
}
|
||||||
delete(s.leaked_allocations);
|
delete(s.leaked_allocations);
|
||||||
delete(s.data, s.backup_allocator);
|
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,
|
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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);
|
s := (^Scratch_Allocator)(allocator_data);
|
||||||
|
|
||||||
@@ -165,7 +166,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
s.prev_allocation = rawptr(ptr);
|
s.prev_allocation = rawptr(ptr);
|
||||||
offset := int(ptr - start);
|
offset := int(ptr - start);
|
||||||
s.curr_offset = offset + size;
|
s.curr_offset = offset + size;
|
||||||
return rawptr(ptr);
|
return byte_slice(rawptr(ptr), size), nil;
|
||||||
|
|
||||||
case size <= len(s.data):
|
case size <= len(s.data):
|
||||||
start := uintptr(raw_data(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);
|
s.prev_allocation = rawptr(ptr);
|
||||||
offset := int(ptr - start);
|
offset := int(ptr - start);
|
||||||
s.curr_offset = offset + size;
|
s.curr_offset = offset + size;
|
||||||
return rawptr(ptr);
|
return byte_slice(rawptr(ptr), size), nil;
|
||||||
}
|
}
|
||||||
a := s.backup_allocator;
|
a := s.backup_allocator;
|
||||||
if a.procedure == nil {
|
if a.procedure == nil {
|
||||||
@@ -183,9 +184,12 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
s.backup_allocator = a;
|
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 {
|
if s.leaked_allocations == nil {
|
||||||
s.leaked_allocations = make([dynamic]rawptr, a);
|
s.leaked_allocations = make([dynamic][]byte, a);
|
||||||
}
|
}
|
||||||
append(&s.leaked_allocations, ptr);
|
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:
|
case .Free:
|
||||||
start := uintptr(raw_data(s.data));
|
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 {
|
if s.prev_allocation == old_memory {
|
||||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||||
s.prev_allocation = nil;
|
s.prev_allocation = nil;
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if start <= old_ptr && old_ptr < end {
|
if start <= old_ptr && old_ptr < end {
|
||||||
// NOTE(bill): Cannot free this pointer but it is valid
|
// NOTE(bill): Cannot free this pointer but it is valid
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.leaked_allocations) != 0 {
|
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 {
|
if ptr == old_memory {
|
||||||
free(ptr, s.backup_allocator);
|
free_bytes(data, s.backup_allocator);
|
||||||
ordered_remove(&s.leaked_allocations, i);
|
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:
|
case .Free_All:
|
||||||
s.curr_offset = 0;
|
s.curr_offset = 0;
|
||||||
s.prev_allocation = nil;
|
s.prev_allocation = nil;
|
||||||
for ptr in s.leaked_allocations {
|
for ptr in s.leaked_allocations {
|
||||||
free(ptr, s.backup_allocator);
|
free_bytes(ptr, s.backup_allocator);
|
||||||
}
|
}
|
||||||
clear(&s.leaked_allocations);
|
clear(&s.leaked_allocations);
|
||||||
|
|
||||||
@@ -238,26 +244,28 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
old_ptr := uintptr(old_memory);
|
old_ptr := uintptr(old_memory);
|
||||||
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
||||||
s.curr_offset = int(old_ptr-begin)+size;
|
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);
|
data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||||
copy(ptr, old_memory, old_size);
|
if err != nil {
|
||||||
scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
|
return data, err;
|
||||||
return ptr;
|
}
|
||||||
|
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:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, nil;
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
|
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,
|
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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;
|
s := cast(^Stack)allocator_data;
|
||||||
|
|
||||||
if s.data == nil {
|
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);
|
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));
|
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
|
||||||
if s.curr_offset + padding + size > len(s.data) {
|
if s.curr_offset + padding + size > len(s.data) {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
s.prev_offset = s.curr_offset;
|
s.prev_offset = s.curr_offset;
|
||||||
s.curr_offset += padding;
|
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);
|
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 {
|
switch mode {
|
||||||
@@ -334,7 +343,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return raw_alloc(s, size, alignment);
|
return raw_alloc(s, size, alignment);
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
start := uintptr(raw_data(s.data));
|
start := uintptr(raw_data(s.data));
|
||||||
end := start + uintptr(len(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) {
|
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||||
// NOTE(bill): Allow double frees
|
// NOTE(bill): Allow double frees
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||||
|
|
||||||
if old_offset != header.prev_offset {
|
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.curr_offset = old_offset;
|
||||||
s.prev_offset = header.prev_offset;
|
s.prev_offset = header.prev_offset;
|
||||||
|
|
||||||
|
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
s.prev_offset = 0;
|
s.prev_offset = 0;
|
||||||
s.curr_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);
|
return raw_alloc(s, size, alignment);
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
start := uintptr(raw_data(s.data));
|
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) {
|
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||||
// NOTE(bill): Allow double frees
|
// NOTE(bill): Allow double frees
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if old_size == size {
|
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));
|
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||||
|
|
||||||
if old_offset != header.prev_offset {
|
if old_offset != header.prev_offset {
|
||||||
ptr := raw_alloc(s, size, alignment);
|
data, err := raw_alloc(s, size, alignment);
|
||||||
copy(ptr, old_memory, min(old_size, size));
|
if err == nil {
|
||||||
return ptr;
|
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||||
|
}
|
||||||
|
return data, err;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
|
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);
|
zero(rawptr(curr_addr + uintptr(diff)), diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return old_memory;
|
return byte_slice(old_memory, size), nil;
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
case .Query_Info:
|
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,
|
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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;
|
s := cast(^Small_Stack)allocator_data;
|
||||||
|
|
||||||
if s.data == nil {
|
if s.data == nil {
|
||||||
return nil;
|
return nil, .Invalid_Argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
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);
|
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));
|
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
|
||||||
if s.offset + padding + size > len(s.data) {
|
if s.offset + padding + size > len(s.data) {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
s.offset += padding;
|
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);
|
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 {
|
switch mode {
|
||||||
@@ -486,19 +498,20 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return raw_alloc(s, size, align);
|
return raw_alloc(s, size, align);
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
start := uintptr(raw_data(s.data));
|
start := uintptr(raw_data(s.data));
|
||||||
end := start + uintptr(len(s.data));
|
end := start + uintptr(len(s.data));
|
||||||
curr_addr := uintptr(old_memory);
|
curr_addr := uintptr(old_memory);
|
||||||
|
|
||||||
if !(start <= curr_addr && curr_addr < end) {
|
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) {
|
if curr_addr >= start+uintptr(s.offset) {
|
||||||
// NOTE(bill): Allow double frees
|
// NOTE(bill): Allow double frees
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
|
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);
|
return raw_alloc(s, size, align);
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
start := uintptr(raw_data(s.data));
|
start := uintptr(raw_data(s.data));
|
||||||
end := start + uintptr(len(s.data));
|
end := start + uintptr(len(s.data));
|
||||||
curr_addr := uintptr(old_memory);
|
curr_addr := uintptr(old_memory);
|
||||||
if !(start <= curr_addr && curr_addr < end) {
|
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) {
|
if curr_addr >= start+uintptr(s.offset) {
|
||||||
// NOTE(bill): Treat as a double free
|
// NOTE(bill): Treat as a double free
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if old_size == size {
|
if old_size == size {
|
||||||
return old_memory;
|
return byte_slice(old_memory, size), nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr := raw_alloc(s, size, align);
|
data, err := raw_alloc(s, size, align);
|
||||||
copy(ptr, old_memory, min(old_size, size));
|
if err == nil {
|
||||||
return ptr;
|
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||||
|
}
|
||||||
|
return data, err;
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
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,
|
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int,
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
flags: u64 = 0, loc := #caller_location) -> rawptr {
|
|
||||||
pool := (^Dynamic_Pool)(allocator_data);
|
pool := (^Dynamic_Pool)(allocator_data);
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc:
|
||||||
return dynamic_pool_alloc(pool, size);
|
return dynamic_pool_alloc_bytes(pool, size);
|
||||||
case .Free:
|
case .Free:
|
||||||
//
|
return nil, nil;
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
dynamic_pool_free_all(pool);
|
dynamic_pool_free_all(pool);
|
||||||
|
return nil, nil;
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if old_size >= size {
|
if old_size >= size {
|
||||||
return old_memory;
|
return byte_slice(old_memory, size), nil;
|
||||||
}
|
}
|
||||||
ptr := dynamic_pool_alloc(pool, size);
|
data, err := dynamic_pool_alloc_bytes(pool, size);
|
||||||
copy(ptr, old_memory, old_size);
|
if err == nil {
|
||||||
return ptr;
|
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||||
|
}
|
||||||
|
return data, err;
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
info := (^Allocator_Query_Info)(old_memory);
|
info := (^Allocator_Query_Info)(old_memory);
|
||||||
if info != nil && info.pointer != nil {
|
if info != nil && info.pointer != nil {
|
||||||
info.size = pool.block_size;
|
info.size = pool.block_size;
|
||||||
info.alignment = pool.alignment;
|
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 {
|
dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
|
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 {
|
if block_allocator.procedure == nil {
|
||||||
panic("You must call pool_init on a Pool before using it");
|
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 {
|
if len(unused_blocks) > 0 {
|
||||||
new_block = pop(&unused_blocks);
|
new_block = pop(&unused_blocks);
|
||||||
} else {
|
} else {
|
||||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
data: []byte;
|
||||||
block_size, alignment,
|
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||||
nil, 0);
|
block_size, alignment,
|
||||||
|
nil, 0);
|
||||||
|
new_block = raw_data(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes_left = block_size;
|
bytes_left = block_size;
|
||||||
current_pos = new_block;
|
current_pos = new_block;
|
||||||
current_block = new_block;
|
current_block = new_block;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
n := bytes;
|
n := bytes;
|
||||||
@@ -678,26 +705,29 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
|||||||
n += extra;
|
n += extra;
|
||||||
if n >= out_band_size {
|
if n >= out_band_size {
|
||||||
assert(block_allocator.procedure != nil);
|
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,
|
block_size, alignment,
|
||||||
nil, 0);
|
nil, 0);
|
||||||
if memory != nil {
|
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 {
|
if bytes_left < n {
|
||||||
cycle_new_block(pool);
|
err := cycle_new_block(pool);
|
||||||
|
if err != nil {
|
||||||
|
return nil, err;
|
||||||
|
}
|
||||||
if current_block == nil {
|
if current_block == nil {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memory := current_pos;
|
memory := current_pos;
|
||||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||||
bytes_left -= 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,
|
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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 {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc:
|
||||||
@@ -753,13 +783,13 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Query_Features};
|
set^ = {.Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
panic_allocator :: proc() -> Allocator {
|
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 {
|
Tracking_Allocator_Entry :: struct {
|
||||||
memory: rawptr,
|
memory: rawptr,
|
||||||
size: int,
|
size: int,
|
||||||
alignment: int,
|
alignment: int,
|
||||||
location: runtime.Source_Code_Location,
|
err: Allocator_Error,
|
||||||
|
location: runtime.Source_Code_Location,
|
||||||
}
|
}
|
||||||
Tracking_Allocator_Bad_Free_Entry :: struct {
|
Tracking_Allocator_Bad_Free_Entry :: struct {
|
||||||
memory: rawptr,
|
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);
|
data := (^Tracking_Allocator)(allocator_data);
|
||||||
if mode == .Query_Info {
|
if mode == .Query_Info {
|
||||||
info := (^Allocator_Query_Info)(old_memory);
|
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 {
|
if entry, ok := data.allocation_map[info.pointer]; ok {
|
||||||
info.size = entry.size;
|
info.size = entry.size;
|
||||||
info.alignment = entry.alignment;
|
info.alignment = entry.alignment;
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
info.pointer = nil;
|
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 {
|
if mode == .Free && old_memory not_in data.allocation_map {
|
||||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||||
memory = old_memory,
|
memory = old_memory,
|
||||||
location = loc,
|
location = loc,
|
||||||
});
|
});
|
||||||
} else {
|
} 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 {
|
if data.allocation_map.allocator.procedure == nil {
|
||||||
data.allocation_map.allocator = context.allocator;
|
data.allocation_map.allocator = context.allocator;
|
||||||
@@ -896,22 +874,24 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
|||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc:
|
||||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||||
memory = result,
|
memory = result_ptr,
|
||||||
size = size,
|
size = size,
|
||||||
alignment = alignment,
|
alignment = alignment,
|
||||||
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
};
|
};
|
||||||
case .Free:
|
case .Free:
|
||||||
delete_key(&data.allocation_map, old_memory);
|
delete_key(&data.allocation_map, old_memory);
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if old_memory != result {
|
if old_memory != result_ptr {
|
||||||
delete_key(&data.allocation_map, old_memory);
|
delete_key(&data.allocation_map, old_memory);
|
||||||
}
|
}
|
||||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||||
memory = result,
|
memory = result_ptr,
|
||||||
size = size,
|
size = size,
|
||||||
alignment = alignment,
|
alignment = alignment,
|
||||||
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -925,13 +905,13 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
|||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
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:
|
case .Query_Features:
|
||||||
return nil;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, nil;
|
||||||
};
|
};
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -139,10 +139,13 @@ slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
|
|||||||
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
|
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||||
assert(len >= 0);
|
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||||
return transmute([]byte)Raw_Slice{data = ptr, len = len};
|
|
||||||
}
|
}
|
||||||
|
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 {
|
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
|
||||||
s := transmute(Raw_Slice)slice;
|
s := transmute(Raw_Slice)slice;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Proc_Tag :: enum {
|
|||||||
Bounds_Check,
|
Bounds_Check,
|
||||||
No_Bounds_Check,
|
No_Bounds_Check,
|
||||||
Optional_Ok,
|
Optional_Ok,
|
||||||
|
Optional_Second,
|
||||||
}
|
}
|
||||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||||
|
|
||||||
|
|||||||
@@ -212,11 +212,6 @@ Type_Flag_Bit_Set :: enum u32le {
|
|||||||
Underlying_Type = 4,
|
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 {
|
from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
|
||||||
s: mem.Raw_Slice;
|
s: mem.Raw_Slice;
|
||||||
s.data = rawptr(uintptr(base) + uintptr(a.offset));
|
s.data = rawptr(uintptr(base) + uintptr(a.offset));
|
||||||
|
|||||||
@@ -1910,12 +1910,10 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
|||||||
ident := expect_token(p, .Ident);
|
ident := expect_token(p, .Ident);
|
||||||
|
|
||||||
switch ident.text {
|
switch ident.text {
|
||||||
case "bounds_check":
|
case "bounds_check": tags |= {.Bounds_Check};
|
||||||
tags |= {.Bounds_Check};
|
case "no_bounds_check": tags |= {.No_Bounds_Check};
|
||||||
case "no_bounds_check":
|
case "optional_ok": tags |= {.Optional_Ok};
|
||||||
tags |= {.No_Bounds_Check};
|
case "optional_second": tags |= {.Optional_Second};
|
||||||
case "optional_ok":
|
|
||||||
tags |= {.Optional_Ok};
|
|
||||||
case:
|
case:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-10
@@ -133,8 +133,7 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
|||||||
|
|
||||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||||
size, alignment: int,
|
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.
|
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
// 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.
|
// 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));
|
a := max(alignment, align_of(rawptr));
|
||||||
space := size + a - 1;
|
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);
|
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||||
diff := int(aligned_ptr - ptr);
|
diff := int(aligned_ptr - ptr);
|
||||||
if (size + diff) > space {
|
if (size + diff) > space {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned_mem = rawptr(aligned_ptr);
|
aligned_mem = rawptr(aligned_ptr);
|
||||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
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) {
|
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 {
|
if p == nil {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
return aligned_alloc(new_size, new_alignment, p);
|
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 {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_allocator :: proc() -> mem.Allocator {
|
heap_allocator :: proc() -> mem.Allocator {
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
|||||||
defer _unix_free(path_ptr);
|
defer _unix_free(path_ptr);
|
||||||
|
|
||||||
path_cstr := cstring(path_ptr);
|
path_cstr := cstring(path_ptr);
|
||||||
path = strings.clone(string(path_cstr), allocator);
|
path_str := strings.clone(string(path_cstr), allocator);
|
||||||
return path, true;
|
return path_str, true;
|
||||||
}
|
}
|
||||||
|
|
||||||
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||||
|
|||||||
@@ -565,14 +565,10 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
|
|||||||
n += _n(io.write_byte(w, ']'));
|
n += _n(io.write_byte(w, ']'));
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
if info.is_x86_mmx {
|
n += write_string(w, "#simd[");
|
||||||
n += write_string(w, "intrinsics.x86_mmx");
|
n += _n(io.write_i64(w, i64(info.count)));
|
||||||
} else {
|
n += _n(io.write_byte(w, ']'));
|
||||||
n += write_string(w, "#simd[");
|
n += write_type(w, info.elem);
|
||||||
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:
|
case Type_Info_Relative_Pointer:
|
||||||
n += write_string(w, "#relative(");
|
n += write_string(w, "#relative(");
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ Type_Info_Simd_Vector :: struct {
|
|||||||
elem: ^Type_Info,
|
elem: ^Type_Info,
|
||||||
elem_size: int,
|
elem_size: int,
|
||||||
count: int,
|
count: int,
|
||||||
is_x86_mmx: bool,
|
|
||||||
};
|
};
|
||||||
Type_Info_Relative_Pointer :: struct {
|
Type_Info_Relative_Pointer :: struct {
|
||||||
pointer: ^Type_Info,
|
pointer: ^Type_Info,
|
||||||
@@ -252,7 +251,6 @@ Source_Code_Location :: struct {
|
|||||||
|
|
||||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
|
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
|
||||||
|
|
||||||
|
|
||||||
// Allocation Stuff
|
// Allocation Stuff
|
||||||
Allocator_Mode :: enum byte {
|
Allocator_Mode :: enum byte {
|
||||||
Alloc,
|
Alloc,
|
||||||
@@ -271,9 +269,17 @@ Allocator_Query_Info :: struct {
|
|||||||
alignment: Maybe(int),
|
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,
|
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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 {
|
Allocator :: struct {
|
||||||
procedure: Allocator_Proc,
|
procedure: Allocator_Proc,
|
||||||
data: rawptr,
|
data: rawptr,
|
||||||
@@ -426,9 +432,6 @@ typeid_base_without_enum :: typeid_core;
|
|||||||
|
|
||||||
@(default_calling_convention = "none")
|
@(default_calling_convention = "none")
|
||||||
foreign {
|
foreign {
|
||||||
@(link_name="llvm.assume")
|
|
||||||
assume :: proc(cond: bool) ---;
|
|
||||||
|
|
||||||
@(link_name="llvm.debugtrap")
|
@(link_name="llvm.debugtrap")
|
||||||
debug_trap :: proc() ---;
|
debug_trap :: proc() ---;
|
||||||
|
|
||||||
|
|||||||
@@ -127,26 +127,30 @@ free_all :: proc{mem_free_all};
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
mem_free(raw_data(str), allocator, loc);
|
return mem_free(raw_data(str), allocator, loc);
|
||||||
}
|
}
|
||||||
@builtin
|
@builtin
|
||||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
mem_free((^byte)(str), allocator, loc);
|
return mem_free((^byte)(str), allocator, loc);
|
||||||
}
|
}
|
||||||
@builtin
|
@builtin
|
||||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||||
mem_free(raw_data(array), array.allocator, loc);
|
return mem_free(raw_data(array), array.allocator, loc);
|
||||||
}
|
}
|
||||||
@builtin
|
@builtin
|
||||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
mem_free(raw_data(array), allocator, loc);
|
return mem_free(raw_data(array), allocator, loc);
|
||||||
}
|
}
|
||||||
@builtin
|
@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;
|
raw := transmute(Raw_Map)m;
|
||||||
delete_slice(raw.hashes, raw.entries.allocator, loc);
|
err := delete_slice(raw.hashes, raw.entries.allocator, loc);
|
||||||
mem_free(raw.entries.data, 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
|
// 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
|
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
|
||||||
@builtin
|
@builtin
|
||||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
|
||||||
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
|
||||||
if ptr != nil { ptr^ = T{}; }
|
return (^T)(ptr), err;
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
|
||||||
ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
|
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
|
||||||
if ptr != nil { ptr^ = data; }
|
res := (^T)(ptr);
|
||||||
return ptr;
|
if ptr != nil && err != .Out_Of_Memory {
|
||||||
|
res^ = data;
|
||||||
|
}
|
||||||
|
return res, err;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_RESERVE_CAPACITY :: 16;
|
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);
|
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 {
|
if data == nil && size_of(E) != 0 {
|
||||||
return nil;
|
return nil, err;
|
||||||
}
|
}
|
||||||
// mem_zero(data, size_of(E)*len);
|
s := Raw_Slice{raw_data(data), len};
|
||||||
s := Raw_Slice{data, len};
|
return transmute(T)s, err;
|
||||||
return transmute(T)s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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);
|
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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);
|
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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);
|
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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);
|
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};
|
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||||
if data == nil && size_of(E) != 0 {
|
if data == nil && size_of(E) != 0 {
|
||||||
s.len, s.cap = 0, 0;
|
s.len, s.cap = 0, 0;
|
||||||
}
|
}
|
||||||
// mem_zero(data, size_of(E)*cap);
|
return transmute(T)s, err;
|
||||||
return transmute(T)s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -449,15 +453,15 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
new_size := capacity * size_of(E);
|
new_size := capacity * size_of(E);
|
||||||
allocator := a.allocator;
|
allocator := a.allocator;
|
||||||
|
|
||||||
new_data := allocator.procedure(
|
new_data, err := allocator.procedure(
|
||||||
allocator.data, .Resize, new_size, align_of(E),
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.data = new_data;
|
a.data = raw_data(new_data);
|
||||||
a.cap = capacity;
|
a.cap = capacity;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -483,15 +487,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
new_size := length * size_of(E);
|
new_size := length * size_of(E);
|
||||||
allocator := a.allocator;
|
allocator := a.allocator;
|
||||||
|
|
||||||
new_data := allocator.procedure(
|
new_data, err := allocator.procedure(
|
||||||
allocator.data, .Resize, new_size, align_of(E),
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.data = new_data;
|
a.data = raw_data(new_data);
|
||||||
a.len = length;
|
a.len = length;
|
||||||
a.cap = length;
|
a.cap = length;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ raw_soa_footer :: proc{
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@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 {
|
if length <= 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,13 +106,15 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
}
|
}
|
||||||
assert(allocator.procedure != nil);
|
assert(allocator.procedure != nil);
|
||||||
|
|
||||||
new_data := allocator.procedure(
|
new_bytes: []byte;
|
||||||
|
new_bytes, err = allocator.procedure(
|
||||||
allocator.data, .Alloc, total_size, max_align,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
new_data := raw_data(new_bytes);
|
||||||
|
|
||||||
data := uintptr(&array);
|
data := uintptr(&array);
|
||||||
offset := 0;
|
offset := 0;
|
||||||
@@ -131,7 +133,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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);
|
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)^;
|
old_data := (^rawptr)(array)^;
|
||||||
|
|
||||||
new_data := array.allocator.procedure(
|
new_bytes, err := array.allocator.procedure(
|
||||||
array.allocator.data, .Alloc, new_size, max_align,
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
new_data := raw_data(new_bytes);
|
||||||
|
|
||||||
|
|
||||||
footer.cap = capacity;
|
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;
|
new_offset += type.size * capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
array.allocator.procedure(
|
_, err = array.allocator.procedure(
|
||||||
array.allocator.data, .Free, 0, max_align,
|
array.allocator.data, .Free, 0, max_align,
|
||||||
old_data, old_size, 0, loc,
|
old_data, old_size, loc,
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
|
|||||||
|
|
||||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||||
size, alignment: int,
|
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) {
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
default_allocator :: proc() -> Allocator {
|
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);
|
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,
|
curr_offset: int,
|
||||||
prev_allocation: rawptr,
|
prev_allocation: rawptr,
|
||||||
backup_allocator: Allocator,
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
for ptr in s.leaked_allocations {
|
for ptr in s.leaked_allocations {
|
||||||
free(ptr, s.backup_allocator);
|
free(raw_data(ptr), s.backup_allocator);
|
||||||
}
|
}
|
||||||
delete(s.leaked_allocations);
|
delete(s.leaked_allocations);
|
||||||
delete(s.data, s.backup_allocator);
|
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,
|
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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);
|
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);
|
s.prev_allocation = rawptr(ptr);
|
||||||
offset := int(ptr - start);
|
offset := int(ptr - start);
|
||||||
s.curr_offset = offset + size;
|
s.curr_offset = offset + size;
|
||||||
return rawptr(ptr);
|
return byte_slice(rawptr(ptr), size), .None;
|
||||||
|
|
||||||
case size <= len(s.data):
|
case size <= len(s.data):
|
||||||
start := uintptr(raw_data(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);
|
s.prev_allocation = rawptr(ptr);
|
||||||
offset := int(ptr - start);
|
offset := int(ptr - start);
|
||||||
s.curr_offset = offset + size;
|
s.curr_offset = offset + size;
|
||||||
return rawptr(ptr);
|
return byte_slice(rawptr(ptr), size), .None;
|
||||||
}
|
}
|
||||||
a := s.backup_allocator;
|
a := s.backup_allocator;
|
||||||
if a.procedure == nil {
|
if a.procedure == nil {
|
||||||
@@ -102,11 +107,14 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
|||||||
s.backup_allocator = a;
|
s.backup_allocator = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr := mem_alloc(size, alignment, a, loc);
|
data, err := mem_alloc_bytes(size, alignment, a, loc);
|
||||||
if s.leaked_allocations == nil {
|
if err != nil {
|
||||||
s.leaked_allocations = make([dynamic]rawptr, a);
|
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?
|
// TODO(bill): Should leaks be notified about?
|
||||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
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:
|
case .Free:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
start := uintptr(raw_data(s.data));
|
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 {
|
if s.prev_allocation == old_memory {
|
||||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||||
s.prev_allocation = nil;
|
s.prev_allocation = nil;
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if start <= old_ptr && old_ptr < end {
|
if start <= old_ptr && old_ptr < end {
|
||||||
// NOTE(bill): Cannot free this pointer but it is valid
|
// NOTE(bill): Cannot free this pointer but it is valid
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.leaked_allocations) != 0 {
|
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 {
|
if ptr == old_memory {
|
||||||
free(ptr, s.backup_allocator);
|
free(ptr, s.backup_allocator);
|
||||||
ordered_remove(&s.leaked_allocations, i);
|
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:
|
case .Free_All:
|
||||||
s.curr_offset = 0;
|
s.curr_offset = 0;
|
||||||
s.prev_allocation = nil;
|
s.prev_allocation = nil;
|
||||||
for ptr in s.leaked_allocations {
|
for data in s.leaked_allocations {
|
||||||
free(ptr, s.backup_allocator);
|
free(raw_data(data), s.backup_allocator);
|
||||||
}
|
}
|
||||||
clear(&s.leaked_allocations);
|
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_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
|
||||||
if old_ptr+uintptr(size) < end {
|
if old_ptr+uintptr(size) < end {
|
||||||
s.curr_offset = int(old_ptr-begin)+size;
|
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);
|
ptr, err := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||||
mem_copy(ptr, old_memory, old_size);
|
if err == .None {
|
||||||
default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc);
|
copy(ptr, byte_slice(old_memory, old_size));
|
||||||
return ptr;
|
_, err = default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||||
|
}
|
||||||
|
return ptr, err;
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory);
|
set := (^Allocator_Mode_Set)(old_memory);
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||||
|
|||||||
@@ -29,10 +29,13 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
|||||||
new_size := cap * elem_size;
|
new_size := cap * elem_size;
|
||||||
allocator := array.allocator;
|
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 {
|
if new_data != nil || elem_size == 0 {
|
||||||
array.data = new_data;
|
array.data = raw_data(new_data);
|
||||||
array.cap = cap;
|
array.cap = min(cap, len(new_data)/elem_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -173,8 +173,8 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
|
|||||||
old_size := array.len*size_of(T);
|
old_size := array.len*size_of(T);
|
||||||
new_size := new_count*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);
|
new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
|
||||||
if new_data == nil {
|
if new_data == nil || err != nil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
array.data = new_data;
|
array.data = new_data;
|
||||||
|
|||||||
+32
-16
@@ -159,43 +159,59 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
|||||||
|
|
||||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
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 {
|
if size == 0 {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
if allocator.procedure == 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 {
|
if ptr == nil {
|
||||||
return;
|
return .None;
|
||||||
}
|
}
|
||||||
if allocator.procedure == nil {
|
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 {
|
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 {
|
switch {
|
||||||
case allocator.procedure == nil:
|
case allocator.procedure == nil:
|
||||||
return nil;
|
return;
|
||||||
case new_size == 0:
|
case new_size == 0:
|
||||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc);
|
||||||
return nil;
|
|
||||||
case ptr == nil:
|
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 {
|
memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
|
||||||
return memory_compare(a, b, n) == 0;
|
return memory_compare(a, b, n) == 0;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
|
|||||||
|
|
||||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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.
|
// 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.
|
// 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));
|
a := max(alignment, align_of(rawptr));
|
||||||
space := size + a - 1;
|
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);
|
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||||
diff := int(aligned_ptr - ptr);
|
diff := int(aligned_ptr - ptr);
|
||||||
if (size + diff) > space {
|
if (size + diff) > space {
|
||||||
return nil;
|
return nil, .Out_Of_Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned_mem = rawptr(aligned_ptr);
|
aligned_mem = rawptr(aligned_ptr);
|
||||||
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||||
|
|
||||||
return aligned_mem;
|
return byte_slice(aligned_mem, size), nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned_free :: proc "contextless" (p: rawptr) {
|
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 {
|
if p == nil {
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
return aligned_alloc(new_size, new_alignment, p);
|
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 {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||||
}
|
}
|
||||||
return set;
|
return nil, nil;
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil, nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
default_allocator :: proc() -> Allocator {
|
default_allocator :: proc() -> Allocator {
|
||||||
|
|||||||
@@ -347,14 +347,10 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
|
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
if info.is_x86_mmx {
|
print_string("#simd[");
|
||||||
print_string("intrinsics.x86_mmx");
|
print_u64(u64(info.count));
|
||||||
} else {
|
print_byte(']');
|
||||||
print_string("#simd[");
|
print_type(info.elem);
|
||||||
print_u64(u64(info.count));
|
|
||||||
print_byte(']');
|
|
||||||
print_type(info.elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Type_Info_Relative_Pointer:
|
case Type_Info_Relative_Pointer:
|
||||||
print_string("#relative(");
|
print_string("#relative(");
|
||||||
|
|||||||
@@ -33,6 +33,21 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring {
|
|||||||
return cstring(d.data);
|
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.
|
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||||
compare :: proc(lhs, rhs: string) -> int {
|
compare :: proc(lhs, rhs: string) -> int {
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ once_do :: proc(o: ^Once, fn: proc()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(cold)
|
||||||
_once_do_slow :: proc(o: ^Once, fn: proc()) {
|
_once_do_slow :: proc(o: ^Once, fn: proc()) {
|
||||||
mutex_lock(&o.m);
|
mutex_lock(&o.m);
|
||||||
defer mutex_unlock(&o.m);
|
defer mutex_unlock(&o.m);
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ enum BuildModeKind {
|
|||||||
BuildMode_DynamicLibrary,
|
BuildMode_DynamicLibrary,
|
||||||
BuildMode_Object,
|
BuildMode_Object,
|
||||||
BuildMode_Assembly,
|
BuildMode_Assembly,
|
||||||
|
BuildMode_LLVM_IR,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CommandKind : u32 {
|
enum CommandKind : u32 {
|
||||||
@@ -113,7 +114,7 @@ enum CommandKind : u32 {
|
|||||||
Command_query = 1<<4,
|
Command_query = 1<<4,
|
||||||
Command_doc = 1<<5,
|
Command_doc = 1<<5,
|
||||||
Command_version = 1<<6,
|
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_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test,
|
||||||
Command__does_build = Command_run|Command_build|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 ");
|
bc->link_flags = str_lit("-arch arm64 ");
|
||||||
break;
|
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_printf_err("The arm64 architecture is only supported with -llvm-api\n");;
|
||||||
gb_exit(1);
|
gb_exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-15
@@ -343,17 +343,31 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT TODO(bill)
|
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
||||||
// Date: 2018-09-29
|
// original entity was still used check checked, but the checking was only
|
||||||
// This assert fails on `using import` if the name of the alias is the same. What should be the expected behaviour?
|
// relying on "constant" data such as the Entity.type and Entity.Constant.value
|
||||||
// Namespace collision or override? Overridding is the current behaviour
|
|
||||||
//
|
//
|
||||||
// using import "foo"
|
// Therefore two things can be done: the type can be assigned to state that it
|
||||||
// bar :: foo.bar;
|
// has been "evaluated" and the variant data can be copied across
|
||||||
|
|
||||||
// GB_ASSERT_MSG(found_entity == original_entity, "%.*s == %.*s", LIT(found_entity->token.string), LIT(new_entity->token.string));
|
|
||||||
|
|
||||||
string_map_set(&found_scope->elements, original_name, new_entity);
|
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 = {};
|
Operand operand = {};
|
||||||
|
|
||||||
|
Entity *other_entity = nullptr;
|
||||||
if (init != nullptr) {
|
if (init != nullptr) {
|
||||||
Entity *entity = nullptr;
|
Entity *entity = nullptr;
|
||||||
if (init->kind == Ast_Ident) {
|
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);
|
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
|
// 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
|
// 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->kind = Entity_ProcGroup;
|
||||||
e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities);
|
e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities);
|
||||||
return;
|
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");
|
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -694,6 +707,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
e->flags |= EntityFlag_Cold;
|
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->Procedure.is_export = ac.is_export;
|
||||||
e->deprecated_message = ac.deprecated_message;
|
e->deprecated_message = ac.deprecated_message;
|
||||||
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
|
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);
|
error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
|
||||||
gb_string_free(str);
|
gb_string_free(str);
|
||||||
}
|
}
|
||||||
if (pt->calling_convention != ProcCC_Odin &&
|
if (pt->calling_convention != ProcCC_Odin) {
|
||||||
pt->calling_convention != ProcCC_Contextless) {
|
|
||||||
error(e->token, "Procedure 'main' cannot have a custom calling convention");
|
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 (e->pkg->kind == Package_Init) {
|
||||||
if (ctx->info->entry_point != nullptr) {
|
if (ctx->info->entry_point != nullptr) {
|
||||||
error(e->token, "Redeclaration of the entry pointer procedure 'main'");
|
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->type == nullptr);
|
||||||
GB_ASSERT(e->kind == Entity_Variable);
|
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"));
|
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);
|
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
|
||||||
auto *pge = &pg_entity->ProcGroup;
|
auto *pge = &pg_entity->ProcGroup;
|
||||||
String proc_group_name = pg_entity->token.string;
|
String proc_group_name = pg_entity->token.string;
|
||||||
|
|||||||
+376
-114
@@ -79,15 +79,12 @@ void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e,
|
|||||||
Type * check_type (CheckerContext *c, Ast *expression);
|
Type * check_type (CheckerContext *c, Ast *expression);
|
||||||
Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
|
Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
|
||||||
Type * make_optional_ok_type (Type *value, bool typed=true);
|
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_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 * 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);
|
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 check_not_tuple (CheckerContext *c, Operand *operand);
|
||||||
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
|
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
|
||||||
gbString expr_to_string (Ast *expression);
|
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 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);
|
void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
|
||||||
bool check_is_terminating (Ast *node, String const &label);
|
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);
|
Ast *expr = unparen_expr(operand->expr);
|
||||||
if (expr != nullptr && expr->kind == Ast_AutoCast) {
|
if (expr != nullptr) {
|
||||||
Operand x = *operand;
|
if (expr->kind == Ast_AutoCast) {
|
||||||
x.expr = expr->AutoCast.expr;
|
Operand x = *operand;
|
||||||
bool ok = check_cast_internal(c, &x, type);
|
x.expr = expr->AutoCast.expr;
|
||||||
if (ok) {
|
bool ok = check_cast_internal(c, &x, type);
|
||||||
return MAXIMUM_TYPE_DISTANCE;
|
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;
|
return -1;
|
||||||
@@ -774,6 +785,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
|
|||||||
LIT(context_name));
|
LIT(context_name));
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return;
|
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);
|
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 a = expr_to_string(o->expr);
|
||||||
gbString b = type_to_string(type);
|
gbString b = type_to_string(type);
|
||||||
|
gbString c = type_to_string(o->type);
|
||||||
defer(
|
defer(
|
||||||
|
gb_string_free(c);
|
||||||
gb_string_free(b);
|
gb_string_free(b);
|
||||||
gb_string_free(a);
|
gb_string_free(a);
|
||||||
o->mode = Addressing_Invalid;
|
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)) {
|
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||||
error(o->expr, "'%s' truncated to '%s'", a, b);
|
error(o->expr, "'%s' truncated to '%s'", a, b);
|
||||||
} else {
|
} else {
|
||||||
error(o->expr, "Cannot convert '%s' to '%s'", a, b);
|
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
|
||||||
check_assignment_error_suggestion(c, o, type);
|
check_assignment_error_suggestion(ctx, o, type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error(o->expr, "Cannot convert '%s' to '%s'", a, b);
|
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
|
||||||
check_assignment_error_suggestion(c, o, type);
|
check_assignment_error_suggestion(ctx, o, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2224,6 +2239,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
|
|||||||
return true;
|
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_constant && is_type_untyped(src) && is_type_string(src)) {
|
||||||
if (is_type_u8_array(dst)) {
|
if (is_type_u8_array(dst)) {
|
||||||
String s = operand->value.value_string;
|
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)) {
|
if (is_type_rawptr(src) && is_type_proc(dst)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2728,31 +2764,27 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
|||||||
ExactValue a = x->value;
|
ExactValue a = x->value;
|
||||||
ExactValue b = y->value;
|
ExactValue b = y->value;
|
||||||
|
|
||||||
// Type *type = base_type(x->type);
|
if (!is_type_constant_type(x->type)) {
|
||||||
Type *type = x->type;
|
#if 0
|
||||||
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)) {
|
|
||||||
gbString xt = type_to_string(x->type);
|
gbString xt = type_to_string(x->type);
|
||||||
gbString err_str = expr_to_string(node);
|
gbString err_str = expr_to_string(node);
|
||||||
error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str);
|
error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str);
|
||||||
gb_string_free(err_str);
|
gb_string_free(err_str);
|
||||||
gb_string_free(xt);
|
gb_string_free(xt);
|
||||||
x->mode = Addressing_Invalid;
|
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;
|
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
|
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) {
|
switch (op.kind) {
|
||||||
case Token_Add: op.kind = Token_Or; break;
|
case Token_Add: op.kind = Token_Or; break;
|
||||||
case Token_Sub: op.kind = Token_AndNot; 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);
|
x->value = exact_binary_operator_value(op.kind, a, b);
|
||||||
|
|
||||||
if (is_type_typed(type)) {
|
if (is_type_typed(x->type)) {
|
||||||
if (node != nullptr) {
|
if (node != nullptr) {
|
||||||
x->expr = node;
|
x->expr = node;
|
||||||
}
|
}
|
||||||
check_is_expressible(c, x, type);
|
check_is_expressible(c, x, x->type);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (is_type_string(x->type)) {
|
} 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) {
|
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);
|
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
|
||||||
if (found == nullptr) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
ExprInfo old = *found;
|
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) {
|
void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
|
||||||
gbString expr_str = expr_to_string(operand->expr);
|
gbString expr_str = expr_to_string(operand->expr);
|
||||||
gbString type_str = type_to_string(target_type);
|
gbString type_str = type_to_string(target_type);
|
||||||
|
gbString from_type_str = type_to_string(operand->type);
|
||||||
char const *extra_text = "";
|
char const *extra_text = "";
|
||||||
|
|
||||||
if (operand->mode == Addressing_Constant) {
|
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(type_str);
|
||||||
gb_string_free(expr_str);
|
gb_string_free(expr_str);
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
@@ -5717,8 +5757,131 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
operand->mode = Addressing_NoValue;
|
operand->mode = Addressing_NoValue;
|
||||||
break;
|
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:
|
||||||
case BuiltinProc_atomic_fence_acq:
|
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:
|
case BuiltinProc_fixed_point_div_sat:
|
||||||
{
|
{
|
||||||
if (!build_context.use_llvm_api) {
|
if (!build_context.use_llvm_api) {
|
||||||
error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name));
|
error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name));
|
||||||
return false;
|
// continue anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand x = {};
|
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)) {
|
if (!are_types_identical(x.type, y.type)) {
|
||||||
gbString xts = type_to_string(x.type);
|
gbString xts = type_to_string(x.type);
|
||||||
gbString yts = type_to_string(y.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(yts);
|
||||||
gb_string_free(xts);
|
gb_string_free(xts);
|
||||||
return false;
|
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)) {
|
if (!is_type_integer(x.type) || is_type_untyped(x.type)) {
|
||||||
gbString xts = type_to_string(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);
|
gb_string_free(xts);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -5903,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (z.mode != Addressing_Constant || !is_type_integer(z.type)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
i64 n = exact_value_to_i64(z.value);
|
i64 n = exact_value_to_i64(z.value);
|
||||||
if (n <= 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
i64 sz = 8*type_size_of(x.type);
|
i64 sz = 8*type_size_of(x.type);
|
||||||
if (n > sz) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5923,6 +6086,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
break;
|
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:
|
case BuiltinProc_type_base_type:
|
||||||
if (operand->mode != Addressing_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 (o.type == nullptr || o.type->kind != Type_Tuple) {
|
||||||
if (lhs.count == 2 && rhs.count == 1 &&
|
if (lhs.count == 2 && rhs.count == 1 &&
|
||||||
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
||||||
Type *tuple = make_optional_ok_type(o.type);
|
bool do_normal = true;
|
||||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
Ast *expr = unparen_expr(o.expr);
|
||||||
|
|
||||||
Operand val = o;
|
Operand val0 = o;
|
||||||
Operand ok = o;
|
Operand val1 = o;
|
||||||
val.mode = Addressing_Value;
|
val0.mode = Addressing_Value;
|
||||||
ok.mode = Addressing_Value;
|
val1.mode = Addressing_Value;
|
||||||
ok.type = t_untyped_bool;
|
val1.type = t_untyped_bool;
|
||||||
array_add(operands, val);
|
|
||||||
array_add(operands, ok);
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
optional_ok = true;
|
||||||
tuple_index += 2;
|
tuple_index += 2;
|
||||||
} else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) {
|
} 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 {
|
} else {
|
||||||
TypeTuple *tuple = &o.type->Tuple;
|
TypeTuple *tuple = &o.type->Tuple;
|
||||||
if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) {
|
for_array(j, tuple->variables) {
|
||||||
GB_ASSERT(tuple->variables.count == 2);
|
o.type = tuple->variables[j]->type;
|
||||||
Ast *expr = unparen_expr(o.expr);
|
array_add(operands, o);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (o.type == nullptr || o.type->kind != Type_Tuple) {
|
||||||
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
|
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
|
||||||
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) {
|
||||||
Type *tuple = make_optional_ok_type(o.type);
|
bool do_normal = true;
|
||||||
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
|
Ast *expr = unparen_expr(o.expr);
|
||||||
|
|
||||||
Operand val = o;
|
Operand val0 = o;
|
||||||
Operand ok = o;
|
Operand val1 = o;
|
||||||
val.mode = Addressing_Value;
|
val0.mode = Addressing_Value;
|
||||||
ok.mode = Addressing_Value;
|
val1.mode = Addressing_Value;
|
||||||
// ok.type = t_bool;
|
val1.type = t_untyped_bool;
|
||||||
ok.type = t_untyped_bool;
|
|
||||||
array_add(operands, val);
|
|
||||||
array_add(operands, ok);
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
optional_ok = true;
|
||||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
|
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
|
||||||
} else {
|
} else {
|
||||||
@@ -6590,30 +6832,13 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TypeTuple *tuple = &o.type->Tuple;
|
TypeTuple *tuple = &o.type->Tuple;
|
||||||
if (o.mode == Addressing_OptionalOk && lhs_count == 1) {
|
for_array(j, tuple->variables) {
|
||||||
GB_ASSERT(tuple->variables.count == 2);
|
o.type = tuple->variables[j]->type;
|
||||||
Ast *expr = unparen_expr(o.expr);
|
array_add(operands, o);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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->score = score;
|
||||||
data->result_type = final_proc_type->Proc.results;
|
data->result_type = final_proc_type->Proc.results;
|
||||||
data->gen_entity = gen_entity;
|
data->gen_entity = gen_entity;
|
||||||
|
add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@@ -7080,6 +7306,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
|||||||
data->score = score;
|
data->score = score;
|
||||||
data->result_type = pt->results;
|
data->result_type = pt->results;
|
||||||
data->gen_entity = gen_entity;
|
data->gen_entity = gen_entity;
|
||||||
|
add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
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);
|
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
|
||||||
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
|
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
|
||||||
|
|
||||||
|
|
||||||
CallArgumentData data = {};
|
CallArgumentData data = {};
|
||||||
CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
|
CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||||
if (err != CallArgumentError_None) {
|
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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
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;
|
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);
|
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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
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) {
|
if (data.gen_entity != nullptr) {
|
||||||
Entity *e = data.gen_entity;
|
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);
|
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
|
||||||
decl->where_clauses_evaluated = true;
|
decl->where_clauses_evaluated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
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) {
|
if (data.gen_entity != nullptr) {
|
||||||
Entity *e = data.gen_entity;
|
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);
|
evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
|
||||||
decl->where_clauses_evaluated = true;
|
decl->where_clauses_evaluated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CallArgumentData data = {};
|
CallArgumentData data = {};
|
||||||
data.result_type = t_invalid;
|
data.result_type = t_invalid;
|
||||||
return data;
|
return data;
|
||||||
@@ -8147,6 +8380,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type *pt = base_type(proc_type);
|
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 (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
|
||||||
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
|
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) {
|
switch (inlining) {
|
||||||
case ProcInlining_inline: {
|
case ProcInlining_inline:
|
||||||
if (proc != nullptr) {
|
if (proc != nullptr) {
|
||||||
Entity *e = entity_from_expr(proc);
|
Entity *e = entity_from_expr(proc);
|
||||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||||
@@ -8189,16 +8430,31 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case ProcInlining_no_inline:
|
case ProcInlining_no_inline:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
operand->expr = call;
|
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);
|
// 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) {
|
if (!is_constant) {
|
||||||
error(node, "Expected all constant elements for a simd vector");
|
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;
|
return kind;
|
||||||
}
|
}
|
||||||
if (type_hint) {
|
if (type_hint) {
|
||||||
|
Type *type = type_of_expr(ac->expr);
|
||||||
check_cast(c, o, type_hint);
|
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;
|
o->expr = node;
|
||||||
return Expr_Expr;
|
return Expr_Expr;
|
||||||
@@ -10563,7 +10825,7 @@ void check_not_tuple(CheckerContext *c, Operand *o) {
|
|||||||
if (o->type->kind == Type_Tuple) {
|
if (o->type->kind == Type_Tuple) {
|
||||||
isize count = o->type->Tuple.variables.count;
|
isize count = o->type->Tuple.variables.count;
|
||||||
error(o->expr,
|
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;
|
o->mode = Addressing_Invalid;
|
||||||
GB_ASSERT(count != 1);
|
GB_ASSERT(count != 1);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-4
@@ -10,7 +10,19 @@ bool is_diverging_stmt(Ast *stmt) {
|
|||||||
String name = expr->CallExpr.proc->BasicDirective.name;
|
String name = expr->CallExpr.proc->BasicDirective.name;
|
||||||
return name == "panic";
|
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);
|
t = base_type(t);
|
||||||
return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
|
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;
|
Type *cond_type = t->Tuple.variables[count-1]->type;
|
||||||
if (!is_type_boolean(cond_type)) {
|
if (!is_type_boolean(cond_type)) {
|
||||||
gbString s = type_to_string(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);
|
gb_string_free(s);
|
||||||
break;
|
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) {
|
if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
|
||||||
gbString s = type_to_string(t);
|
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);
|
gb_string_free(s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
|
if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
|
||||||
gbString s = type_to_string(t);
|
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);
|
gb_string_free(s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-7
@@ -2101,8 +2101,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type_Pointer: break;
|
case Type_Pointer:
|
||||||
case Type_Proc: break; // NOTE(bill): Just a 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
|
// Odin specific
|
||||||
case Type_Slice:
|
case Type_Slice:
|
||||||
@@ -2194,6 +2201,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
|
|||||||
return new_type;
|
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_OS == "windows") {
|
||||||
if (build_context.ODIN_ARCH == "amd64") {
|
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.node = proc_type_node;
|
||||||
type->Proc.scope = c->scope;
|
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) {
|
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 = permanent_allocator();
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
Type *t = alloc_type_tuple();
|
Type *t = alloc_type_tuple();
|
||||||
array_init(&t->Tuple.variables, a, 0, 2);
|
array_init(&t->Tuple.variables, a, 2);
|
||||||
array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value, false, 0));
|
t->Tuple.variables[0] = 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));
|
t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-20
@@ -783,15 +783,6 @@ void init_universal(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bill): Set the correct arch for this
|
|
||||||
if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) {
|
|
||||||
t_vector_x86_mmx = alloc_type(Type_SimdVector);
|
|
||||||
t_vector_x86_mmx->SimdVector.is_x86_mmx = true;
|
|
||||||
|
|
||||||
Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx);
|
|
||||||
add_global_entity(entity, intrinsics_pkg->scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool defined_values_double_declaration = false;
|
bool defined_values_double_declaration = false;
|
||||||
for_array(i, bc->defined_values.entries) {
|
for_array(i, bc->defined_values.entries) {
|
||||||
char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key;
|
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_equal"),
|
||||||
str_lit("memory_compare"),
|
str_lit("memory_compare"),
|
||||||
str_lit("memory_compare_zero"),
|
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++) {
|
for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
|
||||||
force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
|
force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (build_context.no_crt) {
|
||||||
String required_no_crt_entities[] = {
|
String required_no_crt_entities[] = {
|
||||||
// NOTE(bill): Only if these exist
|
// 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 *> proc_group_entities(CheckerContext *c, Operand o) {
|
||||||
Array<Entity *> procs = {};
|
Array<Entity *> procs = {};
|
||||||
if (o.mode == Addressing_ProcGroup) {
|
if (o.mode == Addressing_ProcGroup) {
|
||||||
@@ -2574,6 +2571,29 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ struct BuiltinProc {
|
|||||||
bool variadic;
|
bool variadic;
|
||||||
ExprKind kind;
|
ExprKind kind;
|
||||||
BuiltinProcPkg pkg;
|
BuiltinProcPkg pkg;
|
||||||
|
bool diverging;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -112,6 +113,7 @@ struct AttributeContext {
|
|||||||
String thread_local_model;
|
String thread_local_model;
|
||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
|
u32 optimization_mode; // ProcedureOptimizationMode
|
||||||
struct TypeAtomOpTable *atom_op_table;
|
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_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);
|
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(CheckerContext *c, Slice<Ast *> const &nodes);
|
||||||
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
||||||
|
|||||||
@@ -41,6 +41,18 @@ enum BuiltinProcId {
|
|||||||
|
|
||||||
BuiltinProc_alloca,
|
BuiltinProc_alloca,
|
||||||
BuiltinProc_cpu_relax,
|
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_store,
|
||||||
BuiltinProc_volatile_load,
|
BuiltinProc_volatile_load,
|
||||||
@@ -122,6 +134,7 @@ enum BuiltinProcId {
|
|||||||
BuiltinProc_fixed_point_mul_sat,
|
BuiltinProc_fixed_point_mul_sat,
|
||||||
BuiltinProc_fixed_point_div_sat,
|
BuiltinProc_fixed_point_div_sat,
|
||||||
|
|
||||||
|
BuiltinProc_expect,
|
||||||
|
|
||||||
// Constant type tests
|
// 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("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, 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_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("volatile_load"), 1, false, Expr_Expr, 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_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("fixed_point_div_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(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|||||||
@@ -114,10 +114,6 @@ enum OdinDocTypeFlag_BitSet : u32 {
|
|||||||
OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4,
|
OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OdinDocTypeFlag_SimdVector : u32 {
|
|
||||||
OdinDocTypeFlag_BitSet_x86_mmx = 1<<1,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
// constants
|
// constants
|
||||||
OdinDocType_ElemsCap = 4,
|
OdinDocType_ElemsCap = 4,
|
||||||
|
|||||||
+3
-7
@@ -724,13 +724,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
|
|||||||
break;
|
break;
|
||||||
case Type_SimdVector:
|
case Type_SimdVector:
|
||||||
doc_type.kind = OdinDocType_SimdVector;
|
doc_type.kind = OdinDocType_SimdVector;
|
||||||
if (type->SimdVector.is_x86_mmx) {
|
doc_type.elem_count_len = 1;
|
||||||
doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx;
|
doc_type.elem_counts[0] = type->SimdVector.count;
|
||||||
} else {
|
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):
|
// TODO(bill):
|
||||||
break;
|
break;
|
||||||
case Type_RelativePointer:
|
case Type_RelativePointer:
|
||||||
|
|||||||
+40
-26
@@ -33,39 +33,41 @@ String const entity_strings[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum EntityFlag : u64 {
|
enum EntityFlag : u64 {
|
||||||
EntityFlag_Visited = 1<<0,
|
EntityFlag_Visited = 1ull<<0,
|
||||||
EntityFlag_Used = 1<<1,
|
EntityFlag_Used = 1ull<<1,
|
||||||
EntityFlag_Using = 1<<2,
|
EntityFlag_Using = 1ull<<2,
|
||||||
EntityFlag_Field = 1<<3,
|
EntityFlag_Field = 1ull<<3,
|
||||||
EntityFlag_Param = 1<<4,
|
EntityFlag_Param = 1ull<<4,
|
||||||
EntityFlag_Result = 1<<5,
|
EntityFlag_Result = 1ull<<5,
|
||||||
EntityFlag_ArrayElem = 1<<6,
|
EntityFlag_ArrayElem = 1ull<<6,
|
||||||
EntityFlag_Ellipsis = 1<<7,
|
EntityFlag_Ellipsis = 1ull<<7,
|
||||||
EntityFlag_NoAlias = 1<<8,
|
EntityFlag_NoAlias = 1ull<<8,
|
||||||
EntityFlag_TypeField = 1<<9,
|
EntityFlag_TypeField = 1ull<<9,
|
||||||
EntityFlag_Value = 1<<10,
|
EntityFlag_Value = 1ull<<10,
|
||||||
EntityFlag_Sret = 1<<11,
|
EntityFlag_Sret = 1ull<<11,
|
||||||
EntityFlag_ByVal = 1<<12,
|
EntityFlag_ByVal = 1ull<<12,
|
||||||
EntityFlag_BitFieldValue = 1<<13,
|
EntityFlag_BitFieldValue = 1ull<<13,
|
||||||
EntityFlag_PolyConst = 1<<14,
|
EntityFlag_PolyConst = 1ull<<14,
|
||||||
EntityFlag_NotExported = 1<<15,
|
EntityFlag_NotExported = 1ull<<15,
|
||||||
EntityFlag_ConstInput = 1<<16,
|
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_CVarArg = 1ull<<21,
|
||||||
EntityFlag_AutoCast = 1<<22,
|
EntityFlag_AutoCast = 1ull<<22,
|
||||||
|
|
||||||
EntityFlag_Disabled = 1<<24,
|
EntityFlag_Disabled = 1ull<<24,
|
||||||
EntityFlag_Cold = 1<<25, // procedure is rarely called
|
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,
|
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
|
// An Entity is a named "thing" in the language
|
||||||
struct Entity {
|
struct Entity {
|
||||||
EntityKind kind;
|
EntityKind kind;
|
||||||
@@ -125,6 +135,9 @@ struct Entity {
|
|||||||
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
||||||
// later entity kinds
|
// later entity kinds
|
||||||
union {
|
union {
|
||||||
|
struct {
|
||||||
|
u8 start;
|
||||||
|
} Dummy;
|
||||||
struct {
|
struct {
|
||||||
ExactValue value;
|
ExactValue value;
|
||||||
ParameterValue param_value;
|
ParameterValue param_value;
|
||||||
@@ -160,6 +173,7 @@ struct Entity {
|
|||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
bool is_foreign;
|
bool is_foreign;
|
||||||
bool is_export;
|
bool is_export;
|
||||||
|
ProcedureOptimizationMode optimization_mode;
|
||||||
} Procedure;
|
} Procedure;
|
||||||
struct {
|
struct {
|
||||||
Array<Entity *> entities;
|
Array<Entity *> entities;
|
||||||
|
|||||||
+2
-2
@@ -491,8 +491,8 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
|
|||||||
#define USIZE_MIX U32_MIN
|
#define USIZE_MIX U32_MIN
|
||||||
#define USIZE_MAX U32_MAX
|
#define USIZE_MAX U32_MAX
|
||||||
|
|
||||||
#define ISIZE_MIX S32_MIN
|
#define ISIZE_MIX I32_MIN
|
||||||
#define ISIZE_MAX S32_MAX
|
#define ISIZE_MAX I32_MAX
|
||||||
#elif defined(GB_ARCH_64_BIT)
|
#elif defined(GB_ARCH_64_BIT)
|
||||||
#define USIZE_MIX U64_MIN
|
#define USIZE_MIX U64_MIN
|
||||||
#define USIZE_MAX U64_MAX
|
#define USIZE_MAX U64_MAX
|
||||||
|
|||||||
+5
-8
@@ -8257,7 +8257,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
|||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ac, AutoCast, expr);
|
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_end;
|
||||||
|
|
||||||
case_ast_node(ue, UnaryExpr, expr);
|
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:
|
case Type_SimdVector:
|
||||||
ir_emit_comment(proc, str_lit("Type_SimdVector"));
|
ir_emit_comment(proc, str_lit("Type_SimdVector"));
|
||||||
tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr);
|
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, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem));
|
||||||
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true);
|
ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem)));
|
||||||
} else {
|
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;
|
break;
|
||||||
|
|
||||||
case Type_RelativePointer:
|
case Type_RelativePointer:
|
||||||
|
|||||||
+3
-7
@@ -624,13 +624,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Type_SimdVector:
|
case Type_SimdVector:
|
||||||
if (t->SimdVector.is_x86_mmx) {
|
ir_fprintf(f, "<%lld x ", t->SimdVector.count);;
|
||||||
ir_write_str_lit(f, "x86_mmx");
|
ir_print_type(f, m, t->SimdVector.elem);
|
||||||
} else {
|
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;
|
return;
|
||||||
|
|
||||||
case Type_RelativePointer:
|
case Type_RelativePointer:
|
||||||
|
|||||||
+354
-116
@@ -391,11 +391,37 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
|||||||
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
||||||
return;
|
return;
|
||||||
} else if (addr.kind == lbAddr_Context) {
|
} else if (addr.kind == lbAddr_Context) {
|
||||||
lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p));
|
lbAddr old_addr = 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);
|
// IMPORTANT NOTE(bill, 2021-04-22): reuse unused 'context' variables to minimize stack usage
|
||||||
lbValue next = lb_addr_get_ptr(p, next_addr);
|
// 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) {
|
if (addr.ctx.sel.index.count > 0) {
|
||||||
lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel);
|
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) {
|
} else if (addr.kind == lbAddr_Context) {
|
||||||
lbValue a = addr.addr;
|
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), "");
|
a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), "");
|
||||||
|
|
||||||
if (addr.ctx.sel.index.count > 0) {
|
if (addr.ctx.sel.index.count > 0) {
|
||||||
@@ -1445,9 +1478,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Type_SimdVector:
|
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);
|
return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
|
||||||
|
|
||||||
case Type_RelativePointer:
|
case Type_RelativePointer:
|
||||||
@@ -1899,9 +1929,6 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Type_SimdVector:
|
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);
|
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: {
|
case Type_RelativePointer: {
|
||||||
@@ -2523,7 +2550,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
|||||||
p->type = entity->type;
|
p->type = entity->type;
|
||||||
p->type_expr = decl->type_expr;
|
p->type_expr = decl->type_expr;
|
||||||
p->body = pl->body;
|
p->body = pl->body;
|
||||||
p->inlining = ProcInlining_none;
|
p->inlining = pl->inlining;
|
||||||
p->is_foreign = entity->Procedure.is_foreign;
|
p->is_foreign = entity->Procedure.is_foreign;
|
||||||
p->is_export = entity->Procedure.is_export;
|
p->is_export = entity->Procedure.is_export;
|
||||||
p->is_entry_point = false;
|
p->is_entry_point = false;
|
||||||
@@ -2558,9 +2585,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
|||||||
LLVMSetFunctionCallConv(p->value, cc_kind);
|
LLVMSetFunctionCallConv(p->value, cc_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity->flags & EntityFlag_Cold) {
|
|
||||||
lb_add_attribute_to_proc(m, p->value, "cold");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pt->Proc.diverging) {
|
if (pt->Proc.diverging) {
|
||||||
lb_add_attribute_to_proc(m, p->value, "noreturn");
|
lb_add_attribute_to_proc(m, p->value, "noreturn");
|
||||||
@@ -2575,6 +2599,28 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
|
|||||||
break;
|
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;
|
// lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
||||||
// // TODO(bill): Clean up this logic
|
// // TODO(bill): Clean up this logic
|
||||||
// if (build_context.metrics.os != TargetOs_js) {
|
// 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++) {
|
for (isize i = 0; i < pt->Proc.param_count; i++) {
|
||||||
Entity *e = params->variables[i];
|
Entity *e = params->variables[i];
|
||||||
Type *original_type = e->type;
|
Type *original_type = e->type;
|
||||||
Type *abi_type = pt->Proc.abi_compat_params[i];
|
|
||||||
if (e->kind != Entity_Variable) continue;
|
if (e->kind != Entity_Variable) continue;
|
||||||
|
|
||||||
if (i+1 == params->variables.count && pt->Proc.c_vararg) {
|
if (i+1 == params->variables.count && pt->Proc.c_vararg) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (is_type_tuple(abi_type)) {
|
|
||||||
for_array(j, abi_type->Tuple.variables) {
|
if (e->flags&EntityFlag_NoAlias) {
|
||||||
Type *tft = abi_type->Tuple.variables[j]->type;
|
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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);
|
lbContextData *cd = array_add_and_get(&p->context_stack);
|
||||||
cd->ctx = ctx_addr;
|
cd->ctx = ctx_addr;
|
||||||
cd->scope_index = -1;
|
cd->scope_index = -1;
|
||||||
|
cd->uses = +1; // make sure it has been used already
|
||||||
return cd;
|
return cd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9031,6 +9068,145 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
}
|
}
|
||||||
return {};
|
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:
|
case BuiltinProc_atomic_fence:
|
||||||
LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
|
LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
|
||||||
return {};
|
return {};
|
||||||
@@ -9304,6 +9480,30 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
res.type = platform_type;
|
res.type = platform_type;
|
||||||
return lb_emit_conv(p, res, tv.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));
|
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) {
|
String lb_get_const_string(lbModule *m, lbValue value) {
|
||||||
GB_ASSERT(lb_is_const(value));
|
GB_ASSERT(lb_is_const(value));
|
||||||
|
GB_ASSERT(LLVMIsConstant(value.value));
|
||||||
|
|
||||||
Type *t = base_type(value.type);
|
Type *t = base_type(value.type);
|
||||||
GB_ASSERT(are_types_identical(t, t_string));
|
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;
|
return found->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
|
lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) {
|
||||||
Type *vt = core_type(value.type);
|
GB_ASSERT(type_size_of(value.type) == type_size_of(end_type));
|
||||||
GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
|
|
||||||
|
|
||||||
// TODO(bill): lb_emit_byte_swap
|
if (type_size_of(value.type) < 2) {
|
||||||
lbValue res = {};
|
return value;
|
||||||
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, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10963,7 +11164,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
|||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ac, AutoCast, expr);
|
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_end;
|
||||||
|
|
||||||
case_ast_node(ue, UnaryExpr, expr);
|
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);
|
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[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
|
||||||
vals[3] = lb_const_bool(m, t_bool, true).value;
|
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
|
||||||
} else {
|
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 = {};
|
lbValue res = {};
|
||||||
res.type = type_deref(tag.type);
|
res.type = type_deref(tag.type);
|
||||||
@@ -13844,11 +14042,28 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
|
LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
|
||||||
|
|
||||||
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
|
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(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(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(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(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);
|
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)) {
|
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
|
||||||
TIME_SECTION("LLVM DLL main");
|
TIME_SECTION("LLVM DLL main");
|
||||||
|
|
||||||
@@ -14075,7 +14331,7 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
} else {
|
} else {
|
||||||
lbValue *found = map_get(&m->values, hash_entity(entry_point));
|
lbValue *found = map_get(&m->values, hash_entity(entry_point));
|
||||||
GB_ASSERT(found != nullptr);
|
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));
|
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) {
|
if (m->debug_builder != nullptr) {
|
||||||
TIME_SECTION("LLVM Debug Info Complete Types");
|
TIME_SECTION("LLVM Debug Info Complete Types");
|
||||||
lb_debug_complete_types(m);
|
lb_debug_complete_types(m);
|
||||||
@@ -14158,7 +14373,25 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
|
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
|
||||||
LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
|
LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
|
||||||
} else {
|
} 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;
|
return;
|
||||||
}
|
}
|
||||||
llvm_error = nullptr;
|
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");
|
TIME_SECTION("LLVM Print Module to File");
|
||||||
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
|
if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) {
|
||||||
gb_printf_err("LLVM Error: %s\n", llvm_error);
|
gb_printf_err("LLVM Error: %s\n", llvm_error);
|
||||||
gb_exit(1);
|
gb_exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (build_context.build_mode == BuildMode_LLVM_IR) {
|
||||||
|
gb_exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME_SECTION("LLVM Object Generation");
|
TIME_SECTION("LLVM Object Generation");
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ struct lbBranchBlocks {
|
|||||||
struct lbContextData {
|
struct lbContextData {
|
||||||
lbAddr ctx;
|
lbAddr ctx;
|
||||||
isize scope_index;
|
isize scope_index;
|
||||||
|
isize uses;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum lbParamPasskind {
|
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_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_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);
|
void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
|
||||||
lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
|
lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
|
||||||
lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
|
lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
|
||||||
|
|||||||
@@ -1,11 +1,41 @@
|
|||||||
|
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
|
||||||
|
void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
|
||||||
|
void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
|
||||||
|
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level);
|
||||||
|
|
||||||
|
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
|
||||||
|
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||||
|
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||||
|
LLVMAddConstantPropagationPass(fpm);
|
||||||
|
LLVMAddEarlyCSEPass(fpm);
|
||||||
|
|
||||||
|
LLVMAddConstantPropagationPass(fpm);
|
||||||
|
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||||
|
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||||
|
LLVMAddCFGSimplificationPass(fpm);
|
||||||
|
}
|
||||||
|
|
||||||
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
|
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
|
// NOTE(bill): Treat -opt:3 as if it was -opt:2
|
||||||
// TODO(bill): Determine which opt definitions should exist in the first place
|
// TODO(bill): Determine which opt definitions should exist in the first place
|
||||||
optimization_level = gb_clamp(optimization_level, 0, 2);
|
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);
|
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);
|
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||||
LLVMAddMergedLoadStoreMotionPass(fpm);
|
LLVMAddMergedLoadStoreMotionPass(fpm);
|
||||||
LLVMAddConstantPropagationPass(fpm);
|
LLVMAddConstantPropagationPass(fpm);
|
||||||
@@ -16,16 +46,45 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
|
|||||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||||
LLVMAddCFGSimplificationPass(fpm);
|
LLVMAddCFGSimplificationPass(fpm);
|
||||||
|
|
||||||
// LLVMAddSLPVectorizePass(fpm);
|
LLVMAddSCCPPass(fpm);
|
||||||
// LLVMAddLoopVectorizePass(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) {
|
if (optimization_level == 0) {
|
||||||
|
LLVMAddMemCpyOptPass(fpm);
|
||||||
|
lb_basic_populate_function_pass_manager(fpm);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#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);
|
LLVMAddSCCPPass(fpm);
|
||||||
|
|
||||||
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
LLVMAddPromoteMemoryToRegisterPass(fpm);
|
||||||
|
|||||||
+50
-1
@@ -571,6 +571,7 @@ enum BuildFlagKind {
|
|||||||
|
|
||||||
BuildFlag_OutFile,
|
BuildFlag_OutFile,
|
||||||
BuildFlag_OptimizationLevel,
|
BuildFlag_OptimizationLevel,
|
||||||
|
BuildFlag_OptimizationMode,
|
||||||
BuildFlag_ShowTimings,
|
BuildFlag_ShowTimings,
|
||||||
BuildFlag_ShowUnused,
|
BuildFlag_ShowUnused,
|
||||||
BuildFlag_ShowUnusedWithLocation,
|
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_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_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_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_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_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);
|
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:
|
case BuildFlag_OptimizationLevel:
|
||||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
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);
|
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
|
||||||
break;
|
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:
|
case BuildFlag_ShowTimings:
|
||||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
build_context.show_timings = true;
|
build_context.show_timings = true;
|
||||||
@@ -1109,8 +1140,16 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
build_context.build_mode = BuildMode_Executable;
|
build_context.build_mode = BuildMode_Executable;
|
||||||
} else if (str == "asm" || str == "assembly" || str == "assembler") {
|
} else if (str == "asm" || str == "assembly" || str == "assembler") {
|
||||||
build_context.build_mode = BuildMode_Assembly;
|
build_context.build_mode = BuildMode_Assembly;
|
||||||
|
} else if (str == "llvm" || str == "llvm-ir") {
|
||||||
|
build_context.build_mode = BuildMode_LLVM_IR;
|
||||||
} else {
|
} else {
|
||||||
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
|
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;
|
bad_flags = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1612,6 +1651,10 @@ void print_show_help(String const arg0, String const &command) {
|
|||||||
print_usage_line(1, "-all-packages");
|
print_usage_line(1, "-all-packages");
|
||||||
print_usage_line(2, "Generates documentation for all packages used in the current project");
|
print_usage_line(2, "Generates documentation for all packages used in the current project");
|
||||||
print_usage_line(0, "");
|
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) {
|
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(0, "");
|
||||||
|
|
||||||
print_usage_line(1, "-opt:<integer>");
|
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, "Accepted values: 0, 1, 2, 3");
|
||||||
print_usage_line(2, "Example: -opt:2");
|
print_usage_line(2, "Example: -opt:2");
|
||||||
print_usage_line(0, "");
|
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) {
|
if (check) {
|
||||||
|
|||||||
@@ -1813,6 +1813,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
|
|||||||
|
|
||||||
if (false) {}
|
if (false) {}
|
||||||
ELSE_IF_ADD_TAG(optional_ok)
|
ELSE_IF_ADD_TAG(optional_ok)
|
||||||
|
ELSE_IF_ADD_TAG(optional_second)
|
||||||
ELSE_IF_ADD_TAG(require_results)
|
ELSE_IF_ADD_TAG(require_results)
|
||||||
ELSE_IF_ADD_TAG(bounds_check)
|
ELSE_IF_ADD_TAG(bounds_check)
|
||||||
ELSE_IF_ADD_TAG(no_bounds_check)
|
ELSE_IF_ADD_TAG(no_bounds_check)
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ enum ProcTag {
|
|||||||
|
|
||||||
ProcTag_require_results = 1<<4,
|
ProcTag_require_results = 1<<4,
|
||||||
ProcTag_optional_ok = 1<<5,
|
ProcTag_optional_ok = 1<<5,
|
||||||
|
ProcTag_optional_second = 1<<6,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ProcCallingConvention {
|
enum ProcCallingConvention {
|
||||||
@@ -338,6 +339,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
|||||||
Token ellipsis; \
|
Token ellipsis; \
|
||||||
ProcInlining inlining; \
|
ProcInlining inlining; \
|
||||||
bool optional_ok_one; \
|
bool optional_ok_one; \
|
||||||
|
i32 builtin_id; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
|
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
|
||||||
AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \
|
AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \
|
||||||
|
|||||||
+21
-24
@@ -283,7 +283,6 @@ struct TypeProc {
|
|||||||
TYPE_KIND(SimdVector, struct { \
|
TYPE_KIND(SimdVector, struct { \
|
||||||
i64 count; \
|
i64 count; \
|
||||||
Type *elem; \
|
Type *elem; \
|
||||||
bool is_x86_mmx; \
|
|
||||||
}) \
|
}) \
|
||||||
TYPE_KIND(RelativePointer, struct { \
|
TYPE_KIND(RelativePointer, struct { \
|
||||||
Type *pointer_type; \
|
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_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
|
||||||
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
|
{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_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
|
||||||
{Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
{Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
||||||
|
|
||||||
{Type_Basic, {Basic_rune, BasicFlag_Integer | BasicFlag_Rune, 4, STR_LIT("rune")}},
|
{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_hash = nullptr;
|
||||||
gb_global Type *t_map_header = 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_equal_proc = nullptr;
|
||||||
gb_global Type *t_hasher_proc = nullptr;
|
gb_global Type *t_hasher_proc = nullptr;
|
||||||
@@ -1012,6 +1009,20 @@ bool is_type_integer(Type *t) {
|
|||||||
}
|
}
|
||||||
return false;
|
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) {
|
bool is_type_unsigned(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
// t = core_type(t);
|
// t = core_type(t);
|
||||||
@@ -1468,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) {
|
|||||||
if (t->kind == Type_BitSet) {
|
if (t->kind == Type_BitSet) {
|
||||||
t = bit_set_to_int(t);
|
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) {
|
switch (t->Basic.kind) {
|
||||||
// Endian Specific Types
|
// Endian Specific Types
|
||||||
@@ -2148,12 +2159,8 @@ bool are_types_identical(Type *x, Type *y) {
|
|||||||
|
|
||||||
case Type_SimdVector:
|
case Type_SimdVector:
|
||||||
if (y->kind == Type_SimdVector) {
|
if (y->kind == Type_SimdVector) {
|
||||||
if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) {
|
if (x->SimdVector.count == y->SimdVector.count) {
|
||||||
if (x->SimdVector.is_x86_mmx) {
|
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
|
||||||
return true;
|
|
||||||
} else if (x->SimdVector.count == y->SimdVector.count) {
|
|
||||||
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2953,9 +2960,6 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Type_SimdVector: {
|
case Type_SimdVector: {
|
||||||
if (t->SimdVector.is_x86_mmx) {
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
// align of
|
// align of
|
||||||
i64 count = t->SimdVector.count;
|
i64 count = t->SimdVector.count;
|
||||||
Type *elem = t->SimdVector.elem;
|
Type *elem = t->SimdVector.elem;
|
||||||
@@ -3219,9 +3223,6 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Type_SimdVector: {
|
case Type_SimdVector: {
|
||||||
if (t->SimdVector.is_x86_mmx) {
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
i64 count = t->SimdVector.count;
|
i64 count = t->SimdVector.count;
|
||||||
Type *elem = t->SimdVector.elem;
|
Type *elem = t->SimdVector.elem;
|
||||||
return count * type_size_of_internal(elem, path);
|
return count * type_size_of_internal(elem, path);
|
||||||
@@ -3656,12 +3657,8 @@ gbString write_type_to_string(gbString str, Type *type) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Type_SimdVector:
|
case Type_SimdVector:
|
||||||
if (type->SimdVector.is_x86_mmx) {
|
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
|
||||||
return gb_string_appendc(str, "intrinsics.x86_mmx");
|
str = write_type_to_string(str, type->SimdVector.elem);
|
||||||
} else {
|
|
||||||
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
|
|
||||||
str = write_type_to_string(str, type->SimdVector.elem);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type_RelativePointer:
|
case Type_RelativePointer:
|
||||||
|
|||||||
Reference in New Issue
Block a user