Merge remote-tracking branch 'offical/master'

This commit is contained in:
ed
2025-10-04 10:05:37 -04:00
82 changed files with 4715 additions and 1458 deletions
+61 -43
View File
@@ -54,7 +54,12 @@ container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: type
when !NO_DEFAULT_TEMP_ALLOCATOR { when !NO_DEFAULT_TEMP_ALLOCATOR {
when ODIN_ARCH == .i386 && ODIN_OS == .Windows {
// Thread-local storage is problematic on Windows i386
global_default_temp_allocator_data: Default_Temp_Allocator
} else {
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator @thread_local global_default_temp_allocator_data: Default_Temp_Allocator
}
} }
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR) @(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
@@ -65,31 +70,33 @@ init_global_temporary_allocator :: proc(size: int, backup_allocator := context.a
} }
@(require_results)
copy_slice_raw :: proc "contextless" (dst, src: rawptr, dst_len, src_len, elem_size: int) -> int {
n := min(dst_len, src_len)
if n > 0 {
intrinsics.mem_copy(dst, src, n*elem_size)
}
return n
}
// `copy_slice` is a built-in procedure that copies elements from a source slice `src` to a destination slice `dst`. // `copy_slice` is a built-in procedure that copies elements from a source slice `src` to a destination slice `dst`.
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
// of len(src) and len(dst). // of len(src) and len(dst).
// //
// Prefer the procedure group `copy`. // Prefer the procedure group `copy`.
@builtin @builtin
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int { copy_slice :: #force_inline proc "contextless" (dst, src: $T/[]$E) -> int {
n := min(len(dst), len(src)) return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), size_of(E))
if n > 0 {
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(E))
}
return n
} }
// `copy_from_string` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`. // `copy_from_string` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
// of len(src) and len(dst). // of len(src) and len(dst).
// //
// Prefer the procedure group `copy`. // Prefer the procedure group `copy`.
@builtin @builtin
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int { copy_from_string :: #force_inline proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
n := min(len(dst), len(src)) return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), 1)
if n > 0 {
intrinsics.mem_copy(raw_data(dst), raw_data(src), n)
}
return n
} }
// `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`. // `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
@@ -98,12 +105,8 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
// //
// Prefer the procedure group `copy`. // Prefer the procedure group `copy`.
@builtin @builtin
copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int { copy_from_string16 :: #force_inline proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int {
n := min(len(dst), len(src)) return copy_slice_raw(raw_data(dst), raw_data(src), len(dst), len(src), 2)
if n > 0 {
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16))
}
return n
} }
// `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`. // `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
@@ -166,11 +169,17 @@ remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #calle
@builtin @builtin
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
assert(len(array) > 0, loc=loc) assert(len(array) > 0, loc=loc)
res = array[len(array)-1] _pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E))
(^Raw_Dynamic_Array)(array).len -= 1
return res return res
} }
_pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) {
end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1)))
intrinsics.mem_copy_non_overlapping(res, end, elem_size)
array.len -= 1
}
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. // `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
// If the operation is not possible, it will return false. // If the operation is not possible, it will return false.
@@ -334,20 +343,19 @@ 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, require_results) @(builtin, require_results)
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_allocator_error { new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
return new_aligned(T, align_of(T), allocator, loc) t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
return
} }
@(require_results) @(require_results)
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) { new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return t = (^T)(raw_data(mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return))
t = (^T)(raw_data(data))
return return
} }
@(builtin, require_results) @(builtin, require_results)
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error { new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
t_data := mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
t = (^T)(raw_data(t_data))
if t != nil { if t != nil {
t^ = data t^ = data
} }
@@ -357,14 +365,21 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8 DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
@(require_results) @(require_results)
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
err = _make_aligned_type_erased(&res, size_of(E), len, alignment, allocator, loc)
return
}
@(require_results)
_make_aligned_type_erased :: proc(slice: rawptr, elem_size: int, len: int, alignment: int, allocator: Allocator, loc := #caller_location) -> Allocator_Error {
make_slice_error_loc(loc, len) make_slice_error_loc(loc, len)
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc) data, err := mem_alloc_bytes(elem_size*len, alignment, allocator, loc)
if data == nil && size_of(E) != 0 { if data == nil && elem_size != 0 {
return nil, err return err
} }
s := Raw_Slice{raw_data(data), len} (^Raw_Slice)(slice).data = raw_data(data)
return transmute(T)s, err (^Raw_Slice)(slice).len = len
return err
} }
// `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value. // `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value.
@@ -372,24 +387,27 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
// //
// Note: Prefer using the procedure group `make`. // Note: Prefer using the procedure group `make`.
@(builtin, require_results) @(builtin, require_results)
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
return make_aligned(T, len, align_of(E), allocator, loc) err = _make_aligned_type_erased(&res, size_of(E), len, align_of(E), allocator, loc)
return
} }
// `make_dynamic_array` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // `make_dynamic_array` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
// //
// Note: Prefer using the procedure group `make`. // Note: Prefer using the procedure group `make`.
@(builtin, require_results) @(builtin, require_results)
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
return make_dynamic_array_len_cap(T, 0, 0, allocator, loc) err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), 0, 0, allocator, loc)
return
} }
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
// //
// Note: Prefer using the procedure group `make`. // Note: Prefer using the procedure group `make`.
@(builtin, require_results) @(builtin, require_results)
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error { make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
return make_dynamic_array_len_cap(T, len, len, allocator, loc) err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, len, allocator, loc)
return
} }
// `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
@@ -495,7 +513,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
// Note: Prefer the procedure group `reserve` // Note: Prefer the procedure group `reserve`
@builtin @builtin
reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
} }
// Shrinks the capacity of a map down to the current length. // Shrinks the capacity of a map down to the current length.
@@ -524,7 +542,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
return return
} }
_append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil { if array == nil {
return return
} }
@@ -569,7 +587,7 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc :
} }
} }
_append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error { _append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil { if array == nil {
return 0, nil return 0, nil
} }
@@ -818,7 +836,7 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`). // `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
// //
// Note: Prefer the procedure group `reserve`. // Note: Prefer the procedure group `reserve`.
_reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
if a == nil { if a == nil {
return nil return nil
} }
@@ -863,7 +881,7 @@ non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity
} }
_resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { _resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
if a == nil { if a == nil {
return nil return nil
} }
+3
View File
@@ -989,6 +989,9 @@ __dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
// IMPORTANT: USED WITHIN THE COMPILER // IMPORTANT: USED WITHIN THE COMPILER
@(private) @(private)
__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error { __dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error {
if m == nil {
return nil
}
return map_reserve_dynamic(m, info, uintptr(new_capacity), loc) return map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
} }
+13 -1
View File
@@ -28,7 +28,19 @@ when ODIN_BUILD_MODE == .Dynamic {
return true return true
} }
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT { } else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
when ODIN_ARCH == .i386 || ODIN_NO_CRT { when ODIN_ARCH == .i386 && !ODIN_NO_CRT {
// Windows i386 with CRT: libcmt provides mainCRTStartup which calls _main
// Note: "c" calling convention adds underscore prefix automatically on i386
@(link_name="main", linkage="strong", require)
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
args__ = argv[:argc]
context = default_context()
#force_no_inline _startup_runtime()
intrinsics.__entry_point()
#force_no_inline _cleanup_runtime()
return 0
}
} else when ODIN_NO_CRT {
@(link_name="mainCRTStartup", linkage="strong", require) @(link_name="mainCRTStartup", linkage="strong", require)
mainCRTStartup :: proc "system" () -> i32 { mainCRTStartup :: proc "system" () -> i32 {
context = default_context() context = default_context()
+3 -1
View File
@@ -71,10 +71,12 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
when ODIN_OS != .Windows {
// NOTE: heap_resize does not zero the new memory, so we do it // NOTE: heap_resize does not zero the new memory, so we do it
if zero_memory && new_size > old_size { if zero_memory && new_size > old_size {
new_region := raw_data(new_memory[old_size:]) new_region := raw_data(new_memory[old_size:])
intrinsics.mem_zero(new_region, new_size - old_size) conditional_mem_zero(new_region, new_size - old_size)
}
} }
return return
} }
+59 -10
View File
@@ -123,7 +123,7 @@ 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_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { mem_alloc_bytes :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
if size == 0 || allocator.procedure == nil{ if size == 0 || allocator.procedure == nil{
return nil, nil return nil, nil
@@ -131,7 +131,7 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
} }
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { mem_alloc :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
if size == 0 || allocator.procedure == nil { if size == 0 || allocator.procedure == nil {
return nil, nil return nil, nil
@@ -139,7 +139,7 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
} }
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { mem_alloc_non_zeroed :: #force_no_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
if size == 0 || allocator.procedure == nil { if size == 0 || allocator.procedure == nil {
return nil, nil return nil, nil
@@ -147,7 +147,7 @@ mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_A
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc) return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
} }
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { mem_free :: #force_no_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil { if ptr == nil || allocator.procedure == nil {
return nil return nil
} }
@@ -155,7 +155,7 @@ mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc
return err return err
} }
mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { mem_free_with_size :: #force_no_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil { if ptr == nil || allocator.procedure == nil {
return nil return nil
} }
@@ -163,7 +163,7 @@ mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator
return err return err
} }
mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { mem_free_bytes :: #force_no_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if bytes == nil || allocator.procedure == nil { if bytes == nil || allocator.procedure == nil {
return nil return nil
} }
@@ -172,14 +172,14 @@ mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocat
} }
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { mem_free_all :: #force_no_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
if allocator.procedure != nil { if allocator.procedure != nil {
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc) _, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
} }
return return
} }
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { _mem_resize :: #force_no_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
if allocator.procedure == nil { if allocator.procedure == nil {
return nil, nil return nil, nil
@@ -230,6 +230,55 @@ non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc) return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
} }
conditional_mem_zero :: proc "contextless" (data: rawptr, n_: int) #no_bounds_check {
// When acquiring memory from the OS for the first time it's likely that the
// OS already gives the zero page mapped multiple times for the request. The
// actual allocation does not have physical pages allocated to it until those
// pages are written to which causes a page-fault. This is often called COW
// (Copy on Write)
//
// You do not want to actually zero out memory in this case because it would
// cause a bunch of page faults decreasing the speed of allocations and
// increase the amount of actual resident physical memory used.
//
// Instead a better technique is to check if memory is zerored before zeroing
// it. This turns out to be an important optimization in practice, saving
// nearly half (or more) the amount of physical memory used by an application.
// This is why every implementation of calloc in libc does this optimization.
//
// It may seem counter-intuitive but most allocations in an application are
// wasted and never used. When you consider something like a [dynamic]T which
// always doubles in capacity on resize but you rarely ever actually use the
// full capacity of a dynamic array it means you have a lot of resident waste
// if you actually zeroed the remainder of the memory.
//
// Keep in mind the OS is already guaranteed to give you zeroed memory by
// mapping in this zero page multiple times so in the best case there is no
// need to actually zero anything. As for testing all this memory for a zero
// value, it costs nothing because the the same zero page is used for the
// whole allocation and will exist in L1 cache for the entire zero checking
// process.
if n_ <= 0 {
return
}
n := uint(n_)
n_words := n / size_of(uintptr)
p_words := ([^]uintptr)(data)[:n_words]
p_bytes := ([^]byte)(data)[size_of(uintptr) * n_words:n]
for &p_word in p_words {
if p_word != 0 {
p_word = 0
}
}
for &p_byte in p_bytes {
if p_byte != 0 {
p_byte = 0
}
}
}
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool { memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
switch { switch {
case n == 0: return true case n == 0: return true
@@ -667,7 +716,7 @@ quaternion256_eq :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b) } quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b) }
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) { string_decode_rune :: proc "contextless" (s: string) -> (rune, int) {
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8 // NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
@(static, rodata) accept_sizes := [256]u8{ @(static, rodata) accept_sizes := [256]u8{
@@ -782,7 +831,7 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
} }
string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) { string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) {
REPLACEMENT_CHAR :: '\ufffd' REPLACEMENT_CHAR :: '\ufffd'
_surr1 :: 0xd800 _surr1 :: 0xd800
_surr2 :: 0xdc00 _surr2 :: 0xdc00
+5
View File
@@ -1 +1,6 @@
comment: false comment: false
coverage:
status:
project:
default:
threshold: 1%
+1 -1
View File
@@ -275,7 +275,7 @@ foreign libc {
// 7.21.7 Character input/output functions // 7.21.7 Character input/output functions
fgetc :: proc(stream: ^FILE) -> int --- fgetc :: proc(stream: ^FILE) -> int ---
fgets :: proc(s: [^]char, n: int, stream: ^FILE) -> [^]char --- fgets :: proc(s: [^]char, n: int, stream: ^FILE) -> [^]char ---
fputc :: proc(s: cstring, stream: ^FILE) -> int --- fputc :: proc(s: c.int, stream: ^FILE) -> int ---
getc :: proc(stream: ^FILE) -> int --- getc :: proc(stream: ^FILE) -> int ---
getchar :: proc() -> int --- getchar :: proc() -> int ---
putc :: proc(c: int, stream: ^FILE) -> int --- putc :: proc(c: int, stream: ^FILE) -> int ---
+52 -6
View File
@@ -1,8 +1,8 @@
package container_small_array package container_small_array
import "base:builtin" import "base:builtin"
import "base:runtime" @require import "base:intrinsics"
_ :: runtime @require import "base:runtime"
/* /*
A fixed-size stack-allocated array operated on in a dynamic fashion. A fixed-size stack-allocated array operated on in a dynamic fashion.
@@ -169,7 +169,7 @@ Output:
x x
*/ */
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check { get_safe :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
if index < 0 || index >= a.len { if index < 0 || index >= a.len {
return {}, false return {}, false
} }
@@ -187,7 +187,7 @@ Get a pointer to the item at the specified position.
- the pointer to the element at the specified position - the pointer to the element at the specified position
- true if element exists, false otherwise - true if element exists, false otherwise
*/ */
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check { get_ptr_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
if index < 0 || index >= a.len { if index < 0 || index >= a.len {
return {}, false return {}, false
} }
@@ -231,7 +231,7 @@ Example:
fmt.println(small_array.slice(&a)) fmt.println(small_array.slice(&a))
// resizing makes the change visible // resizing makes the change visible
small_array.resize(&a, 100) small_array.non_zero_resize(&a, 100)
fmt.println(small_array.slice(&a)) fmt.println(small_array.slice(&a))
} }
@@ -250,6 +250,8 @@ set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
/* /*
Tries to resize the small-array to the specified length. Tries to resize the small-array to the specified length.
The memory of added elements will be zeroed out.
The new length will be: The new length will be:
- `length` if `length` <= capacity - `length` if `length` <= capacity
- capacity if length > capacity - capacity if length > capacity
@@ -277,13 +279,57 @@ Example:
fmt.println(small_array.slice(&a)) fmt.println(small_array.slice(&a))
} }
Output:
[1, 2]
[1]
[1, 0, 0, 0, 0]
*/
resize :: proc "contextless" (a: ^$A/Small_Array($N, $T), length: int) {
prev_len := a.len
a.len = min(length, builtin.len(a.data))
if prev_len < a.len {
intrinsics.mem_zero(&a.data[prev_len], size_of(T)*(a.len-prev_len))
}
}
/*
Tries to resize the small-array to the specified length.
The new length will be:
- `length` if `length` <= capacity
- capacity if length > capacity
**Inputs**
- `a`: A pointer to the small-array
- `length`: The new desired length
Example:
import "core:container/small_array"
import "core:fmt"
non_zero_resize :: proc() {
a: small_array.Small_Array(5, int)
small_array.push_back(&a, 1)
small_array.push_back(&a, 2)
fmt.println(small_array.slice(&a))
small_array.non_zero_resize(&a, 1)
fmt.println(small_array.slice(&a))
small_array.non_zero_resize(&a, 100)
fmt.println(small_array.slice(&a))
}
Output: Output:
[1, 2] [1, 2]
[1] [1]
[1, 2, 0, 0, 0] [1, 2, 0, 0, 0]
*/ */
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) { non_zero_resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
a.len = min(length, builtin.len(a.data)) a.len = min(length, builtin.len(a.data))
} }
+1
View File
@@ -1,3 +1,4 @@
#+test
package encoding_base32 package encoding_base32
import "core:testing" import "core:testing"
+144 -2
View File
@@ -64,8 +64,16 @@ Image_Metadata :: union #shared_nil {
^QOI_Info, ^QOI_Info,
^TGA_Info, ^TGA_Info,
^BMP_Info, ^BMP_Info,
^JPEG_Info,
} }
Exif :: struct {
byte_order: enum {
little_endian,
big_endian,
},
data: []u8 `fmt:"-"`,
}
/* /*
@@ -112,8 +120,7 @@ Image_Option:
`.alpha_drop_if_present` `.alpha_drop_if_present`
If the image has an alpha channel, drop it. If the image has an alpha channel, drop it.
You may want to use `.alpha_ You may want to use `.alpha_premultiply` in this case.
tiply` in this case.
NOTE: For PNG, this also skips handling of the tRNS chunk, if present, NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
unless you select `alpha_premultiply`. unless you select `alpha_premultiply`.
@@ -163,6 +170,7 @@ Error :: union #shared_nil {
PNG_Error, PNG_Error,
QOI_Error, QOI_Error,
BMP_Error, BMP_Error,
JPEG_Error,
compress.Error, compress.Error,
compress.General_Error, compress.General_Error,
@@ -575,6 +583,140 @@ TGA_Info :: struct {
extension: Maybe(TGA_Extension), extension: Maybe(TGA_Extension),
} }
/*
JPEG-specific
*/
JFIF_Magic := [?]byte{0x4A, 0x46, 0x49, 0x46} // "JFIF"
JFXX_Magic := [?]byte{0x4A, 0x46, 0x58, 0x58} // "JFXX"
Exif_Magic := [?]byte{0x45, 0x78, 0x69, 0x66} // "Exif"
JPEG_Error :: enum {
None = 0,
Duplicate_SOI_Marker,
Invalid_JFXX_Extension_Code,
Encountered_SOS_Before_SOF,
Invalid_Quantization_Table_Precision,
Invalid_Quantization_Table_Index,
Invalid_Huffman_Coefficient_Type,
Invalid_Huffman_Table_Index,
Unsupported_Frame_Type,
Invalid_Frame_Bit_Depth_Combo,
Invalid_Sampling_Factor,
Unsupported_12_Bit_Depth,
Multiple_SOS_Markers,
Encountered_RST_Marker_Outside_ECS,
Extra_Data_After_SOS, // Image seemed to have decoded okay, but there's more data after SOS
Invalid_Thumbnail_Size,
Huffman_Symbols_Exceeds_Max,
}
JFIF_Unit :: enum byte {
None = 0,
Dots_Per_Inch = 1,
Dots_Per_Centimeter = 2,
}
JFIF_APP0 :: struct {
version: u16be,
x_density: u16be,
y_density: u16be,
units: JFIF_Unit,
x_thumbnail: u8,
y_thumbnail: u8,
greyscale_thumbnail: bool,
thumbnail: []RGB_Pixel `fmt:"-"`,
}
JFXX_APP0 :: struct {
extension_code: JFXX_Extension_Code,
x_thumbnail: u8,
y_thumbnail: u8,
thumbnail: []byte `fmt:"-"`,
}
JFXX_Extension_Code :: enum u8 {
Thumbnail_JPEG = 0x10,
Thumbnail_1_Byte_Palette = 0x11,
Thumbnail_3_Byte_RGB = 0x13,
}
JPEG_Marker :: enum u8 {
SOF0 = 0xC0, // Baseline sequential DCT
SOF1 = 0xC1, // Extended sequential DCT
SOF2 = 0xC2, // Progressive DCT
SOF3 = 0xC3, // Lossless (sequential)
SOF5 = 0xC5, // Differential sequential DCT
SOF6 = 0xC6, // Differential progressive DCT
SOF7 = 0xC7, // Differential lossless (sequential)
SOF9 = 0xC9, // Extended sequential DCT, Arithmetic coding
SOF10 = 0xCA, // Progressive DCT, Arithmetic coding
SOF11 = 0xCB, // Lossless (sequential), Arithmetic coding
SOF13 = 0xCD, // Differential sequential DCT, Arithmetic coding
SOF14 = 0xCE, // Differential progressive DCT, Arithmetic coding
SOF15 = 0xCF, // Differential lossless (sequential), Arithmetic coding
DHT = 0xC4,
JPG = 0xC8,
DAC = 0xCC,
RST0 = 0xD0,
RST1 = 0xD1,
RST2 = 0xD2,
RST3 = 0xD3,
RST4 = 0xD4,
RST5 = 0xD5,
RST6 = 0xD6,
RST7 = 0xD7,
SOI = 0xD8,
EOI = 0xD9,
SOS = 0xDA,
DQT = 0xDB,
DNL = 0xDC,
DRI = 0xDD,
DHP = 0xDE,
EXP = 0xDF,
APP0 = 0xE0,
APP1 = 0xE1,
APP2 = 0xE2,
APP3 = 0xE3,
APP4 = 0xE4,
APP5 = 0xE5,
APP6 = 0xE6,
APP7 = 0xE7,
APP8 = 0xE8,
APP9 = 0xE9,
APP10 = 0xEA,
APP11 = 0xEB,
APP12 = 0xEC,
APP13 = 0xED,
APP14 = 0xEE,
APP15 = 0xEF,
JPG0 = 0xF0,
JPG1 = 0xF1,
JPG2 = 0xF2,
JPG3 = 0xF3,
JPG4 = 0xF4,
JPG5 = 0xF5,
JPG6 = 0xF6,
JPG7 = 0xF7,
JPG8 = 0xF8,
JPG9 = 0xF9,
JPG10 = 0xFA,
JPG11 = 0xFB,
JPG12 = 0xFC,
JPG13 = 0xFD,
COM = 0xFE,
TEM = 0x01,
}
JPEG_Info :: struct {
jfif_app0: Maybe(JFIF_APP0),
jfxx_app0: Maybe(JFXX_APP0),
comments: [dynamic]string,
exif: [dynamic]Exif,
frame_type: JPEG_Marker,
}
// Function to help with image buffer calculations // Function to help with image buffer calculations
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
+1 -1
View File
@@ -147,7 +147,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
return .JPEG return .JPEG
case s[:3] == "\xff\xd8\xff": case s[:3] == "\xff\xd8\xff":
switch s[3] { switch s[3] {
case 0xdb, 0xee, 0xe1, 0xe0: case 0xdb, 0xee, 0xe1, 0xe0, 0xfe, 0xed:
return .JPEG return .JPEG
} }
switch { switch {
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
package jpeg
load :: proc{load_from_bytes, load_from_context}
+18
View File
@@ -0,0 +1,18 @@
package jpeg
import "core:os"
load :: proc{load_from_file, load_from_bytes, load_from_context}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
+1 -1
View File
@@ -366,7 +366,7 @@ chrm :: proc(c: image.PNG_Chunk) -> (res: cHRM, ok: bool) {
return return
} }
exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) { exif :: proc(c: image.PNG_Chunk) -> (res: image.Exif, ok: bool) {
ok = true ok = true
+5 -9
View File
@@ -138,14 +138,6 @@ Text :: struct {
text: string, text: string,
} }
Exif :: struct {
byte_order: enum {
little_endian,
big_endian,
},
data: []u8,
}
iCCP :: struct { iCCP :: struct {
name: string, name: string,
profile: []u8, profile: []u8,
@@ -250,10 +242,14 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
header := (^image.PNG_IHDR)(raw_data(c.data))^ header := (^image.PNG_IHDR)(raw_data(c.data))^
// Validate IHDR // Validate IHDR
using header using header
if width == 0 || height == 0 || u128(width) * u128(height) > image.MAX_DIMENSIONS { if width == 0 || height == 0 {
return {}, .Invalid_Image_Dimensions return {}, .Invalid_Image_Dimensions
} }
if u128(width) * u128(height) > image.MAX_DIMENSIONS {
return {}, .Image_Dimensions_Too_Large
}
if compression_method != 0 { if compression_method != 0 {
return {}, compress.General_Error.Unknown_Compression_Method return {}, compress.General_Error.Unknown_Compression_Method
} }
+17
View File
@@ -189,6 +189,23 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
write_encoded_rune(w, r, false, &n) or_return write_encoded_rune(w, r, false, &n) or_return
return return
} }
if r < 32 && for_json {
switch r {
case '\b': write_string(w, `\b`, &n) or_return
case '\f': write_string(w, `\f`, &n) or_return
case '\n': write_string(w, `\n`, &n) or_return
case '\r': write_string(w, `\r`, &n) or_return
case '\t': write_string(w, `\t`, &n) or_return
case:
write_byte(w, '\\', &n) or_return
write_byte(w, 'u', &n) or_return
write_byte(w, '0', &n) or_return
write_byte(w, '0', &n) or_return
write_byte(w, DIGITS_LOWER[r>>4 & 0xf], &n) or_return
write_byte(w, DIGITS_LOWER[r & 0xf], &n) or_return
}
return
}
switch r { switch r {
case '\a': write_string(w, `\a`, &n) or_return case '\a': write_string(w, `\a`, &n) or_return
case '\b': write_string(w, `\b`, &n) or_return case '\b': write_string(w, `\b`, &n) or_return
+1 -12
View File
@@ -14,23 +14,12 @@ import "base:intrinsics"
This allows to benchmark and/or setting optimized values for a certain CPU without recompiling. This allows to benchmark and/or setting optimized values for a certain CPU without recompiling.
*/ */
/*
========================== TUNABLES ==========================
`initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)`
and we initialize this cutoff that way so that the procedure is used and called,
because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF.
`initialize_constants` also replaces the other `_DEFAULT_*` cutoffs with custom compile-time values if so `#config`ured.
*/
/* /*
There is a bug with DLL globals. They don't get set. There is a bug with DLL globals. They don't get set.
To allow tests to run we add `-define:MATH_BIG_EXE=false` to hardcode the cutoffs for now. To allow tests to run we add `-define:MATH_BIG_EXE=false` to hardcode the cutoffs for now.
*/ */
when #config(MATH_BIG_EXE, true) { when #config(MATH_BIG_EXE, true) {
MUL_KARATSUBA_CUTOFF := initialize_constants() MUL_KARATSUBA_CUTOFF := _DEFAULT_MUL_KARATSUBA_CUTOFF
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF
+11 -8
View File
@@ -778,13 +778,14 @@ int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, alloca
*/ */
INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{} INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
@(init, private) @(private)
_init_constants :: proc "contextless" () { constant_allocator: runtime.Allocator
initialize_constants()
}
initialize_constants :: proc "contextless" () -> (res: int) { @(init, private)
initialize_constants :: proc "contextless" () {
context = runtime.default_context() context = runtime.default_context()
constant_allocator = context.allocator
internal_int_set_from_integer( INT_ZERO, 0); INT_ZERO.flags = {.Immutable} internal_int_set_from_integer( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}
internal_int_set_from_integer( INT_ONE, 1); INT_ONE.flags = {.Immutable} internal_int_set_from_integer( INT_ONE, 1); INT_ONE.flags = {.Immutable}
internal_int_set_from_integer(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable} internal_int_set_from_integer(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable}
@@ -796,15 +797,17 @@ initialize_constants :: proc "contextless" () -> (res: int) {
internal_int_set_from_integer( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN} internal_int_set_from_integer( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}
internal_int_set_from_integer( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf} internal_int_set_from_integer( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}
internal_int_set_from_integer(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf} internal_int_set_from_integer(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
return _DEFAULT_MUL_KARATSUBA_CUTOFF
} }
/* /*
Destroy constants. Destroy constants.
Optional for an EXE, as this would be called at the very end of a process. Optional for an EXE, as this would be called at the very end of a process.
*/ */
destroy_constants :: proc() { @(fini, private)
destroy_constants :: proc "contextless" () {
context = runtime.default_context()
context.allocator = constant_allocator
internal_destroy(INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN) internal_destroy(INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN)
} }
+8 -8
View File
@@ -1819,18 +1819,18 @@ memory region.
*/ */
@(require_results) @(require_results)
dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
n := align_formula(size, a.alignment) if size >= a.out_band_size {
if n > a.block_size { assert(a.out_band_allocations.allocator.procedure != nil, "Backing array allocator must be initialized", loc=loc)
return nil, .Invalid_Argument memory, err := alloc_bytes_non_zeroed(size, a.alignment, a.out_band_allocations.allocator, loc)
}
if n >= a.out_band_size {
assert(a.block_allocator.procedure != nil, "Backing block allocator must be initialized", loc=loc)
memory, err := alloc_bytes_non_zeroed(a.block_size, a.alignment, a.block_allocator, loc)
if memory != nil { if memory != nil {
append(&a.out_band_allocations, raw_data(memory), loc = loc) append(&a.out_band_allocations, raw_data(memory), loc = loc)
} }
return memory, err return memory, err
} }
n := align_formula(size, a.alignment)
if n > a.block_size {
return nil, .Invalid_Argument
}
if a.bytes_left < n { if a.bytes_left < n {
err := _dynamic_arena_cycle_new_block(a, loc) err := _dynamic_arena_cycle_new_block(a, loc)
if err != nil { if err != nil {
@@ -1867,7 +1867,7 @@ dynamic_arena_reset :: proc(a: ^Dynamic_Arena, loc := #caller_location) {
} }
clear(&a.used_blocks) clear(&a.used_blocks)
for allocation in a.out_band_allocations { for allocation in a.out_band_allocations {
free(allocation, a.block_allocator, loc=loc) free(allocation, a.out_band_allocations.allocator, loc=loc)
} }
clear(&a.out_band_allocations) clear(&a.out_band_allocations)
a.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. a.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again.
+16 -2
View File
@@ -108,6 +108,16 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
} }
sync.mutex_guard(&arena.mutex) sync.mutex_guard(&arena.mutex)
return arena_alloc_unguarded(arena, size, alignment, loc)
}
// Allocates memory from the provided arena.
@(require_results, no_sanitize_address, private)
arena_alloc_unguarded :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
size := size
if size == 0 {
return nil, nil
}
switch arena.kind { switch arena.kind {
case .Growing: case .Growing:
@@ -345,7 +355,11 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case size == 0: case size == 0:
err = .Mode_Not_Implemented err = .Mode_Not_Implemented
return return
case uintptr(old_data) & uintptr(alignment-1) == 0: }
sync.mutex_guard(&arena.mutex)
if uintptr(old_data) & uintptr(alignment-1) == 0 {
if size < old_size { if size < old_size {
// shrink data in-place // shrink data in-place
data = old_data[:size] data = old_data[:size]
@@ -369,7 +383,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
} }
} }
new_memory := arena_alloc(arena, size, alignment, location) or_return new_memory := arena_alloc_unguarded(arena, size, alignment, location) or_return
if new_memory == nil { if new_memory == nil {
return return
} }
+1 -1
View File
@@ -2485,7 +2485,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
allow_token(p, .Comma) or_break allow_token(p, .Comma) or_break
} }
close := expect_token(p, .Close_Brace) close := expect_closing_brace_of_field_list(p)
if len(args) == 0 { if len(args) == 0 {
error(p, tok.pos, "expected at least 1 argument in procedure group") error(p, tok.pos, "expected at least 1 argument in procedure group")
+5 -7
View File
@@ -18,7 +18,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({}) temp_allocator := TEMP_ALLOCATOR_GUARD({})
dir := dir if dir != "" else temp_directory(temp_allocator) or_return dir := dir if dir != "" else temp_directory(temp_allocator) or_return
prefix, suffix := _prefix_and_suffix(pattern) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return prefix = temp_join_path(dir, prefix, temp_allocator) or_return
rand_buf: [10]byte rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator) name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator)
@@ -50,7 +50,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator)
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
dir := dir if dir != "" else temp_directory(temp_allocator) or_return dir := dir if dir != "" else temp_directory(temp_allocator) or_return
prefix, suffix := _prefix_and_suffix(pattern) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return prefix = temp_join_path(dir, prefix, temp_allocator) or_return
rand_buf: [10]byte rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator) name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator)
@@ -88,12 +88,10 @@ temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) {
@(private="file") @(private="file")
temp_join_path :: proc(dir, name: string) -> (string, runtime.Allocator_Error) { temp_join_path :: proc(dir, name: string, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({})
if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) { if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) {
return concatenate({dir, name}, temp_allocator,) return concatenate({dir, name}, allocator)
} }
return concatenate({dir, Path_Separator_String, name}, temp_allocator) return concatenate({dir, Path_Separator_String, name}, allocator)
} }
+12 -15
View File
@@ -32,7 +32,6 @@ is_UNC :: proc(path: string) -> bool {
return volume_name_len(path) > 2 return volume_name_len(path) > 2
} }
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
if is_reserved_name(path) { if is_reserved_name(path) {
return true return true
@@ -50,7 +49,6 @@ is_abs :: proc(path: string) -> bool {
return is_slash(path[0]) return is_slash(path[0])
} }
@(private) @(private)
temp_full_path :: proc(name: string) -> (path: string, err: os.Error) { temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
ta := context.temp_allocator ta := context.temp_allocator
@@ -76,8 +74,6 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
return win32.utf16_to_utf8(buf[:n], ta) return win32.utf16_to_utf8(buf[:n], ta)
} }
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path) full_path, err := temp_full_path(path)
@@ -88,17 +84,16 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
return p, true return p, true
} }
join :: proc(elems: []string, allocator := context.allocator) -> (string, runtime.Allocator_Error) #optional_allocator_error {
join :: proc(elems: []string, allocator := context.allocator) -> string {
for e, i in elems { for e, i in elems {
if e != "" { if e != "" {
return join_non_empty(elems[i:], allocator) return join_non_empty(elems[i:], allocator)
} }
} }
return "" return "", nil
} }
join_non_empty :: proc(elems: []string, allocator := context.allocator) -> string { join_non_empty :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) {
context.allocator = allocator context.allocator = allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
@@ -110,23 +105,25 @@ join_non_empty :: proc(elems: []string, allocator := context.allocator) -> strin
break break
} }
} }
s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return
s = strings.concatenate({elems[0], s}, context.temp_allocator) s = strings.concatenate({elems[0], s}, context.temp_allocator) or_return
return clean(s) return clean(s)
} }
p := clean(strings.join(elems, SEPARATOR_STRING, context.temp_allocator)) p := strings.join(elems, SEPARATOR_STRING, context.temp_allocator) or_return
p = clean(p) or_return
if !is_UNC(p) { if !is_UNC(p) {
return p return p, nil
} }
head := clean(elems[0], context.temp_allocator) head := clean(elems[0], context.temp_allocator) or_return
if is_UNC(head) { if is_UNC(head) {
return p return p, nil
} }
delete(p) // It is not needed now delete(p) // It is not needed now
tail := clean(strings.join(elems[1:], SEPARATOR_STRING, context.temp_allocator), context.temp_allocator) tail := strings.join(elems[1:], SEPARATOR_STRING, context.temp_allocator) or_return
tail = clean(tail, context.temp_allocator) or_return
if head[len(head)-1] == SEPARATOR { if head[len(head)-1] == SEPARATOR {
return strings.concatenate({head, tail}) return strings.concatenate({head, tail})
} }
+7
View File
@@ -1655,6 +1655,13 @@ Output:
write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size)) return string(generic_ftoa(buf, f, fmt, prec, bit_size))
} }
// Accepts '0'..='9', otherwise returns ok = false
digit_to_int :: proc(r: rune) -> (value: int, ok: bool) {
if '0' <= r && r <= '9' {
return int(r - '0'), true
}
return -1, false
}
/* /*
Writes a quoted string representation of the input string to a given byte slice and returns the result as a string Writes a quoted string representation of the input string to a given byte slice and returns the result as a string
+6 -6
View File
@@ -296,8 +296,8 @@ Inputs:
Returns: Returns:
- res: A cstring of the Builder's buffer - res: A cstring of the Builder's buffer
*/ */
unsafe_to_cstring :: proc(b: ^Builder) -> (res: cstring) { unsafe_to_cstring :: proc(b: ^Builder, loc := #caller_location) -> (res: cstring) {
append(&b.buf, 0) append(&b.buf, 0, loc)
pop(&b.buf) pop(&b.buf)
return cstring(raw_data(b.buf)) return cstring(raw_data(b.buf))
} }
@@ -311,8 +311,8 @@ Returns:
- res: A cstring of the Builder's buffer upon success - res: A cstring of the Builder's buffer upon success
- err: An optional allocator error if one occured, `nil` otherwise - err: An optional allocator error if one occured, `nil` otherwise
*/ */
to_cstring :: proc(b: ^Builder) -> (res: cstring, err: mem.Allocator_Error) #optional_allocator_error { to_cstring :: proc(b: ^Builder, loc := #caller_location) -> (res: cstring, err: mem.Allocator_Error) #optional_allocator_error {
n := append(&b.buf, 0) or_return n := append(&b.buf, 0, loc) or_return
if n != 1 { if n != 1 {
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
@@ -518,9 +518,9 @@ Output:
abc abc
*/ */
write_string :: proc(b: ^Builder, s: string) -> (n: int) { write_string :: proc(b: ^Builder, s: string, loc := #caller_location) -> (n: int) {
n0 := len(b.buf) n0 := len(b.buf)
append(&b.buf, s) append(&b.buf, s, loc)
n1 := len(b.buf) n1 := len(b.buf)
return n1-n0 return n1-n0
} }
+4 -2
View File
@@ -1,10 +1,12 @@
package CoreFoundation package CoreFoundation
import "core:c"
foreign import CoreFoundation "system:CoreFoundation.framework" foreign import CoreFoundation "system:CoreFoundation.framework"
String :: distinct TypeRef // same as CFStringRef String :: distinct TypeRef // same as CFStringRef
StringEncoding :: distinct u32 StringEncoding :: distinct c.long
StringBuiltInEncodings :: enum StringEncoding { StringBuiltInEncodings :: enum StringEncoding {
MacRoman = 0, MacRoman = 0,
@@ -171,7 +173,7 @@ foreign CoreFoundation {
// Fetches a range of the characters from a string into a byte buffer after converting the characters to a specified encoding. // Fetches a range of the characters from a string into a byte buffer after converting the characters to a specified encoding.
StringGetBytes :: proc(thestring: String, range: Range, encoding: StringEncoding, lossByte: u8, isExternalRepresentation: b8, buffer: [^]byte, maxBufLen: Index, usedBufLen: ^Index) -> Index --- StringGetBytes :: proc(thestring: String, range: Range, encoding: StringEncoding, lossByte: u8, isExternalRepresentation: b8, buffer: [^]byte, maxBufLen: Index, usedBufLen: ^Index) -> Index ---
StringIsEncodingAvailable :: proc(encoding: StringEncoding) -> bool --- StringIsEncodingAvailable :: proc(encoding: StringEncoding) -> b8 ---
@(link_name = "__CFStringMakeConstantString") @(link_name = "__CFStringMakeConstantString")
StringMakeConstantString :: proc "c" (#const c: cstring) -> String --- StringMakeConstantString :: proc "c" (#const c: cstring) -> String ---
+15 -3
View File
@@ -50,10 +50,22 @@ NotificationCenter_defaultCenter :: proc "c" () -> ^NotificationCenter {
return msgSend(^NotificationCenter, NotificationCenter, "defaultCenter") return msgSend(^NotificationCenter, NotificationCenter, "defaultCenter")
} }
@(objc_type=NotificationCenter, objc_name="addObserver") @(objc_type=NotificationCenter, objc_name="addObserverForName")
NotificationCenter_addObserverName :: proc "c" (self: ^NotificationCenter, name: NotificationName, pObj: ^Object, pQueue: rawptr, block: ^Block) -> ^Object { NotificationCenter_addObserverForName :: proc{NotificationCenter_addObserverForName_old, NotificationCenter_addObserverForName_new}
return msgSend(^Object, self, "addObserverName:object:queue:block:", name, pObj, pQueue, block)
NotificationCenter_addObserverForName_old :: proc "c" (self: ^NotificationCenter, name: NotificationName, pObj: ^Object, pQueue: rawptr, block: ^Block) -> ^Object {
return msgSend(^Object, self, "addObserverForName:object:queue:usingBlock:", name, pObj, pQueue, block)
} }
NotificationCenter_addObserverForName_new :: proc "c" (self: ^NotificationCenter, name: NotificationName, pObj: ^Object, pQueue: rawptr, block: ^Objc_Block) -> ^Object {
return msgSend(^Object, self, "addObserverForName:object:queue:usingBlock:", name, pObj, pQueue, block)
}
@(objc_type=NotificationCenter, objc_name="addObserver")
NotificationCenter_addObserver :: proc "c" (self: ^NotificationCenter, observer: ^Object, selector: SEL, name: NotificationName, object: ^Object) {
msgSend(nil, self, "addObserver:selector:name:object:", observer, selector, name, object)
}
@(objc_type=NotificationCenter, objc_name="removeObserver") @(objc_type=NotificationCenter, objc_name="removeObserver")
NotificationCenter_removeObserver :: proc "c" (self: ^NotificationCenter, pObserver: ^Object) { NotificationCenter_removeObserver :: proc "c" (self: ^NotificationCenter, pObserver: ^Object) {
msgSend(nil, self, "removeObserver:", pObserver) msgSend(nil, self, "removeObserver:", pObserver)
-1
View File
@@ -30,7 +30,6 @@ Example:
fmt.printfln("OS: %v", si.os_version.as_string) fmt.printfln("OS: %v", si.os_version.as_string)
fmt.printfln("OS: %#v", si.os_version) fmt.printfln("OS: %#v", si.os_version)
fmt.printfln("CPU: %v", si.cpu.name) fmt.printfln("CPU: %v", si.cpu.name)
fmt.printfln("CPU: %v", si.cpu.name)
fmt.printfln("CPU cores: %vc/%vt", si.cpu.physical_cores, si.cpu.logical_cores) fmt.printfln("CPU cores: %vc/%vt", si.cpu.physical_cores, si.cpu.logical_cores)
fmt.printfln("RAM: %#.1M", si.ram.total_ram) fmt.printfln("RAM: %#.1M", si.ram.total_ram)
-1
View File
@@ -50,7 +50,6 @@ foreign lib {
af: AF, // INET or INET6 af: AF, // INET or INET6
src: cstring, src: cstring,
dst: rawptr, // either ^in_addr or ^in_addr6 dst: rawptr, // either ^in_addr or ^in_addr6
size: socklen_t, // size_of(dst^)
) -> pton_result --- ) -> pton_result ---
} }
+2 -2
View File
@@ -124,7 +124,7 @@ foreign lib {
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]] [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]]
*/ */
pthread_attr_setscope :: proc(attr: ^pthread_attr_t, contentionscope: ^Thread_Scope) -> Errno --- pthread_attr_setscope :: proc(attr: ^pthread_attr_t, contentionscope: Thread_Scope) -> Errno ---
/* /*
Get the area of storage to be used for the created thread's stack. Get the area of storage to be used for the created thread's stack.
@@ -400,7 +400,7 @@ when ODIN_OS == .Darwin {
PTHREAD_SCOPE_PROCESS :: 2 PTHREAD_SCOPE_PROCESS :: 2
PTHREAD_SCOPE_SYSTEM :: 1 PTHREAD_SCOPE_SYSTEM :: 1
pthread_t :: distinct u64 pthread_t :: distinct rawptr
pthread_attr_t :: struct { pthread_attr_t :: struct {
__sig: c.long, __sig: c.long,
+5
View File
@@ -92,7 +92,12 @@ foreign lib {
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html ]] [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html ]]
*/ */
when ODIN_OS == .Darwin {
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/shm_open.2.html
shm_open :: proc(name: cstring, oflag: O_Flags, #c_vararg args: ..any) -> FD ---
} else {
shm_open :: proc(name: cstring, oflag: O_Flags, mode: mode_t) -> FD --- shm_open :: proc(name: cstring, oflag: O_Flags, mode: mode_t) -> FD ---
}
/* /*
Removes a shared memory object. Removes a shared memory object.
+2
View File
@@ -413,6 +413,7 @@ foreign kernel32 {
lpBytesLeftThisMessage: ^u32, lpBytesLeftThisMessage: ^u32,
) -> BOOL --- ) -> BOOL ---
CancelIo :: proc(handle: HANDLE) -> BOOL --- CancelIo :: proc(handle: HANDLE) -> BOOL ---
CancelIoEx :: proc(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) -> BOOL ---
GetOverlappedResult :: proc( GetOverlappedResult :: proc(
hFile: HANDLE, hFile: HANDLE,
lpOverlapped: LPOVERLAPPED, lpOverlapped: LPOVERLAPPED,
@@ -554,6 +555,7 @@ foreign kernel32 {
GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL --- GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL ---
RtlCaptureStackBackTrace :: proc(FramesToSkip: ULONG, FramesToCapture: ULONG, BackTrace: [^]PVOID, BackTraceHash: PULONG) -> USHORT --- RtlCaptureStackBackTrace :: proc(FramesToSkip: ULONG, FramesToCapture: ULONG, BackTrace: [^]PVOID, BackTraceHash: PULONG) -> USHORT ---
RtlNtStatusToDosError :: proc(status: NTSTATUS) -> ULONG ---
GetSystemPowerStatus :: proc(lpSystemPowerStatus: ^SYSTEM_POWER_STATUS) -> BOOL --- GetSystemPowerStatus :: proc(lpSystemPowerStatus: ^SYSTEM_POWER_STATUS) -> BOOL ---
} }
+2
View File
@@ -222,9 +222,11 @@ ERROR_LOCK_FAILED : DWORD : 167
ERROR_ALREADY_EXISTS : DWORD : 183 ERROR_ALREADY_EXISTS : DWORD : 183
ERROR_NO_DATA : DWORD : 232 ERROR_NO_DATA : DWORD : 232
ERROR_ENVVAR_NOT_FOUND : DWORD : 203 ERROR_ENVVAR_NOT_FOUND : DWORD : 203
ERROR_MR_MID_NOT_FOUND : DWORD : 317
ERROR_OPERATION_ABORTED : DWORD : 995 ERROR_OPERATION_ABORTED : DWORD : 995
ERROR_IO_PENDING : DWORD : 997 ERROR_IO_PENDING : DWORD : 997
ERROR_NO_UNICODE_TRANSLATION : DWORD : 1113 ERROR_NO_UNICODE_TRANSLATION : DWORD : 1113
ERROR_NOT_FOUND : DWORD : 1168
ERROR_TIMEOUT : DWORD : 1460 ERROR_TIMEOUT : DWORD : 1460
ERROR_DATATYPE_MISMATCH : DWORD : 1629 ERROR_DATATYPE_MISMATCH : DWORD : 1629
ERROR_UNSUPPORTED_TYPE : DWORD : 1630 ERROR_UNSUPPORTED_TYPE : DWORD : 1630
+12 -4
View File
@@ -24,10 +24,18 @@ import "core:terminal/ansi"
@(private="file") stop_test_passed: libc.sig_atomic_t @(private="file") stop_test_passed: libc.sig_atomic_t
@(private="file") stop_test_alert: libc.sig_atomic_t @(private="file") stop_test_alert: libc.sig_atomic_t
@(private="file", thread_local) when ODIN_ARCH == .i386 && ODIN_OS == .Windows {
local_test_index: libc.sig_atomic_t // Thread-local storage is problematic on Windows i386
@(private="file", thread_local) @(private="file")
local_test_index_set: bool local_test_index: libc.sig_atomic_t
@(private="file")
local_test_index_set: bool
} else {
@(private="file", thread_local)
local_test_index: libc.sig_atomic_t
@(private="file", thread_local)
local_test_index_set: bool
}
// Windows does not appear to have a SIGTRAP, so this is defined here, instead // Windows does not appear to have a SIGTRAP, so this is defined here, instead
// of in the libc package, just so there's no confusion about it being // of in the libc package, just so there's no confusion about it being
+1 -1
View File
@@ -24,7 +24,7 @@ _sleep :: proc "contextless" (d: Duration) {
_tick_now :: proc "contextless" () -> Tick { _tick_now :: proc "contextless" () -> Tick {
foreign odin_env { foreign odin_env {
tick_now :: proc "contextless" () -> f32 --- tick_now :: proc "contextless" () -> f64 ---
} }
return Tick{i64(tick_now()*1e6)} return Tick{i64(tick_now()*1e6)}
} }
+1
View File
@@ -82,6 +82,7 @@ package all
@(require) import "core:image/png" @(require) import "core:image/png"
@(require) import "core:image/qoi" @(require) import "core:image/qoi"
@(require) import "core:image/tga" @(require) import "core:image/tga"
@(require) import "core:image/jpeg"
@(require) import "core:io" @(require) import "core:io"
@(require) import "core:log" @(require) import "core:log"
+13 -15
View File
@@ -418,6 +418,7 @@ enum LinkerChoice : i32 {
Linker_Default = 0, Linker_Default = 0,
Linker_lld, Linker_lld,
Linker_radlink, Linker_radlink,
Linker_mold,
Linker_COUNT, Linker_COUNT,
}; };
@@ -433,6 +434,7 @@ String linker_choices[Linker_COUNT] = {
str_lit("default"), str_lit("default"),
str_lit("lld"), str_lit("lld"),
str_lit("radlink"), str_lit("radlink"),
str_lit("mold"),
}; };
enum IntegerDivisionByZeroKind : u8 { enum IntegerDivisionByZeroKind : u8 {
@@ -546,6 +548,8 @@ struct BuildContext {
bool ignore_microsoft_magic; bool ignore_microsoft_magic;
bool linker_map_file; bool linker_map_file;
bool build_diagnostics;
bool use_single_module; bool use_single_module;
bool use_separate_modules; bool use_separate_modules;
bool module_per_file; bool module_per_file;
@@ -554,6 +558,8 @@ struct BuildContext {
bool internal_no_inline; bool internal_no_inline;
bool internal_by_value; bool internal_by_value;
bool internal_weak_monomorphization;
bool internal_ignore_llvm_verification;
bool no_threaded_checker; bool no_threaded_checker;
@@ -964,14 +970,6 @@ gb_internal bool is_excluded_target_filename(String name) {
return true; return true;
} }
if (build_context.command_kind != Command_test) {
String test_suffix = str_lit("_test");
if (string_ends_with(name, test_suffix) && name != test_suffix) {
// Ignore *_test.odin files
return true;
}
}
String str1 = {}; String str1 = {};
String str2 = {}; String str2 = {};
isize n = 0; isize n = 0;
@@ -1126,7 +1124,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex); mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex)); defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); text = permanent_alloc_array<wchar_t>(len+1);
GetModuleFileNameW(nullptr, text, cast(int)len); GetModuleFileNameW(nullptr, text, cast(int)len);
path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len)); path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len));
@@ -1163,8 +1161,8 @@ gb_internal String internal_odin_root_dir(void) {
return global_module_path; return global_module_path;
} }
auto path_buf = array_make<char>(heap_allocator(), 300); TEMPORARY_ALLOCATOR_GUARD();
defer (array_free(&path_buf)); auto path_buf = array_make<char>(temporary_allocator(), 300);
len = 0; len = 0;
for (;;) { for (;;) {
@@ -1181,7 +1179,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex); mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex)); defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1); text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len); gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1233,7 +1231,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex); mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex)); defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1); text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len); gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr); path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1394,7 +1392,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex); mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex)); defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1); text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len); gb_memmove(text, &path_buf[0], len);
@@ -1429,7 +1427,7 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr); len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr);
if (len != 0) { if (len != 0) {
wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); wchar_t *text = permanent_alloc_array<wchar_t>(len+1);
GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr); GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr);
mutex_unlock(&fullpath_mutex); mutex_unlock(&fullpath_mutex);
+1 -1
View File
@@ -83,7 +83,7 @@ i32 bundle_android(String original_init_directory) {
return 1; return 1;
} }
int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count); int *dir_numbers = temporary_alloc_array<int>(possible_valid_dirs.count);
char buf[1024] = {}; char buf[1024] = {};
for_array(i, possible_valid_dirs) { for_array(i, possible_valid_dirs) {
+13 -13
View File
@@ -354,7 +354,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
} }
isize const arg_offset = 1; isize const arg_offset = 1;
auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset); auto param_types = permanent_slice_make<Type *>(ce->args.count-arg_offset);
param_types[0] = t_objc_id; param_types[0] = t_objc_id;
param_types[1] = sel_type; param_types[1] = sel_type;
@@ -462,7 +462,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
{ {
// NOTE(harold): The last argument specified in the call is the handler proc, // NOTE(harold): The last argument specified in the call is the handler proc,
// any other arguments before it are capture by-copy arguments. // any other arguments before it are capture by-copy arguments.
auto param_operands = slice_make<Operand>(permanent_allocator(), ce->args.count); auto param_operands = permanent_slice_make<Operand>(ce->args.count);
isize capture_arg_count = ce->args.count - 1; isize capture_arg_count = ce->args.count - 1;
@@ -3555,7 +3555,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
} }
case BuiltinProc_compress_values: { case BuiltinProc_compress_values: {
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count); Operand *ops = temporary_alloc_array<Operand>(ce->args.count);
isize value_count = 0; isize value_count = 0;
@@ -3686,9 +3686,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->mode = Addressing_Value; operand->mode = Addressing_Value;
} else { } else {
Type *st = alloc_type_struct_complete(); Type *st = alloc_type_struct_complete();
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count); st->Struct.fields = permanent_slice_make<Entity *>(value_count);
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count); st->Struct.tags = permanent_alloc_array<String>(value_count);
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count); st->Struct.offsets = permanent_alloc_array<i64>(value_count);
Scope *scope = create_scope(c->info, nullptr); Scope *scope = create_scope(c->info, nullptr);
@@ -4388,7 +4388,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem = alloc_type_struct(); elem = alloc_type_struct();
elem->Struct.scope = s; elem->Struct.scope = s;
elem->Struct.fields = slice_from_array(fields); elem->Struct.fields = slice_from_array(fields);
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count); elem->Struct.tags = permanent_alloc_array<String>(fields.count);
elem->Struct.node = dummy_node_struct; elem->Struct.node = dummy_node_struct;
type_set_offsets(elem); type_set_offsets(elem);
wait_signal_set(&elem->Struct.fields_wait_signal); wait_signal_set(&elem->Struct.fields_wait_signal);
@@ -4420,7 +4420,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(s); gb_string_free(s);
return false; return false;
} }
auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1); auto types = permanent_slice_make<Type *>(t->Struct.fields.count-1);
for_array(i, types) { for_array(i, types) {
Entity *f = t->Struct.fields[i]; Entity *f = t->Struct.fields[i];
GB_ASSERT(f->type->kind == Type_MultiPointer); GB_ASSERT(f->type->kind == Type_MultiPointer);
@@ -4756,8 +4756,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (is_type_array(elem)) { if (is_type_array(elem)) {
Type *old_array = base_type(elem); Type *old_array = base_type(elem);
soa_struct = alloc_type_struct(); soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count); soa_struct->Struct.fields = permanent_slice_make<Entity *>(cast(isize)old_array->Array.count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count); soa_struct->Struct.tags = permanent_alloc_array<String>(cast(isize)old_array->Array.count);
soa_struct->Struct.node = operand->expr; soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_elem = elem;
@@ -4789,8 +4789,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Type *old_struct = base_type(elem); Type *old_struct = base_type(elem);
soa_struct = alloc_type_struct(); soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count); soa_struct->Struct.fields = permanent_slice_make<Entity *>(old_struct->Struct.fields.count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count); soa_struct->Struct.tags = permanent_alloc_array<String>(old_struct->Struct.fields.count);
soa_struct->Struct.node = operand->expr; soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed; soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem; soa_struct->Struct.soa_elem = elem;
@@ -6182,7 +6182,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
} }
Type *new_type = alloc_type_union(); Type *new_type = alloc_type_union();
auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count); auto variants = permanent_slice_make<Type *>(bt->Union.variants.count);
for_array(i, bt->Union.variants) { for_array(i, bt->Union.variants) {
variants[i] = alloc_type_pointer(bt->Union.variants[i]); variants[i] = alloc_type_pointer(bt->Union.variants[i]);
} }
+76 -11
View File
@@ -162,8 +162,6 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
if (found_scope == nullptr) { if (found_scope == nullptr) {
return; return;
} }
rw_mutex_lock(&found_scope->mutex);
defer (rw_mutex_unlock(&found_scope->mutex));
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
// original entity was still used check checked, but the checking was only // original entity was still used check checked, but the checking was only
@@ -172,7 +170,9 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
// Therefore two things can be done: the type can be assigned to state that it // Therefore two things can be done: the type can be assigned to state that it
// has been "evaluated" and the variant data can be copied across // has been "evaluated" and the variant data can be copied across
rw_mutex_lock(&found_scope->mutex);
string_map_set(&found_scope->elements, original_name, new_entity); string_map_set(&found_scope->elements, original_name, new_entity);
rw_mutex_unlock(&found_scope->mutex);
original_entity->flags |= EntityFlag_Overridden; original_entity->flags |= EntityFlag_Overridden;
original_entity->type = new_entity->type; original_entity->type = new_entity->type;
@@ -851,6 +851,50 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
} }
} }
Type *x_base = base_type(x);
Type *y_base = base_type(y);
if (x_base == y_base) {
return true;
}
if (x_base->kind == y_base->kind &&
x_base->kind == Type_Struct) {
i64 xs = type_size_of(x_base);
i64 ys = type_size_of(y_base);
i64 xa = type_align_of(x_base);
i64 ya = type_align_of(y_base);
if (x_base->Struct.is_raw_union == y_base->Struct.is_raw_union &&
xs == ys && xa == ya) {
if (xs > 16) {
// @@ABI NOTE(bill): Just allow anything over 16-bytes to be allowed, because on all current ABIs
// it will be passed by point
// NOTE(bill): this must be changed when ABI changes
return true;
}
if (x_base->Struct.is_raw_union) {
return true;
}
if (x->Struct.fields.count == y->Struct.fields.count) {
for (isize i = 0; i < x->Struct.fields.count; i++) {
Entity *a = x->Struct.fields[i];
Entity *b = y->Struct.fields[i];
bool similar = signature_parameter_similar_enough(a->type, b->type);
if (!similar) {
// NOTE(bill): If the fields are not similar enough, then stop.
goto end;
}
}
}
// HACK NOTE(bill): Allow this for the time begin until it actually becomes a practical problem
return true;
}
}
end:;
return are_types_identical(x, y); return are_types_identical(x, y);
} }
@@ -948,7 +992,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
error(ident, "foreign library names must be an identifier"); error(ident, "foreign library names must be an identifier");
} else { } else {
String name = ident->Ident.token.string; String name = ident->Ident.token.string;
Entity *found = scope_lookup(ctx->scope, name); Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash);
if (found == nullptr) { if (found == nullptr) {
if (is_blank_ident(name)) { if (is_blank_ident(name)) {
@@ -1549,7 +1593,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s", "\tother at %s",
LIT(name), token_pos_to_string(pos)); LIT(name), token_pos_to_string(pos));
} else if (name == "main") { } else if (name == "main") {
if (d->entity->pkg->kind != Package_Runtime) { if (d->entity.load()->pkg->kind != Package_Runtime) {
error(d->proc_lit, "The link name 'main' is reserved for internal use"); error(d->proc_lit, "The link name 'main' is reserved for internal use");
} }
} else { } else {
@@ -1565,7 +1609,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} }
} }
gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) { gb_internal 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);
@@ -1686,7 +1730,28 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration")); check_init_variable(ctx, e, &o, str_lit("variable declaration"));
if (e->Variable.is_rodata && o.mode != Addressing_Constant) { if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
ERROR_BLOCK();
error(o.expr, "Variables declared with @(rodata) must have constant initialization"); error(o.expr, "Variables declared with @(rodata) must have constant initialization");
Ast *expr = unparen_expr(o.expr);
if (is_type_struct(e->type) && expr && expr->kind == Ast_CompoundLit) {
ast_node(cl, CompoundLit, expr);
for (Ast *elem_ : cl->elems) {
Ast *elem = elem_;
if (elem->kind == Ast_FieldValue) {
elem = elem->FieldValue.value;
}
elem = unparen_expr(elem);
Entity *e = entity_of_node(elem);
if (elem->tav.mode != Addressing_Constant && e == nullptr && elem->kind != Ast_ProcLit) {
Token tok = ast_token(elem);
TokenPos pos = tok.pos;
gbString s = type_to_string(type_of_expr(elem));
error_line("%s Element is not constant, which is required for @(rodata), of type %s\n", token_pos_to_string(pos), s);
gb_string_free(s);
}
}
}
} }
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
@@ -1915,7 +1980,7 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
rw_mutex_shared_lock(&decl->deps_mutex); rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&decl->parent->deps_mutex); rw_mutex_lock(&decl->parent->deps_mutex);
for (Entity *e : decl->deps) { FOR_PTR_SET(e, decl->deps) {
ptr_set_add(&decl->parent->deps, e); ptr_set_add(&decl->parent->deps, e);
} }
@@ -1967,8 +2032,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ctx->curr_proc_sig = type; ctx->curr_proc_sig = type;
ctx->curr_proc_calling_convention = type->Proc.calling_convention; ctx->curr_proc_calling_convention = type->Proc.calling_convention;
if (decl->parent && decl->entity && decl->parent->entity) { if (decl->parent && decl->entity.load() && decl->parent->entity) {
decl->entity->parent_proc_decl = decl->parent; decl->entity.load()->parent_proc_decl = decl->parent;
} }
if (ctx->pkg->name != "runtime") { if (ctx->pkg->name != "runtime") {
@@ -1981,9 +2046,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ast_node(bs, BlockStmt, body); ast_node(bs, BlockStmt, body);
TEMPORARY_ALLOCATOR_GUARD();
Array<ProcUsingVar> using_entities = {}; Array<ProcUsingVar> using_entities = {};
using_entities.allocator = heap_allocator(); using_entities.allocator = temporary_allocator();
defer (array_free(&using_entities));
{ {
if (type->Proc.param_count > 0) { if (type->Proc.param_count > 0) {
@@ -2072,7 +2137,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked); GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked);
if (decl->defer_use_checked) { if (decl->defer_use_checked) {
GB_ASSERT(is_type_polymorphic(type, true)); GB_ASSERT(is_type_polymorphic(type, true));
error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string)); error(token, "Defer Use Checked: %.*s", LIT(decl->entity.load()->token.string));
GB_ASSERT(decl->defer_use_checked == false); GB_ASSERT(decl->defer_use_checked == false);
} }
+189 -118
View File
@@ -587,6 +587,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->proc_lit = proc_lit; d->proc_lit = proc_lit;
d->proc_checked_state = ProcCheckedState_Unchecked; d->proc_checked_state = ProcCheckedState_Unchecked;
d->defer_use_checked = false; d->defer_use_checked = false;
d->para_poly_original = old_decl->entity;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
entity->state.store(EntityState_Resolved); entity->state.store(EntityState_Resolved);
@@ -608,7 +609,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
entity->flags |= EntityFlag_Disabled; entity->flags |= EntityFlag_Disabled;
} }
d->entity = entity; d->entity.store(entity);
AstFile *file = nullptr; AstFile *file = nullptr;
{ {
@@ -821,11 +822,13 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
} }
} }
if (c != nullptr) {
if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) { if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
if (c->in_enum_type) { if (c->in_enum_type) {
return 3; return 3;
} }
} }
}
{ {
@@ -1293,6 +1296,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
error_line("\t Got: %s\n", s_got); error_line("\t Got: %s\n", s_got);
gb_string_free(s_got); gb_string_free(s_got);
gb_string_free(s_expected); gb_string_free(s_expected);
Type *tx = x->Proc.params->Tuple.variables[0]->type;
Type *ty = y->Proc.params->Tuple.variables[0]->type;
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(tx), LIT(type_strings[tx->kind]), tx->Named.type_name, tx->Named.type_name->TypeName.original_type_for_parapoly);
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(ty), LIT(type_strings[ty->kind]), ty->Named.type_name, ty->Named.type_name->TypeName.original_type_for_parapoly);
} else { } else {
gbString s_expected = type_to_string(y); gbString s_expected = type_to_string(y);
gbString s_got = type_to_string(x); gbString s_got = type_to_string(x);
@@ -1738,7 +1746,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->expr = n; o->expr = n;
String name = n->Ident.token.string; String name = n->Ident.token.string;
Entity *e = scope_lookup(c->scope, name); Entity *e = scope_lookup(c->scope, name, n->Ident.hash);
if (e == nullptr) { if (e == nullptr) {
if (is_blank_ident(name)) { if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value"); error(n, "'_' cannot be used as a value");
@@ -2333,6 +2341,20 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
if (in_value.kind == ExactValue_Integer) { if (in_value.kind == ExactValue_Integer) {
return true; return true;
} }
} else if (is_type_typeid(type)) {
if (in_value.kind == ExactValue_Compound) {
ast_node(cl, CompoundLit, in_value.value_compound);
if (cl->elems.count == 0) {
in_value = exact_value_typeid(nullptr);
} else {
return false;
}
}
if (in_value.kind == ExactValue_Typeid) {
if (out_value) *out_value = in_value;
return true;
}
} }
return false; return false;
@@ -3503,6 +3525,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) { gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant; bool is_const_expr = x->mode == Addressing_Constant;
Type *bt = base_type(type); Type *bt = base_type(type);
if (is_const_expr && is_type_constant_type(bt)) { if (is_const_expr && is_type_constant_type(bt)) {
if (core_type(bt)->kind == Type_Basic) { if (core_type(bt)->kind == Type_Basic) {
@@ -3524,6 +3547,9 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
} else if (is_type_slice(type) && is_type_string(x->type)) { } else if (is_type_slice(type) && is_type_string(x->type)) {
x->mode = Addressing_Value; x->mode = Addressing_Value;
} else if (is_type_union(type)) { } else if (is_type_union(type)) {
if (is_type_union_constantable(type)) {
return true;
}
x->mode = Addressing_Value; x->mode = Addressing_Value;
} }
if (x->mode == Addressing_Value) { if (x->mode == Addressing_Value) {
@@ -4639,7 +4665,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
} }
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
// GB_ASSERT_NOT_NULL(target_type);
if (target_type == nullptr || operand->mode == Addressing_Invalid || if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type || operand->mode == Addressing_Type ||
is_type_typed(operand->type) || is_type_typed(operand->type) ||
@@ -4810,7 +4835,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
TEMPORARY_ALLOCATOR_GUARD(); TEMPORARY_ALLOCATOR_GUARD();
isize count = t->Union.variants.count; isize count = t->Union.variants.count;
ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count); ValidIndexAndScore *valids = temporary_alloc_array<ValidIndexAndScore>(count);
isize valid_count = 0; isize valid_count = 0;
isize first_success_index = -1; isize first_success_index = -1;
for_array(i, t->Union.variants) { for_array(i, t->Union.variants) {
@@ -4851,7 +4876,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break; break;
} }
operand->type = new_type; operand->type = new_type;
if (operand->mode != Addressing_Constant ||
!elem_type_can_be_constant(operand->type)) {
operand->mode = Addressing_Value; operand->mode = Addressing_Value;
}
break; break;
} else if (valid_count > 1) { } else if (valid_count > 1) {
ERROR_BLOCK(); ERROR_BLOCK();
@@ -5093,7 +5121,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
} }
if (cl->elems[0]->kind == Ast_FieldValue) { if (cl->elems[0]->kind == Ast_FieldValue) {
if (is_type_struct(node->tav.type)) { if (is_type_raw_union(node->tav.type)) {
if (success_) *success_ = false;
if (finish_) *finish_ = true;
return empty_exact_value;
} else if (is_type_struct(node->tav.type)) {
bool found = false; bool found = false;
for (Ast *elem : cl->elems) { for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) { if (elem->kind != Ast_FieldValue) {
@@ -5102,7 +5134,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
ast_node(fv, FieldValue, elem); ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string; String name = fv->field->Ident.token.string;
Selection sub_sel = lookup_field(node->tav.type, name, false); Selection sub_sel = lookup_field(node->tav.type, name, false);
defer (array_free(&sub_sel.index));
if (sub_sel.index.count > 0 && if (sub_sel.index.count > 0 &&
sub_sel.index[0] == index) { sub_sel.index[0] == index) {
value = fv->value->tav.value; value = fv->value->tav.value;
@@ -5363,7 +5394,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
} }
} else */if (node->kind == Ast_Ident) { } else */if (node->kind == Ast_Ident) {
String name = node->Ident.token.string; String name = node->Ident.token.string;
return scope_lookup(c->scope, name); return scope_lookup(c->scope, name, node->Ident.hash);
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) { } else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
ast_node(se, SelectorExpr, node); ast_node(se, SelectorExpr, node);
if (se->token.kind == Token_ArrowRight) { if (se->token.kind == Token_ArrowRight) {
@@ -5385,7 +5416,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
if (op_expr->kind == Ast_Ident) { if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string; String op_name = op_expr->Ident.token.string;
Entity *e = scope_lookup(c->scope, op_name); Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
if (e == nullptr) { if (e == nullptr) {
return nullptr; return nullptr;
} }
@@ -5482,7 +5513,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (op_expr->kind == Ast_Ident) { if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string; String op_name = op_expr->Ident.token.string;
Entity *e = scope_lookup(c->scope, op_name); Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
add_entity_use(c, op_expr, e); add_entity_use(c, op_expr, e);
expr_entity = e; expr_entity = e;
@@ -5905,7 +5936,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return true; return true;
} }
} else { } else {
Entity *e = scope_lookup(s, name); Entity *e = scope_lookup(s, name, i->hash);
if (e != nullptr) { if (e != nullptr) {
if (out_scope) *out_scope = e->scope; if (out_scope) *out_scope = e->scope;
return true; return true;
@@ -6029,7 +6060,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
} }
rw_mutex_shared_lock(&decl->deps_mutex); rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&c->decl->deps_mutex); rw_mutex_lock(&c->decl->deps_mutex);
for (Entity *dep : decl->deps) { FOR_PTR_SET(dep, decl->deps) {
ptr_set_add(&c->decl->deps, dep); ptr_set_add(&c->decl->deps, dep);
} }
rw_mutex_unlock(&c->decl->deps_mutex); rw_mutex_unlock(&c->decl->deps_mutex);
@@ -6257,7 +6288,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
} }
GB_ASSERT(ce->split_args); GB_ASSERT(ce->split_args);
auto visited = slice_make<bool>(temporary_allocator(), pt->param_count); auto visited = temporary_slice_make<bool>(pt->param_count);
auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count); auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
defer ({ defer ({
for (Operand const &o : ordered_operands) { for (Operand const &o : ordered_operands) {
@@ -6473,7 +6504,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
} }
if (e && e->kind == Entity_Constant && is_type_proc(e->type)) { if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
if (o->mode != Addressing_Constant) { bool ok = false;
if (o->mode == Addressing_Constant) {
ok = true;
} else if (o->value.kind == ExactValue_Procedure) {
ok = true;
}
if (!ok) {
if (show_error) { if (show_error) {
error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string)); error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string));
} }
@@ -6972,6 +7010,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_unordered_remove(&procs, proc_index); array_unordered_remove(&procs, proc_index);
continue; continue;
} }
if (!pt->Proc.variadic && max_arg_count != ISIZE_MAX && param_count < max_arg_count) {
array_unordered_remove(&procs, proc_index);
continue;
}
proc_index++; proc_index++;
} }
} }
@@ -6980,10 +7022,10 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
isize lhs_count = -1; isize lhs_count = -1;
i32 variadic_index = -1; i32 variadic_index = -1;
auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0); TEMPORARY_ALLOCATOR_GUARD();
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
defer (array_free(&positional_operands)); auto positional_operands = array_make<Operand>(temporary_allocator(), 0, 0);
defer (array_free(&named_operands)); auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (procs.count == 1) { if (procs.count == 1) {
Entity *e = procs[0]; Entity *e = procs[0];
@@ -7024,7 +7066,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (proc_arg_count >= 0) { if (proc_arg_count >= 0) {
lhs_count = proc_arg_count; lhs_count = proc_arg_count;
if (lhs_count > 0) { if (lhs_count > 0) {
lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); lhs = gb_alloc_array(temporary_allocator(), Entity *, lhs_count);
for (isize param_index = 0; param_index < lhs_count; param_index++) { for (isize param_index = 0; param_index < lhs_count; param_index++) {
Entity *e = nullptr; Entity *e = nullptr;
for (Entity *p : procs) { for (Entity *p : procs) {
@@ -7110,13 +7152,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&named_operands, o); array_add(&named_operands, o);
} }
gb_free(heap_allocator(), lhs); auto valids = array_make<ValidIndexAndScore>(temporary_allocator(), 0, procs.count);
auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count); auto proc_entities = array_make<Entity *>(temporary_allocator(), 0, procs.count*2 + 1);
defer (array_free(&valids));
auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
defer (array_free(&proc_entities));
for (Entity *proc : procs) { for (Entity *proc : procs) {
array_add(&proc_entities, proc); array_add(&proc_entities, proc);
} }
@@ -7247,7 +7285,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
} }
// Try to reduce the list further for `$T: typeid` like parameters // Try to reduce the list further for `$T: typeid` like parameters
bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count); bool *possibly_ignore = temporary_alloc_array<bool>(procs.count);
isize possibly_ignore_set = 0; isize possibly_ignore_set = 0;
if (true) { if (true) {
@@ -7335,7 +7373,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
} }
isize max_spaces = gb_max(max_name_length, max_type_length); isize max_spaces = gb_max(max_name_length, max_type_length);
char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1); char *spaces = temporary_alloc_array<char>(max_spaces+1);
for (isize i = 0; i < max_spaces; i++) { for (isize i = 0; i < max_spaces; i++) {
spaces[i] = ' '; spaces[i] = ' ';
} }
@@ -7510,11 +7548,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
return check_call_arguments_proc_group(c, operand, call); return check_call_arguments_proc_group(c, operand, call);
} }
auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count); TEMPORARY_ALLOCATOR_GUARD();
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
defer (array_free(&positional_operands)); auto positional_operands = array_make<Operand>(temporary_allocator(), 0, positional_args.count);
defer (array_free(&named_operands)); auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (positional_args.count > 0) { if (positional_args.count > 0) {
Entity **lhs = nullptr; Entity **lhs = nullptr;
@@ -7601,6 +7638,8 @@ gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_
gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) { gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
TEMPORARY_ALLOCATOR_GUARD();
ast_node(ce, CallExpr, call); ast_node(ce, CallExpr, call);
Type *original_type = operand->type; Type *original_type = operand->type;
@@ -7609,7 +7648,6 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
bool show_error = true; bool show_error = true;
Array<Operand> operands = {}; Array<Operand> operands = {};
defer (array_free(&operands));
CallArgumentError err = CallArgumentError_None; CallArgumentError err = CallArgumentError_None;
@@ -7617,15 +7655,14 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{ {
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
auto prev_type_path = c->type_path; auto prev_type_path = c->type_path;
c->type_path = new_checker_type_path(); TEMPORARY_ALLOCATOR_GUARD();
defer ({
destroy_checker_type_path(c->type_path); c->type_path = new_checker_type_path(temporary_allocator());
c->type_path = prev_type_path; defer (c->type_path = prev_type_path);
});
if (is_call_expr_field_value(ce)) { if (is_call_expr_field_value(ce)) {
named_fields = true; named_fields = true;
operands = array_make<Operand>(heap_allocator(), ce->args.count); operands = array_make<Operand>(temporary_allocator(), ce->args.count);
for_array(i, ce->args) { for_array(i, ce->args) {
Ast *arg = ce->args[i]; Ast *arg = ce->args[i];
ast_node(fv, FieldValue, arg); ast_node(fv, FieldValue, arg);
@@ -7657,7 +7694,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
} }
} else { } else {
operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); operands = array_make<Operand>(temporary_allocator(), 0, 2*ce->args.count);
Entity **lhs = nullptr; Entity **lhs = nullptr;
isize lhs_count = -1; isize lhs_count = -1;
@@ -7699,7 +7736,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
} else { } else {
TEMPORARY_ALLOCATOR_GUARD(); TEMPORARY_ALLOCATOR_GUARD();
bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); bool *visited = temporary_alloc_array<bool>(param_count);
// LEAK(bill) // LEAK(bill)
ordered_operands = array_make<Operand>(permanent_allocator(), param_count); ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
@@ -7883,11 +7920,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{ {
GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type); GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
mutex_lock(&found_gen_types->mutex); mutex_lock(&found_gen_types->mutex);
defer (mutex_unlock(&found_gen_types->mutex)); defer (mutex_unlock(&found_gen_types->mutex));
Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
if (found_entity) { if (found_entity) {
operand->mode = Addressing_Type; operand->mode = Addressing_Type;
operand->type = found_entity->type; operand->type = found_entity->type;
@@ -8015,62 +8051,8 @@ gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char con
return Expr_Stmt; \ return Expr_Stmt; \
} }
gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *operand, Ast *call, Slice<Ast *> const &args, Type *type_hint) {
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) { GB_ASSERT(operand->mode == Addressing_Type);
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
String name = bd->name.string;
if (
name == "location" ||
name == "exists" ||
name == "assert" ||
name == "panic" ||
name == "defined" ||
name == "config" ||
name == "load" ||
name == "load_directory" ||
name == "load_hash" ||
name == "hash" ||
name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
operand->expr = proc;
operand->type = t_invalid;
add_type_and_value(c, proc, operand->mode, operand->type, operand->value);
} else {
error(proc, "Unknown directive: #%.*s", LIT(name));
operand->expr = proc;
operand->type = t_invalid;
operand->mode = Addressing_Invalid;
return Expr_Expr;
}
if (inlining != ProcInlining_none) {
error(call, "Inlining operators are not allowed on built-in procedures");
}
} else {
if (proc != nullptr) {
check_expr_or_type(c, operand, proc);
} else {
GB_ASSERT(operand->expr != nullptr);
}
}
if (operand->mode == Addressing_Invalid) {
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call");
for (Ast *arg : args) {
if (arg->kind == Ast_FieldValue) {
arg = arg->FieldValue.value;
}
check_expr_base(c, operand, arg, nullptr);
}
operand->mode = Addressing_Invalid;
operand->expr = call;
return Expr_Stmt;
}
if (operand->mode == Addressing_Type) {
Type *t = operand->type; Type *t = operand->type;
if (is_type_polymorphic_record(t)) { if (is_type_polymorphic_record(t)) {
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
@@ -8156,6 +8138,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
} }
operand->type = t; operand->type = t;
operand->expr = call; operand->expr = call;
if (operand->mode != Addressing_Invalid) { if (operand->mode != Addressing_Invalid) {
update_untyped_expr_type(c, arg, t, false); update_untyped_expr_type(c, arg, t, false);
} }
@@ -8164,6 +8148,66 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
} }
} }
return Expr_Expr; return Expr_Expr;
}
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
String name = bd->name.string;
if (
name == "location" ||
name == "exists" ||
name == "assert" ||
name == "panic" ||
name == "defined" ||
name == "config" ||
name == "load" ||
name == "load_directory" ||
name == "load_hash" ||
name == "hash" ||
name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
operand->expr = proc;
operand->type = t_invalid;
add_type_and_value(c, proc, operand->mode, operand->type, operand->value);
} else {
error(proc, "Unknown directive: #%.*s", LIT(name));
operand->expr = proc;
operand->type = t_invalid;
operand->mode = Addressing_Invalid;
return Expr_Expr;
}
if (inlining != ProcInlining_none) {
error(call, "Inlining operators are not allowed on built-in procedures");
}
} else {
if (proc != nullptr) {
check_expr_or_type(c, operand, proc);
} else {
GB_ASSERT(operand->expr != nullptr);
}
}
if (operand->mode == Addressing_Invalid) {
CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call");
for (Ast *arg : args) {
if (arg->kind == Ast_FieldValue) {
arg = arg->FieldValue.value;
}
check_expr_base(c, operand, arg, nullptr);
}
operand->mode = Addressing_Invalid;
operand->expr = call;
return Expr_Stmt;
}
if (operand->mode == Addressing_Type) {
return check_call_expr_as_type_cast(c, operand, call, args, type_hint);
} }
if (operand->mode == Addressing_Builtin) { if (operand->mode == Addressing_Builtin) {
@@ -8335,9 +8379,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (c->curr_proc_decl == nullptr) { if (c->curr_proc_decl == nullptr) {
error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope"); error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
} else { } else {
GB_ASSERT(c->curr_proc_decl->entity); Entity *e = c->curr_proc_decl->entity.load();
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); GB_ASSERT(e);
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature; GB_ASSERT(e->type->kind == Type_Proc);
String scope_features = e->type->Proc.enable_target_feature;
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) { if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
ERROR_BLOCK(); ERROR_BLOCK();
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid)); error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
@@ -8630,7 +8675,7 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Ope
return true; return true;
} }
gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) { gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o, Type *field_type) {
if (is_operand_nil(*o)) { if (is_operand_nil(*o)) {
return true; return true;
} }
@@ -8645,6 +8690,13 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
return true; return true;
} }
} }
if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
add_type_info_type(c, o->type);
return true;
}
if (is_type_any(field_type)) {
return false;
}
return o->mode == Addressing_Constant; return o->mode == Addressing_Constant;
} }
@@ -8874,7 +8926,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
isize count = multi_map_count(seen, key); isize count = multi_map_count(seen, key);
if (count) { if (count) {
TEMPORARY_ALLOCATOR_GUARD(); TEMPORARY_ALLOCATOR_GUARD();
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count); TypeAndToken *taps = temporary_alloc_array<TypeAndToken>(count);
multi_map_get_all(seen, key, taps); multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) { for (isize i = 0; i < count; i++) {
@@ -9595,8 +9647,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
break; break;
} }
} }
if (is_constant && if (is_constant && elem_cannot_be_constant(ft)) {
(is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
is_constant = false; is_constant = false;
} }
} }
@@ -9631,11 +9682,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
Operand o = {}; Operand o = {};
check_expr_or_type(c, &o, fv->value, field->type); check_expr_or_type(c, &o, fv->value, field->type);
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { if (elem_cannot_be_constant(field->type)) {
is_constant = false; is_constant = false;
} }
if (is_constant) { if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &o); is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
} }
u8 prev_bit_field_bit_size = c->bit_field_bit_size; u8 prev_bit_field_bit_size = c->bit_field_bit_size;
@@ -9668,7 +9719,10 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
} }
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) { gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) { if (cl->elems.count == 0) {
return false;
}
if ((check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
ERROR_BLOCK(); ERROR_BLOCK();
error(node, "Compound literals of dynamic types are disabled by default"); error(node, "Compound literals of dynamic types are disabled by default");
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n"); error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
@@ -9679,9 +9733,13 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n"); error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
} }
return false; return false;
} else if (c->curr_proc_decl != nullptr && c->curr_proc_calling_convention != ProcCC_Odin) {
if (c->scope != nullptr && (c->scope->flags & ScopeFlag_ContextDefined) == 0) {
error(node, "Compound literals of dynamic types require a 'context' to defined");
}
} }
return cl->elems.count > 0; return true;
} }
gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) { gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
@@ -9801,7 +9859,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (t->Struct.is_raw_union) { if (t->Struct.is_raw_union) {
if (cl->elems.count > 0) { if (cl->elems.count > 0) {
// NOTE: unions cannot be constant // NOTE: unions cannot be constant
is_constant = false; is_constant = elem_type_can_be_constant(t);
if (cl->elems[0]->kind != Ast_FieldValue) { if (cl->elems[0]->kind != Ast_FieldValue) {
gbString type_str = type_to_string(type); gbString type_str = type_to_string(type);
@@ -9861,11 +9919,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
Operand o = {}; Operand o = {};
check_expr_or_type(c, &o, elem, field->type); check_expr_or_type(c, &o, elem, field->type);
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { if (elem_cannot_be_constant(field->type)) {
is_constant = false; is_constant = false;
} }
if (is_constant) { if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &o); is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
} }
check_assignment(c, &o, field->type, str_lit("structure literal")); check_assignment(c, &o, field->type, str_lit("structure literal"));
@@ -10010,7 +10068,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type); check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
} else { } else {
Operand op_index = {}; Operand op_index = {};
check_expr(c, &op_index, fv->field); check_expr(c, &op_index, fv->field);
@@ -10042,7 +10102,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type); check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
} }
} }
@@ -10069,7 +10131,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, e, elem_type); check_expr_with_type_hint(c, &operand, e, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
} }
if (max < index) { if (max < index) {
@@ -10243,7 +10307,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type); check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
TokenKind upper_op = Token_LtEq; TokenKind upper_op = Token_LtEq;
if (op.kind == Token_RangeHalf) { if (op.kind == Token_RangeHalf) {
@@ -10284,7 +10350,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type); check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
add_to_seen_map(c, &seen, op_index); add_to_seen_map(c, &seen, op_index);
} }
@@ -10314,7 +10382,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, e, elem_type); check_expr_with_type_hint(c, &operand, e, elem_type);
check_assignment(c, &operand, elem_type, context_name); check_assignment(c, &operand, elem_type, context_name);
is_constant = is_constant && operand.mode == Addressing_Constant; if (is_constant) {
is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
}
} }
if (max < index) { if (max < index) {
@@ -10920,7 +10990,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
} }
} }
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1); auto modified_args = permanent_slice_make<Ast *>(ce->args.count+1);
modified_args[0] = first_arg; modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1); slice_copy(&modified_args, ce->args, 1);
ce->args = modified_args; ce->args = modified_args;
@@ -11423,6 +11493,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
o->mode = Addressing_Value; o->mode = Addressing_Value;
o->type = type; o->type = type;
o->value = exact_value_procedure(node);
case_end; case_end;
case_ast_node(te, TernaryIfExpr, node); case_ast_node(te, TernaryIfExpr, node);
+84 -63
View File
@@ -484,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
} }
if (ident_node != nullptr) { if (ident_node != nullptr) {
ast_node(i, Ident, ident_node); ast_node(i, Ident, ident_node);
e = scope_lookup(ctx->scope, i->token.string); e = scope_lookup(ctx->scope, i->token.string, i->hash);
if (e != nullptr && e->kind == Entity_Variable) { if (e != nullptr && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
} }
@@ -1812,8 +1812,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed"); error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed");
} }
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
String name = rs->vals[0]->Ident.token.string; AstIdent *ident = &rs->vals[0]->Ident;
Entity *found = scope_lookup(ctx->scope, name); String name = ident->token.string;
Entity *found = scope_lookup(ctx->scope, name, ident->hash);
if (found && are_types_identical(found->type, t->BitSet.elem)) { if (found && are_types_identical(found->type, t->BitSet.elem)) {
ERROR_BLOCK(); ERROR_BLOCK();
gbString s = expr_to_string(expr); gbString s = expr_to_string(expr);
@@ -1857,8 +1858,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
error(node, "#reverse for is not supported for map types, as maps are unordered"); error(node, "#reverse for is not supported for map types, as maps are unordered");
} }
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
String name = rs->vals[0]->Ident.token.string; AstIdent *ident = &rs->vals[0]->Ident;
Entity *found = scope_lookup(ctx->scope, name); String name = ident->token.string;
Entity *found = scope_lookup(ctx->scope, name, ident->hash);
if (found && are_types_identical(found->type, t->Map.key)) { if (found && are_types_identical(found->type, t->Map.key)) {
ERROR_BLOCK(); ERROR_BLOCK();
gbString s = expr_to_string(expr); gbString s = expr_to_string(expr);
@@ -1963,7 +1965,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
} }
auto rhs = slice_from_array(vals); auto rhs = slice_from_array(vals);
auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count); auto lhs = temporary_slice_make<Ast *>(rhs.count);
slice_copy(&lhs, rs->vals); slice_copy(&lhs, rs->vals);
isize addressable_index = cast(isize)is_map; isize addressable_index = cast(isize)is_map;
@@ -2539,6 +2541,78 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_close_scope(ctx); check_close_scope(ctx);
} }
// NOTE(bill): This is very basic escape analysis
// This needs to be improved tremendously, and a lot of it done during the
// middle-end (or LLVM side) to improve checks and error messages
void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
auto const unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
gbString s = expr_to_string(o.expr);
if (extra_type) {
gbString t = type_to_string(extra_type);
error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
gb_string_free(t);
} else {
error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
}
gb_string_free(s);
};
if (type == nullptr || expr == nullptr) {
return;
}
if (expr->kind == Ast_CompoundLit && is_type_slice(type)) {
ast_node(cl, CompoundLit, expr);
if (cl->elems.count == 0) {
return;
}
unsafe_return_error(o, "a compound literal of a slice");
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
Ast *x = unparen_expr(expr->UnaryExpr.expr);
Entity *e = entity_of_node(x);
if (is_entity_local_variable(e)) {
unsafe_return_error(o, "the address of a local variable");
} else if (x->kind == Ast_CompoundLit) {
unsafe_return_error(o, "the address of a compound literal");
} else if (x->kind == Ast_IndexExpr) {
Entity *f = entity_of_node(x->IndexExpr.expr);
if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
if (is_entity_local_variable(f)) {
unsafe_return_error(o, "the address of an indexed variable", f->type);
}
}
} else if (x->kind == Ast_MatrixIndexExpr) {
Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
unsafe_return_error(o, "the address of an indexed variable", f->type);
}
}
} else if (expr->kind == Ast_SliceExpr) {
Ast *x = unparen_expr(expr->SliceExpr.expr);
Entity *e = entity_of_node(x);
if (is_entity_local_variable(e) && is_type_array(e->type)) {
unsafe_return_error(o, "a slice of a local variable");
} else if (x->kind == Ast_CompoundLit) {
unsafe_return_error(o, "a slice of a compound literal");
}
} else if (o.mode == Addressing_Constant && is_type_slice(type)) {
ERROR_BLOCK();
unsafe_return_error(o, "a compound literal of a slice");
error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
} else if (expr->kind == Ast_CompoundLit) {
ast_node(cl, CompoundLit, expr);
for (Ast *elem : cl->elems) {
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
Entity *e = entity_of_node(fv->field);
if (e != nullptr) {
check_unsafe_return(o, e->type, fv->value);
}
}
}
}
}
gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
ast_node(rs, ReturnStmt, node); ast_node(rs, ReturnStmt, node);
@@ -2567,8 +2641,9 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
result_count = proc_type->Proc.results->Tuple.variables.count; result_count = proc_type->Proc.results->Tuple.variables.count;
} }
auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count); TEMPORARY_ALLOCATOR_GUARD();
defer (array_free(&operands));
auto operands = array_make<Operand>(temporary_allocator(), 0, 2*rs->results.count);
check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk); check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk);
@@ -2614,61 +2689,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
expr = unparen_expr(arg); expr = unparen_expr(arg);
} }
auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) { check_unsafe_return(o, o.type, expr);
gbString s = expr_to_string(o.expr);
if (extra_type) {
gbString t = type_to_string(extra_type);
error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
gb_string_free(t);
} else {
error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
}
gb_string_free(s);
};
// NOTE(bill): This is very basic escape analysis
// This needs to be improved tremendously, and a lot of it done during the
// middle-end (or LLVM side) to improve checks and error messages
if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) {
ast_node(cl, CompoundLit, expr);
if (cl->elems.count == 0) {
continue;
}
unsafe_return_error(o, "a compound literal of a slice");
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
Ast *x = unparen_expr(expr->UnaryExpr.expr);
Entity *e = entity_of_node(x);
if (is_entity_local_variable(e)) {
unsafe_return_error(o, "the address of a local variable");
} else if (x->kind == Ast_CompoundLit) {
unsafe_return_error(o, "the address of a compound literal");
} else if (x->kind == Ast_IndexExpr) {
Entity *f = entity_of_node(x->IndexExpr.expr);
if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
if (is_entity_local_variable(f)) {
unsafe_return_error(o, "the address of an indexed variable", f->type);
}
}
} else if (x->kind == Ast_MatrixIndexExpr) {
Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
unsafe_return_error(o, "the address of an indexed variable", f->type);
}
}
} else if (expr->kind == Ast_SliceExpr) {
Ast *x = unparen_expr(expr->SliceExpr.expr);
Entity *e = entity_of_node(x);
if (is_entity_local_variable(e) && is_type_array(e->type)) {
unsafe_return_error(o, "a slice of a local variable");
} else if (x->kind == Ast_CompoundLit) {
unsafe_return_error(o, "a slice of a compound literal");
}
} else if (o.mode == Addressing_Constant && is_type_slice(o.type)) {
ERROR_BLOCK();
unsafe_return_error(o, "a compound literal of a slice");
error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
}
} }
} }
+27 -25
View File
@@ -267,19 +267,19 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_,
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) { gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
mutex_lock(&ctx->info->gen_types_mutex); // @@global
GenTypesData *found_gen_types = nullptr; GenTypesData *found_gen_types = nullptr;
auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
if (found_gen_types_ptr == nullptr) { GB_ASSERT(original_type->kind == Type_Named);
mutex_lock(&original_type->Named.gen_types_data_mutex);
if (original_type->Named.gen_types_data == nullptr) {
GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData); GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData);
gen_types->types = array_make<Entity *>(heap_allocator()); gen_types->types = array_make<Entity *>(heap_allocator());
map_set(&ctx->info->gen_types, original_type, gen_types); original_type->Named.gen_types_data = gen_types;
found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
} }
found_gen_types = *found_gen_types_ptr; found_gen_types = original_type->Named.gen_types_data;
GB_ASSERT(found_gen_types != nullptr);
mutex_unlock(&ctx->info->gen_types_mutex); // @@global mutex_unlock(&original_type->Named.gen_types_data_mutex);
return found_gen_types; return found_gen_types;
} }
@@ -312,6 +312,7 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
e->state = EntityState_Resolved; e->state = EntityState_Resolved;
e->file = ctx->file; e->file = ctx->file;
e->pkg = pkg; e->pkg = pkg;
e->TypeName.original_type_for_parapoly = original_type;
add_entity_use(ctx, node, e); add_entity_use(ctx, node, e);
} }
@@ -1106,7 +1107,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
GB_ASSERT(fields.count <= bf->fields.count); GB_ASSERT(fields.count <= bf->fields.count);
auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count); auto bit_offsets = permanent_slice_make<i64>(fields.count);
i64 curr_offset = 0; i64 curr_offset = 0;
for_array(i, bit_sizes) { for_array(i, bit_sizes) {
bit_offsets[i] = curr_offset; bit_offsets[i] = curr_offset;
@@ -2707,7 +2708,7 @@ gb_internal Type *get_map_cell_type(Type *type) {
// Padding exists // Padding exists
Type *s = alloc_type_struct(); Type *s = alloc_type_struct();
Scope *scope = create_scope(nullptr, nullptr); Scope *scope = create_scope(nullptr, nullptr);
s->Struct.fields = slice_make<Entity *>(permanent_allocator(), 2); s->Struct.fields = permanent_slice_make<Entity *>(2);
s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved); s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved); s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
s->Struct.scope = scope; s->Struct.scope = scope;
@@ -2732,7 +2733,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Type *metadata_type = alloc_type_struct(); Type *metadata_type = alloc_type_struct();
Scope *metadata_scope = create_scope(nullptr, nullptr); Scope *metadata_scope = create_scope(nullptr, nullptr);
metadata_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 5); metadata_type->Struct.fields = permanent_slice_make<Entity *>(5);
metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved);
metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved);
metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved);
@@ -2750,7 +2751,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Scope *scope = create_scope(nullptr, nullptr); Scope *scope = create_scope(nullptr, nullptr);
Type *debug_type = alloc_type_struct(); Type *debug_type = alloc_type_struct();
debug_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 3); debug_type->Struct.fields = permanent_slice_make<Entity *>(3);
debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved); debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved);
debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved);
debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved);
@@ -2983,13 +2984,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
if (wait_to_finish) { if (wait_to_finish) {
wait_signal_until_available(&old_struct->Struct.fields_wait_signal); wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
} else { } else {
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load()); GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0);
} }
field_count = old_struct->Struct.fields.count; field_count = old_struct->Struct.fields.count;
t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); t->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); t->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
auto const &add_entity = [](Scope *scope, Entity *entity) { auto const &add_entity = [](Scope *scope, Entity *entity) {
@@ -3107,7 +3108,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (is_polymorphic) { if (is_polymorphic) {
field_count = 0; field_count = 0;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.soa_count = 0; soa_struct->Struct.soa_count = 0;
@@ -3117,7 +3118,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Type *old_array = base_type(elem); Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count; field_count = cast(isize)old_array->Array.count;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
string_map_init(&scope->elements, 8); string_map_init(&scope->elements, 8);
@@ -3159,8 +3160,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (old_struct->Struct.fields_wait_signal.futex.load()) { if (old_struct->Struct.fields_wait_signal.futex.load()) {
field_count = old_struct->Struct.fields.count; field_count = old_struct->Struct.fields.count;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count); soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); soa_struct->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
for_array(i, old_struct->Struct.fields) { for_array(i, old_struct->Struct.fields) {
Entity *old_field = old_struct->Struct.fields[i]; Entity *old_field = old_struct->Struct.fields[i];
@@ -3355,7 +3356,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
} }
} }
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
GB_ASSERT_NOT_NULL(type); GB_ASSERT(type != nullptr);
if (e == nullptr) { if (e == nullptr) {
*type = t_invalid; *type = t_invalid;
return true; return true;
@@ -3512,8 +3513,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_ast_node(pt, PointerType, e); case_ast_node(pt, PointerType, e);
CheckerContext c = *ctx; CheckerContext c = *ctx;
c.type_path = new_checker_type_path();
defer (destroy_checker_type_path(c.type_path)); TEMPORARY_ALLOCATOR_GUARD();
c.type_path = new_checker_type_path(temporary_allocator());
Type *elem = t_invalid; Type *elem = t_invalid;
Operand o = {}; Operand o = {};
@@ -3747,8 +3749,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
gb_internal Type *check_type(CheckerContext *ctx, Ast *e) { gb_internal Type *check_type(CheckerContext *ctx, Ast *e) {
CheckerContext c = *ctx; CheckerContext c = *ctx;
c.type_path = new_checker_type_path(); TEMPORARY_ALLOCATOR_GUARD();
defer (destroy_checker_type_path(c.type_path)); c.type_path = new_checker_type_path(temporary_allocator());
return check_type_expr(&c, e, nullptr); return check_type_expr(&c, e, nullptr);
} }
+392 -186
View File
File diff suppressed because it is too large Load Diff
+15 -12
View File
@@ -209,7 +209,7 @@ struct DeclInfo {
Scope * scope; Scope * scope;
Entity *entity; std::atomic<Entity *> entity;
Ast * decl_node; Ast * decl_node;
Ast * type_expr; Ast * type_expr;
@@ -218,6 +218,8 @@ struct DeclInfo {
Ast * proc_lit; // Ast_ProcLit Ast * proc_lit; // Ast_ProcLit
Type * gen_proc_type; // Precalculated Type * gen_proc_type; // Precalculated
Entity * para_poly_original;
bool is_using; bool is_using;
bool where_clauses_evaluated; bool where_clauses_evaluated;
bool foreign_require_results; bool foreign_require_results;
@@ -309,7 +311,8 @@ struct EntityGraphNode;
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet; typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
struct EntityGraphNode { struct EntityGraphNode {
Entity * entity; // Procedure, Variable, Constant Entity *entity; // Procedure, Variable, Constant
EntityGraphNodeSet pred; EntityGraphNodeSet pred;
EntityGraphNodeSet succ; EntityGraphNodeSet succ;
isize index; // Index in array/queue isize index; // Index in array/queue
@@ -448,9 +451,11 @@ struct CheckerInfo {
AstPackage * init_package; AstPackage * init_package;
Scope * init_scope; Scope * init_scope;
Entity * entry_point; Entity * entry_point;
PtrSet<Entity *> minimum_dependency_set;
BlockingMutex minimum_dependency_type_info_mutex; RwMutex minimum_dependency_type_info_mutex;
PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map; PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
RWSpinLock min_dep_type_info_set_mutex;
TypeSet min_dep_type_info_set; TypeSet min_dep_type_info_set;
Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count
@@ -477,8 +482,6 @@ struct CheckerInfo {
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
BlockingMutex gen_types_mutex;
PtrMap<Type *, GenTypesData *> gen_types;
// BlockingMutex type_info_mutex; // NOT recursive // BlockingMutex type_info_mutex; // NOT recursive
// Array<TypeInfoPair> type_info_types; // Array<TypeInfoPair> type_info_types;
@@ -514,7 +517,7 @@ struct CheckerInfo {
BlockingMutex load_file_mutex; BlockingMutex load_file_mutex;
StringMap<LoadFileCache *> load_file_cache; StringMap<LoadFileCache *> load_file_cache;
BlockingMutex all_procedures_mutex; MPSCQueue<ProcInfo *> all_procedures_queue;
Array<ProcInfo *> all_procedures; Array<ProcInfo *> all_procedures;
BlockingMutex instrumentation_mutex; BlockingMutex instrumentation_mutex;
@@ -622,12 +625,12 @@ gb_internal Entity *entity_of_node(Ast *expr);
gb_internal Entity *scope_lookup_current(Scope *s, String const &name); gb_internal Entity *scope_lookup_current(Scope *s, String const &name);
gb_internal Entity *scope_lookup (Scope *s, String const &name); gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0);
gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_); gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0);
gb_internal Entity *scope_insert (Scope *s, Entity *entity); gb_internal Entity *scope_insert (Scope *s, Entity *entity);
gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value); gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex=true);
gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr); gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr);
gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value); gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value);
gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
@@ -648,8 +651,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
gb_internal CheckerTypePath *new_checker_type_path(); gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator);
gb_internal void destroy_checker_type_path(CheckerTypePath *tp); gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator);
gb_internal void check_type_path_push(CheckerContext *c, Entity *e); gb_internal void check_type_path_push(CheckerContext *c, Entity *e);
gb_internal Entity *check_type_path_pop (CheckerContext *c); gb_internal Entity *check_type_path_pop (CheckerContext *c);
+54 -4
View File
@@ -113,6 +113,13 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
return ptr; return ptr;
} }
template <typename T>
gb_internal T *arena_alloc_item(Arena *arena) {
return cast(T *)arena_alloc(arena, gb_size_of(T), gb_align_of(T));
}
gb_internal void arena_free_all(Arena *arena) { gb_internal void arena_free_all(Arena *arena) {
while (arena->curr_block != nullptr) { while (arena->curr_block != nullptr) {
MemoryBlock *free_block = arena->curr_block; MemoryBlock *free_block = arena->curr_block;
@@ -346,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) {
gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) { gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
void *ptr = nullptr; void *ptr = nullptr;
Arena *arena = cast(Arena *)allocator_data; Arena *arena = cast(Arena *)allocator_data;
GB_ASSERT_NOT_NULL(arena); GB_ASSERT(arena != nullptr);
switch (type) { switch (type) {
case gbAllocation_Alloc: case gbAllocation_Alloc:
@@ -394,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) {
} }
template <typename T>
gb_internal T *permanent_alloc_item() {
Arena *arena = get_arena(ThreadArena_Permanent);
return arena_alloc_item<T>(arena);
}
template <typename T>
gb_internal T *permanent_alloc_array(isize count) {
Arena *arena = get_arena(ThreadArena_Permanent);
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
}
template <typename T>
gb_internal Slice<T> permanent_slice_make(isize count) {
Arena *arena = get_arena(ThreadArena_Permanent);
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
return {data, count};
}
template <typename T>
gb_internal T *temporary_alloc_item() {
Arena *arena = get_arena(ThreadArena_Temporary);
return arena_alloc_item<T>(arena);
}
template <typename T>
gb_internal T *temporary_alloc_array(isize count) {
Arena *arena = get_arena(ThreadArena_Temporary);
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
}
template <typename T>
gb_internal Slice<T> temporary_slice_make(isize count) {
Arena *arena = get_arena(ThreadArena_Temporary);
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
return {data, count};
}
gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) { gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
void *ptr = nullptr; void *ptr = nullptr;
@@ -432,15 +481,16 @@ gb_internal gbAllocator permanent_allocator() {
} }
gb_internal gbAllocator temporary_allocator() { gb_internal gbAllocator temporary_allocator() {
return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent}; // return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Temporary};
return permanent_allocator();
} }
#define TEMP_ARENA_GUARD(arena) ArenaTempGuard GB_DEFER_3(_arena_guard_){arena} #define TEMP_ARENA_GUARD(arena) ArenaTempGuard GB_DEFER_3(_arena_guard_){arena}
// #define TEMPORARY_ALLOCATOR_GUARD() // #define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary)) #define TEMPORARY_ALLOCATOR_GUARD()
#define PERMANENT_ALLOCATOR_GUARD() #define PERMANENT_ALLOCATOR_GUARD()
+3 -1
View File
@@ -164,6 +164,7 @@ struct Entity {
u64 id; u64 id;
std::atomic<u64> flags; std::atomic<u64> flags;
std::atomic<EntityState> state; std::atomic<EntityState> state;
std::atomic<i32> min_dep_count;
Token token; Token token;
Scope * scope; Scope * scope;
Type * type; Type * type;
@@ -233,6 +234,7 @@ struct Entity {
} Variable; } Variable;
struct { struct {
Type * type_parameter_specialization; Type * type_parameter_specialization;
Type * original_type_for_parapoly;
String ir_mangled_name; String ir_mangled_name;
bool is_type_alias; bool is_type_alias;
bool objc_is_implementation; bool objc_is_implementation;
@@ -347,7 +349,7 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ
entity->type = type; entity->type = type;
entity->id = 1 + global_entity_id.fetch_add(1); entity->id = 1 + global_entity_id.fetch_add(1);
if (token.pos.file_id) { if (token.pos.file_id) {
entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id); entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id);
} }
return entity; return entity;
} }
+19 -8
View File
@@ -86,7 +86,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos);
gb_internal bool set_file_path_string(i32 index, String const &path) { gb_internal bool set_file_path_string(i32 index, String const &path) {
bool ok = false; bool ok = false;
GB_ASSERT(index >= 0); GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.path_mutex); // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex); mutex_lock(&global_files_mutex);
if (index >= global_file_path_strings.count) { if (index >= global_file_path_strings.count) {
@@ -99,14 +99,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) {
} }
mutex_unlock(&global_files_mutex); mutex_unlock(&global_files_mutex);
mutex_unlock(&global_error_collector.path_mutex); // mutex_unlock(&global_error_collector.path_mutex);
return ok; return ok;
} }
gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false; bool ok = false;
GB_ASSERT(index >= 0); GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.path_mutex); // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex); mutex_lock(&global_files_mutex);
if (index >= global_files.count) { if (index >= global_files.count) {
@@ -118,13 +118,13 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
ok = true; ok = true;
} }
mutex_unlock(&global_files_mutex); mutex_unlock(&global_files_mutex);
mutex_unlock(&global_error_collector.path_mutex); // mutex_unlock(&global_error_collector.path_mutex);
return ok; return ok;
} }
gb_internal String get_file_path_string(i32 index) { gb_internal String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0); GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.path_mutex); // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex); mutex_lock(&global_files_mutex);
String path = {}; String path = {};
@@ -133,13 +133,13 @@ gb_internal String get_file_path_string(i32 index) {
} }
mutex_unlock(&global_files_mutex); mutex_unlock(&global_files_mutex);
mutex_unlock(&global_error_collector.path_mutex); // mutex_unlock(&global_error_collector.path_mutex);
return path; return path;
} }
gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0); GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.path_mutex); // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex); mutex_lock(&global_files_mutex);
AstFile *file = nullptr; AstFile *file = nullptr;
@@ -148,7 +148,18 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
} }
mutex_unlock(&global_files_mutex); mutex_unlock(&global_files_mutex);
mutex_unlock(&global_error_collector.path_mutex); // mutex_unlock(&global_error_collector.path_mutex);
return file;
}
// use AFTER PARSER
gb_internal AstFile *thread_unsafe_get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
return file; return file;
} }
+13 -11
View File
@@ -714,13 +714,15 @@ extern "C++" {
} while (0) } while (0)
#endif #endif
#if defined(DISABLE_ASSERT)
#define GB_ASSERT(cond) gb_unused(cond)
#endif
#ifndef GB_ASSERT #ifndef GB_ASSERT
#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) #define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL)
#endif #endif
#ifndef GB_ASSERT_NOT_NULL
#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL")
#endif
// NOTE(bill): Things that shouldn't happen with a message! // NOTE(bill): Things that shouldn't happen with a message!
#ifndef GB_PANIC #ifndef GB_PANIC
@@ -3719,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) {
} }
gb_inline char *gb_strcpy(char *dest, char const *source) { gb_inline char *gb_strcpy(char *dest, char const *source) {
GB_ASSERT_NOT_NULL(dest); GB_ASSERT(dest != NULL);
if (source) { if (source) {
char *str = dest; char *str = dest;
while (*source) *str++ = *source++; while (*source) *str++ = *source++;
@@ -3729,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) {
gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
GB_ASSERT_NOT_NULL(dest); GB_ASSERT(dest != NULL);
if (source) { if (source) {
char *str = dest; char *str = dest;
while (len > 0 && *source) { while (len > 0 && *source) {
@@ -3746,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) { gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) {
isize result = 0; isize result = 0;
GB_ASSERT_NOT_NULL(dest); GB_ASSERT(dest != NULL);
if (source) { if (source) {
char const *source_start = source; char const *source_start = source;
char *str = dest; char *str = dest;
@@ -5636,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
void gb_file_free_contents(gbFileContents *fc) { void gb_file_free_contents(gbFileContents *fc) {
if (fc == NULL || fc->size == 0) return; if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data); GB_ASSERT(fc->data != NULL);
gb_free(fc->allocator, fc->data); gb_free(fc->allocator, fc->data);
fc->data = NULL; fc->data = NULL;
fc->size = 0; fc->size = 0;
@@ -5648,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) {
gb_inline b32 gb_path_is_absolute(char const *path) { gb_inline b32 gb_path_is_absolute(char const *path) {
b32 result = false; b32 result = false;
GB_ASSERT_NOT_NULL(path); GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
result == (gb_strlen(path) > 2) && result == (gb_strlen(path) > 2) &&
gb_char_is_alpha(path[0]) && gb_char_is_alpha(path[0]) &&
@@ -5663,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut
gb_inline b32 gb_path_is_root(char const *path) { gb_inline b32 gb_path_is_root(char const *path) {
b32 result = false; b32 result = false;
GB_ASSERT_NOT_NULL(path); GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
result = gb_path_is_absolute(path) && (gb_strlen(path) == 3); result = gb_path_is_absolute(path) && (gb_strlen(path) == 3);
#else #else
@@ -5674,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) {
gb_inline char const *gb_path_base_name(char const *path) { gb_inline char const *gb_path_base_name(char const *path) {
char const *ls; char const *ls;
GB_ASSERT_NOT_NULL(path); GB_ASSERT(path != NULL);
ls = gb_char_last_occurence(path, '/'); ls = gb_char_last_occurence(path, '/');
return (ls == NULL) ? path : ls+1; return (ls == NULL) ? path : ls+1;
} }
gb_inline char const *gb_path_extension(char const *path) { gb_inline char const *gb_path_extension(char const *path) {
char const *ld; char const *ld;
GB_ASSERT_NOT_NULL(path); GB_ASSERT(path != NULL);
ld = gb_char_last_occurence(path, '.'); ld = gb_char_last_occurence(path, '.');
return (ld == NULL) ? NULL : ld+1; return (ld == NULL) ? NULL : ld+1;
} }
+22 -3
View File
@@ -161,21 +161,32 @@ gb_internal i32 linker_stage(LinkerData *gen) {
try_cross_linking:; try_cross_linking:;
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
String section_name = str_lit("msvc-link");
bool is_windows = build_context.metrics.os == TargetOs_windows; bool is_windows = build_context.metrics.os == TargetOs_windows;
#else #else
String section_name = str_lit("lld-link");
bool is_windows = false; bool is_windows = false;
#endif #endif
bool is_osx = build_context.metrics.os == TargetOs_darwin; bool is_osx = build_context.metrics.os == TargetOs_darwin;
if (is_windows) {
String section_name = str_lit("msvc-link");
switch (build_context.linker_choice) { switch (build_context.linker_choice) {
case Linker_Default: break; case Linker_Default: break;
case Linker_lld: section_name = str_lit("lld-link"); break; case Linker_lld: section_name = str_lit("lld-link"); break;
#if defined(GB_SYSTEM_LINUX)
case Linker_mold: section_name = str_lit("mold-link"); break;
#endif
#if defined(GB_SYSTEM_WINDOWS)
case Linker_radlink: section_name = str_lit("rad-link"); break; case Linker_radlink: section_name = str_lit("rad-link"); break;
#endif
default:
gb_printf_err("'%.*s' linker is not support for this platform\n", LIT(linker_choices[build_context.linker_choice]));
return 1;
} }
if (is_windows) {
timings_start_section(timings, section_name); timings_start_section(timings, section_name);
gbString lib_str = gb_string_make(heap_allocator(), ""); gbString lib_str = gb_string_make(heap_allocator(), "");
@@ -281,8 +292,12 @@ try_cross_linking:;
link_settings = gb_string_append_fmt(link_settings, " /NOENTRY"); link_settings = gb_string_append_fmt(link_settings, " /NOENTRY");
} }
} else { } else {
// For i386 with CRT, libcmt provides the entry point
// For other cases or no_crt, we need to specify the entry point
if (!(build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt)) {
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup"); link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
} }
}
if (build_context.build_paths[BuildPath_Symbols].name != "") { if (build_context.build_paths[BuildPath_Symbols].name != "") {
String symbol_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Symbols]); String symbol_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Symbols]);
@@ -419,7 +434,8 @@ try_cross_linking:;
} }
} }
} else { } else {
timings_start_section(timings, str_lit("ld-link"));
timings_start_section(timings, section_name);
int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL; int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
@@ -952,6 +968,9 @@ try_cross_linking:;
if (build_context.linker_choice == Linker_lld) { if (build_context.linker_choice == Linker_lld) {
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld"); link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
result = system_exec_command_line_app("lld-link", link_command_line); result = system_exec_command_line_app("lld-link", link_command_line);
} else if (build_context.linker_choice == Linker_mold) {
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=mold");
result = system_exec_command_line_app("mold-link", link_command_line);
} else { } else {
result = system_exec_command_line_app("ld-link", link_command_line); result = system_exec_command_line_app("ld-link", link_command_line);
} }
+17 -17
View File
@@ -522,6 +522,23 @@ namespace lbAbiAmd64Win64 {
} }
}; };
gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
return false;
}
if (LLVMCountStructElementTypes(type) != 2) {
return false;
}
LLVMTypeRef fields[2] = {};
LLVMGetStructElementTypes(type, fields);
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
return false;
}
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
}
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything // NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
namespace lbAbiAmd64SysV { namespace lbAbiAmd64SysV {
enum RegClass { enum RegClass {
@@ -652,23 +669,6 @@ namespace lbAbiAmd64SysV {
return false; return false;
} }
gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
return false;
}
if (LLVMCountStructElementTypes(type) != 2) {
return false;
}
LLVMTypeRef fields[2] = {};
LLVMGetStructElementTypes(type, fields);
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
return false;
}
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
}
gb_internal bool is_aggregate(LLVMTypeRef type) { gb_internal bool is_aggregate(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type); LLVMTypeKind kind = LLVMGetTypeKind(type);
switch (kind) { switch (kind) {
+253 -165
View File
@@ -8,7 +8,11 @@
#endif #endif
#ifndef LLVM_IGNORE_VERIFICATION #ifndef LLVM_IGNORE_VERIFICATION
#define LLVM_IGNORE_VERIFICATION 0 #define LLVM_IGNORE_VERIFICATION build_context.internal_ignore_llvm_verification
#endif
#ifndef LLVM_WEAK_MONOMORPHIZATION
#define LLVM_WEAK_MONOMORPHIZATION (USE_SEPARATE_MODULES && build_context.internal_weak_monomorphization)
#endif #endif
@@ -242,26 +246,12 @@ gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type
return proc_name; return proc_name;
} }
gb_internal void lb_equal_proc_generate_body(lbModule *m, lbProcedure *p) {
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) { Type *type = p->internal_gen_type;
type = base_type(type);
GB_ASSERT(is_type_comparable(type));
Type *pt = alloc_type_pointer(type); Type *pt = alloc_type_pointer(type);
LLVMTypeRef ptr_type = lb_type(m, pt); LLVMTypeRef ptr_type = lb_type(m, pt);
String proc_name = lb_internal_gen_name_from_type("__$equal", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
lbProcedure *compare_proc = nullptr;
if (found) {
compare_proc = *found;
GB_ASSERT(compare_proc != nullptr);
return {compare_proc->value, compare_proc->type};
}
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
LLVMSetLinkage(p->value, LLVMInternalLinkage); LLVMSetLinkage(p->value, LLVMInternalLinkage);
@@ -389,9 +379,29 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
} }
lb_end_procedure_body(p); lb_end_procedure_body(p);
}
compare_proc = p; gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
return {compare_proc->value, compare_proc->type}; type = base_type(type);
GB_ASSERT(is_type_comparable(type));
String proc_name = lb_internal_gen_name_from_type("__$equal", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
lbProcedure *p = *found;
GB_ASSERT(p != nullptr);
return {p->value, p->type};
}
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
string_map_set(&m->gen_procs, proc_name, p);
p->internal_gen_type = type;
p->generate_body = lb_equal_proc_generate_body;
// p->generate_body(m, p);
mpsc_enqueue(&m->procedures_to_generate, p);
return {p->value, p->type};
} }
gb_internal lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) { gb_internal lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
@@ -620,6 +630,7 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
#define LLVM_SET_VALUE_NAME(value, name) LLVMSetValueName2((value), (name), gb_count_of((name))-1); #define LLVM_SET_VALUE_NAME(value, name) LLVMSetValueName2((value), (name), gb_count_of((name))-1);
gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) { gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(!build_context.dynamic_map_calls); GB_ASSERT(!build_context.dynamic_map_calls);
type = base_type(type); type = base_type(type);
@@ -634,6 +645,9 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc); lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
string_map_set(&m->gen_procs, proc_name, p); string_map_set(&m->gen_procs, proc_name, p);
p->internal_gen_type = type;
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p)); defer (lb_end_procedure_body(p));
@@ -1153,15 +1167,6 @@ gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_pt
return lb_emit_runtime_call(p, "__dynamic_map_reserve", args); return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
} }
struct lbGlobalVariable {
lbValue var;
lbValue init;
DeclInfo *decl;
bool is_initialized;
};
gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
if (build_context.metrics.os != TargetOs_darwin) { if (build_context.metrics.os != TargetOs_darwin) {
return nullptr; return nullptr;
@@ -1900,12 +1905,16 @@ gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=fa
} }
gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
if (LLVM_IGNORE_VERIFICATION) {
return 0;
}
char *llvm_error = nullptr; char *llvm_error = nullptr;
defer (LLVMDisposeMessage(llvm_error)); defer (LLVMDisposeMessage(llvm_error));
lbModule *m = cast(lbModule *)data; lbModule *m = cast(lbModule *)data;
if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
gb_printf_err("LLVM Error:\n%s\n", llvm_error); gb_printf_err("LLVM Error in module %s:\n%s\n", m->module_name, llvm_error);
if (build_context.keep_temp_files) { if (build_context.keep_temp_files) {
TIME_SECTION("LLVM Print Module to File"); TIME_SECTION("LLVM Print Module to File");
String filepath_ll = lb_filepath_ll_for_module(m); String filepath_ll = lb_filepath_ll_for_module(m);
@@ -1921,40 +1930,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
return 0; return 0;
} }
gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast *init_expr, lbGlobalVariable &var) {
gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
p->is_startup = true;
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime startup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(p);
lb_setup_type_info_data(main_module);
if (objc_names) {
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
}
for (auto &var : global_variables) {
if (var.is_initialized) {
continue;
}
lbModule *entity_module = main_module;
Entity *e = var.decl->entity;
GB_ASSERT(e->kind == Entity_Variable);
e->code_gen_module = entity_module;
Ast *init_expr = var.decl->init_expr;
if (init_expr != nullptr) { if (init_expr != nullptr) {
lbValue init = lb_build_expr(p, init_expr); lbValue init = lb_build_expr(p, init_expr);
if (init.value == nullptr) { if (init.value == nullptr) {
@@ -1966,12 +1942,12 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
if (e->Variable.is_rodata) { if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(var.var.value, true); LLVMSetGlobalConstant(var.var.value, true);
} }
continue; return true;
} }
GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
} }
if (is_type_any(e->type) || is_type_union(e->type)) { if (is_type_any(e->type)) {
var.init = init; var.init = init;
} else if (lb_is_const_or_global(init)) { } else if (lb_is_const_or_global(init)) {
if (!var.is_initialized) { if (!var.is_initialized) {
@@ -1984,7 +1960,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
if (e->Variable.is_rodata) { if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(var.var.value, true); LLVMSetGlobalConstant(var.var.value, true);
} }
continue; return true;
} }
} else { } else {
var.init = init; var.init = init;
@@ -2001,7 +1977,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::"); gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
gbString e_str = string_canonical_entity_name(temporary_allocator(), e); gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str)); var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, {}, make_string_c(var_name)); lbAddr g = lb_add_global_generated_with_name(m, var_type, {}, make_string_c(var_name));
lb_addr_store(p, g, var.init); lb_addr_store(p, g, var.init);
lbValue gp = lb_addr_get_ptr(p, g); lbValue gp = lb_addr_get_ptr(p, g);
@@ -2019,20 +1995,86 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
var.is_initialized = true; var.is_initialized = true;
} }
return false;
}
gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedure *p) {
lb_begin_procedure_body(p);
lb_setup_type_info_data(m);
if (p->objc_names) {
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(m, p->objc_names->type), p->objc_names->value, nullptr, 0, "");
} }
CheckerInfo *info = main_module->gen->info; Type *dummy_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
LLVMTypeRef raw_dummy_type = lb_type_internal_for_procedures_raw(m, dummy_type);
for (auto &var : *p->global_variables) {
if (var.is_initialized) {
continue;
}
lbModule *entity_module = m;
Entity *e = var.decl->entity;
GB_ASSERT(e->kind == Entity_Variable);
e->code_gen_module = entity_module;
Ast *init_expr = var.decl->init_expr;
if (init_expr == nullptr && var.init.value == nullptr) {
continue;
}
if (type_size_of(e->type) > 8) {
String ename = lb_get_entity_name(m, e);
gbString name = gb_string_make(permanent_allocator(), "");
name = gb_string_appendc(name, "__$startup$");
name = gb_string_append_length(name, ename.text, ename.len);
lbProcedure *dummy = lb_create_dummy_procedure(m, make_string_c(name), dummy_type);
LLVMSetVisibility(dummy->value, LLVMHiddenVisibility);
LLVMSetLinkage(dummy->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(dummy);
lb_init_global_var(m, dummy, e, init_expr, var);
lb_end_procedure_body(dummy);
LLVMValueRef context_ptr = lb_find_or_generate_context_ptr(p).addr.value;
LLVMBuildCall2(p->builder, raw_dummy_type, dummy->value, &context_ptr, 1, "");
} else {
lb_init_global_var(m, p, e, init_expr, var);
}
}
CheckerInfo *info = m->gen->info;
for (Entity *e : info->init_procedures) { for (Entity *e : info->init_procedures) {
lbValue value = lb_find_procedure_value_from_entity(main_module, e); lbValue value = lb_find_procedure_value_from_entity(m, e);
lb_emit_call(p, value, {}, ProcInlining_none); lb_emit_call(p, value, {}, ProcInlining_none);
} }
lb_end_procedure_body(p); lb_end_procedure_body(p);
}
gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
p->is_startup = true;
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime startup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
p->global_variables = &global_variables;
p->objc_names = objc_names;
lb_create_startup_runtime_generate_body(main_module, p);
lb_verify_function(main_module, p);
return p; return p;
} }
@@ -2073,7 +2115,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
for (Entity *e : m->global_procedures_to_create) { for (Entity *e : m->global_procedures_to_create) {
(void)lb_get_entity_name(m, e); (void)lb_get_entity_name(m, e);
array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); mpsc_enqueue(&m->procedures_to_generate, lb_create_procedure(m, e));
} }
return 0; return 0;
} }
@@ -2097,8 +2139,6 @@ gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) {
} }
gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) { gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) {
auto *min_dep_set = &info->minimum_dependency_set;
for (Entity *e : info->entities) { for (Entity *e : info->entities) {
String name = e->token.string; String name = e->token.string;
Scope * scope = e->scope; Scope * scope = e->scope;
@@ -2135,14 +2175,19 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
} }
} }
if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) { if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built // NOTE(bill): Nothing depends upon it so doesn't need to be built
continue; continue;
} }
// if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
// // NOTE(bill): Nothing depends upon it so doesn't need to be built
// continue;
// }
lbModule *m = &gen->default_module; lbModule *m = &gen->default_module;
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
m = lb_module_of_entity(gen, e); m = lb_module_of_entity(gen, e, m);
} }
GB_ASSERT(m != nullptr); GB_ASSERT(m != nullptr);
@@ -2258,7 +2303,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names); lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
} }
for (lbProcedure *p : m->procedures_to_generate) { MUTEX_GUARD_BLOCK(&m->generated_procedures_mutex) for (lbProcedure *p : m->generated_procedures) {
if (p->body != nullptr) { // Build Procedure if (p->body != nullptr) { // Build Procedure
lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default; lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default;
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
@@ -2297,17 +2342,23 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
} }
void lb_remove_unused_functions_and_globals(lbGenerator *gen) {
for (auto &entry : gen->modules) {
lbModule *m = entry.value;
lb_run_remove_unused_function_pass(m);
lb_run_remove_unused_globals_pass(m);
}
}
struct lbLLVMModulePassWorkerData { struct lbLLVMModulePassWorkerData {
lbModule *m; lbModule *m;
LLVMTargetMachineRef target_machine; LLVMTargetMachineRef target_machine;
bool do_threading;
}; };
gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
auto wd = cast(lbLLVMModulePassWorkerData *)data; auto wd = cast(lbLLVMModulePassWorkerData *)data;
lb_run_remove_unused_function_pass(wd->m);
lb_run_remove_unused_globals_pass(wd->m);
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level); lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
LLVMRunPassManager(module_pass_manager, wd->m->mod); LLVMRunPassManager(module_pass_manager, wd->m->mod);
@@ -2383,6 +2434,17 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
return 1; return 1;
} }
#endif #endif
if (LLVM_IGNORE_VERIFICATION) {
return 0;
}
if (wd->do_threading) {
thread_pool_add_task(lb_llvm_module_verification_worker_proc, wd->m);
} else {
lb_llvm_module_verification_worker_proc(wd->m);
}
return 0; return 0;
} }
@@ -2390,8 +2452,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
lbModule *m = cast(lbModule *)data; lbModule *m = cast(lbModule *)data;
for (isize i = 0; i < m->procedures_to_generate.count; i++) { for (lbProcedure *p = nullptr; mpsc_dequeue(&m->procedures_to_generate, &p); /**/) {
lbProcedure *p = m->procedures_to_generate[i];
lb_generate_procedure(p->module, p); lb_generate_procedure(p->module, p);
} }
return 0; return 0;
@@ -2415,11 +2476,16 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
lbModule *m = cast(lbModule *)data; lbModule *m = cast(lbModule *)data;
for (isize i = 0; i < m->missing_procedures_to_check.count; i++) { for (lbProcedure *p = nullptr; mpsc_dequeue(&m->missing_procedures_to_check, &p); /**/) {
lbProcedure *p = m->missing_procedures_to_check[i]; if (!p->is_done.load(std::memory_order_relaxed)) {
debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m); debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
lb_generate_procedure(m, p); lb_generate_procedure(m, p);
} }
for (lbProcedure *nested = nullptr; mpsc_dequeue(&m->procedures_to_generate, &nested); /**/) {
mpsc_enqueue(&m->missing_procedures_to_check, nested);
}
}
return 0; return 0;
} }
@@ -2438,6 +2504,12 @@ gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_thread
lb_generate_missing_procedures_to_check_worker_proc(m); lb_generate_missing_procedures_to_check_worker_proc(m);
} }
} }
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
GB_ASSERT(m->missing_procedures_to_check.count == 0);
GB_ASSERT(m->procedures_to_generate.count == 0);
}
} }
gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
@@ -2465,19 +2537,16 @@ gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) {
} }
gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool do_threading) {
if (do_threading) { if (do_threading) {
for (auto const &entry : gen->modules) { for (auto const &entry : gen->modules) {
lbModule *m = entry.value; lbModule *m = entry.value;
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->m = m; wd->m = m;
wd->target_machine = m->target_machine; wd->target_machine = m->target_machine;
wd->do_threading = true;
if (do_threading) {
thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
} else {
lb_llvm_module_pass_worker_proc(wd);
}
} }
thread_pool_wait(); thread_pool_wait();
} else { } else {
@@ -2486,6 +2555,7 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->m = m; wd->m = m;
wd->target_machine = m->target_machine; wd->target_machine = m->target_machine;
wd->do_threading = false;
lb_llvm_module_pass_worker_proc(wd); lb_llvm_module_pass_worker_proc(wd);
} }
} }
@@ -2566,31 +2636,6 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
} }
gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) {
if (LLVM_IGNORE_VERIFICATION) {
return true;
}
if (do_threading) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
thread_pool_add_task(lb_llvm_module_verification_worker_proc, m);
}
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
if (lb_llvm_module_verification_worker_proc(m)) {
return false;
}
}
}
return true;
}
gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) { gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) {
for (auto const &entry : gen->modules) { for (auto const &entry : gen->modules) {
lbModule *m = entry.value; lbModule *m = entry.value;
@@ -2686,8 +2731,15 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true); params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true); params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false; call_cleanup = false;
} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) { } else if (build_context.metrics.os == TargetOs_windows && build_context.no_crt) {
name = str_lit("mainCRTStartup"); name = str_lit("mainCRTStartup");
} else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt) {
// Windows i386 with CRT: libcmt expects _main (main with underscore prefix)
name = str_lit("main");
has_args = true;
slice_init(&params->Tuple.variables, permanent_allocator(), 2);
params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true);
} else if (is_arch_wasm()) { } else if (is_arch_wasm()) {
name = str_lit("_start"); name = str_lit("_start");
call_cleanup = false; call_cleanup = false;
@@ -2813,16 +2865,19 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
} }
gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
if (p->is_done) { if (p->is_done.load(std::memory_order_relaxed)) {
return; return;
} }
if (p->body != nullptr) { // Build Procedure if (p->body != nullptr) { // Build Procedure
m->curr_procedure = p; m->curr_procedure = p;
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
lb_build_stmt(p, p->body); lb_build_stmt(p, p->body);
lb_end_procedure_body(p); lb_end_procedure_body(p);
p->is_done = true; p->is_done.store(true, std::memory_order_relaxed);
m->curr_procedure = nullptr; m->curr_procedure = nullptr;
} else if (p->generate_body != nullptr) {
p->generate_body(m, p);
} }
// Add Flags // Add Flags
@@ -2831,6 +2886,9 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
} }
lb_verify_function(m, p, true); lb_verify_function(m, p, true);
MUTEX_GUARD(&m->generated_procedures_mutex);
array_add(&m->generated_procedures, p);
} }
@@ -2845,8 +2903,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbModule *default_module = &gen->default_module; lbModule *default_module = &gen->default_module;
CheckerInfo *info = gen->info; CheckerInfo *info = gen->info;
auto *min_dep_set = &info->minimum_dependency_set;
switch (build_context.metrics.arch) { switch (build_context.metrics.arch) {
case TargetArch_amd64: case TargetArch_amd64:
case TargetArch_i386: case TargetArch_i386:
@@ -3162,6 +3218,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
String link_name = e->Procedure.link_name; String link_name = e->Procedure.link_name;
if (e->pkg->kind == Package_Runtime) { if (e->pkg->kind == Package_Runtime) {
if (link_name == "main" || if (link_name == "main" ||
link_name == "_main" ||
link_name == "DllMain" || link_name == "DllMain" ||
link_name == "WinMain" || link_name == "WinMain" ||
link_name == "wWinMain" || link_name == "wWinMain" ||
@@ -3184,7 +3241,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
continue; continue;
} }
if (!ptr_set_exists(min_dep_set, e)) { if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue; continue;
} }
@@ -3194,47 +3251,32 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
} }
GB_ASSERT(e->kind == Entity_Variable); GB_ASSERT(e->kind == Entity_Variable);
bool is_foreign = e->Variable.is_foreign; bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export; bool is_export = e->Variable.is_export;
lbModule *default_module = &gen->default_module;
lbModule *m = default_module;
lbModule *e_module = lb_module_of_entity(gen, e, default_module);
bool const split_globals_across_modules = false;
if (split_globals_across_modules) {
m = e_module;
}
lbModule *m = &gen->default_module;
String name = lb_get_entity_name(m, e); String name = lb_get_entity_name(m, e);
lbValue g = {};
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
g.type = alloc_type_pointer(e->type);
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
if (is_foreign) {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
LLVMSetExternallyInitialized(g.value, true);
lb_add_foreign_library_path(m, e->Variable.foreign_library);
} else {
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
}
if (is_export) {
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
} else if (!is_foreign) {
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
}
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
if (e->Variable.link_section.len > 0) {
LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
}
lbGlobalVariable var = {}; lbGlobalVariable var = {};
var.var = g;
var.decl = decl; var.decl = decl;
lbValue g = {};
g.type = alloc_type_pointer(e->type);
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
if (decl->init_expr != nullptr) { if (decl->init_expr != nullptr) {
TypeAndValue tav = type_and_value_of_expr(decl->init_expr); TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
if (!is_type_any(e->type) && !is_type_union(e->type)) { if (!is_type_any(e->type)) {
if (tav.mode != Addressing_Invalid) { if (tav.mode != Addressing_Invalid) {
if (tav.value.kind != ExactValue_Invalid) { if (tav.value.kind != ExactValue_Invalid) {
auto cc = LB_CONST_CONTEXT_DEFAULT; auto cc = LB_CONST_CONTEXT_DEFAULT;
@@ -3244,6 +3286,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
ExactValue v = tav.value; ExactValue v = tav.value;
lbValue init = lb_const_value(m, tav.type, v, cc); lbValue init = lb_const_value(m, tav.type, v, cc);
LLVMDeleteGlobal(g.value);
g.value = nullptr;
g.value = LLVMAddGlobal(m->mod, LLVMTypeOf(init.value), alloc_cstring(permanent_allocator(), name));
LLVMSetInitializer(g.value, init.value); LLVMSetInitializer(g.value, init.value);
var.is_initialized = true; var.is_initialized = true;
if (cc.is_rodata) { if (cc.is_rodata) {
@@ -3262,16 +3309,33 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetGlobalConstant(g.value, true); LLVMSetGlobalConstant(g.value, true);
} }
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
if (is_foreign) {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
LLVMSetExternallyInitialized(g.value, true);
lb_add_foreign_library_path(m, e->Variable.foreign_library);
} else if (LLVMGetInitializer(g.value) == nullptr) {
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
}
if (is_export) {
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
} else if (!is_foreign) {
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
}
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
if (e->Variable.link_section.len > 0) {
LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
}
if (e->flags & EntityFlag_Require) { if (e->flags & EntityFlag_Require) {
lb_append_to_compiler_used(m, g.value); lb_append_to_compiler_used(m, g.value);
} }
array_add(&global_variables, var);
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
if (m->debug_builder) { if (m->debug_builder) {
String global_name = e->token.string; String global_name = e->token.string;
if (global_name.len != 0 && global_name != "_") { if (global_name.len != 0 && global_name != "_") {
@@ -3300,6 +3364,27 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata); LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata);
} }
} }
if (default_module == m) {
g.value = LLVMConstPointerCast(g.value, lb_type(m, alloc_type_pointer(e->type)));
var.var = g;
array_add(&global_variables, var);
} else {
lbValue local_g = {};
local_g.type = alloc_type_pointer(e->type);
local_g.value = LLVMAddGlobal(default_module->mod, lb_type(default_module, e->type), alloc_cstring(permanent_allocator(), name));
LLVMSetLinkage(local_g.value, LLVMExternalLinkage);
var.var = local_g;
array_add(&global_variables, var);
lb_add_entity(default_module, e, local_g);
lb_add_member(default_module, name, local_g);
}
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
} }
if (build_context.ODIN_DEBUG) { if (build_context.ODIN_DEBUG) {
@@ -3471,15 +3556,23 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
} }
} }
TIME_SECTION("LLVM Add Foreign Library Paths");
lb_add_foreign_library_paths(gen);
TIME_SECTION("LLVM Function Pass"); TIME_SECTION("LLVM Function Pass");
lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG); lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG);
TIME_SECTION("LLVM Module Pass"); TIME_SECTION("LLVM Remove Unused Functions and Globals");
lb_llvm_module_passes(gen, do_threading); lb_remove_unused_functions_and_globals(gen);
TIME_SECTION("LLVM Module Verification"); TIME_SECTION("LLVM Module Pass and Verification");
if (!lb_llvm_module_verification(gen, do_threading)) { lb_llvm_module_passes_and_verification(gen, do_threading);
return false;
TIME_SECTION("LLVM Correct Entity Linkage");
lb_correct_entity_linkage(gen);
if (build_context.build_diagnostics) {
lb_do_build_diagnostics(gen);
} }
llvm_error = nullptr; llvm_error = nullptr;
@@ -3508,11 +3601,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
} }
} }
TIME_SECTION("LLVM Add Foreign Library Paths");
lb_add_foreign_library_paths(gen);
TIME_SECTION("LLVM Correct Entity Linkage");
lb_correct_entity_linkage(gen);
//////////////////////////////////////////// ////////////////////////////////////////////
for (auto const &entry: gen->modules) { for (auto const &entry: gen->modules) {
+28 -7
View File
@@ -147,9 +147,13 @@ struct lbModule {
LLVMModuleRef mod; LLVMModuleRef mod;
LLVMContextRef ctx; LLVMContextRef ctx;
Checker *checker;
struct lbGenerator *gen; struct lbGenerator *gen;
LLVMTargetMachineRef target_machine; LLVMTargetMachineRef target_machine;
lbModule *polymorphic_module;
CheckerInfo *info; CheckerInfo *info;
AstPackage *pkg; // possibly associated AstPackage *pkg; // possibly associated
AstFile *file; // possibly associated AstFile *file; // possibly associated
@@ -171,7 +175,8 @@ struct lbModule {
StringMap<lbValue> members; StringMap<lbValue> members;
StringMap<lbProcedure *> procedures; StringMap<lbProcedure *> procedures;
PtrMap<LLVMValueRef, Entity *> procedure_values; PtrMap<LLVMValueRef, Entity *> procedure_values;
Array<lbProcedure *> missing_procedures_to_check;
MPSCQueue<lbProcedure *> missing_procedures_to_check;
StringMap<LLVMValueRef> const_strings; StringMap<LLVMValueRef> const_strings;
String16Map<LLVMValueRef> const_string16s; String16Map<LLVMValueRef> const_string16s;
@@ -180,10 +185,13 @@ struct lbModule {
StringMap<lbProcedure *> gen_procs; // key is the canonicalized name StringMap<lbProcedure *> gen_procs; // key is the canonicalized name
Array<lbProcedure *> procedures_to_generate; MPSCQueue<lbProcedure *> procedures_to_generate;
Array<Entity *> global_procedures_to_create; Array<Entity *> global_procedures_to_create;
Array<Entity *> global_types_to_create; Array<Entity *> global_types_to_create;
BlockingMutex generated_procedures_mutex;
Array<lbProcedure *> generated_procedures;
lbProcedure *curr_procedure; lbProcedure *curr_procedure;
LLVMBuilderRef const_dummy_builder; LLVMBuilderRef const_dummy_builder;
@@ -232,8 +240,7 @@ struct lbGenerator : LinkerData {
PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; PtrMap<LLVMContextRef, lbModule *> modules_through_ctx;
lbModule default_module; lbModule default_module;
RecursiveMutex anonymous_proc_lits_mutex; lbModule *equal_module;
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
isize used_module_count; isize used_module_count;
@@ -329,6 +336,14 @@ struct lbVariadicReuseSlices {
lbAddr slice_addr; lbAddr slice_addr;
}; };
struct lbGlobalVariable {
lbValue var;
lbValue init;
DeclInfo *decl;
bool is_initialized;
};
struct lbProcedure { struct lbProcedure {
u32 flags; u32 flags;
u16 state_flags; u16 state_flags;
@@ -353,7 +368,7 @@ struct lbProcedure {
LLVMValueRef value; LLVMValueRef value;
LLVMBuilderRef builder; LLVMBuilderRef builder;
bool is_done; std::atomic<bool> is_done;
lbAddr return_ptr; lbAddr return_ptr;
Array<lbDefer> defer_stmts; Array<lbDefer> defer_stmts;
@@ -391,6 +406,12 @@ struct lbProcedure {
PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map; PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
Array<lbValue> asan_stack_locals; Array<lbValue> asan_stack_locals;
void (*generate_body)(lbModule *m, lbProcedure *p);
Array<lbGlobalVariable> *global_variables;
lbProcedure *objc_names;
Type *internal_gen_type; // map_set, map_get, etc.
}; };
@@ -434,7 +455,7 @@ static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, {
gb_internal lbValue lb_const_nil(lbModule *m, Type *type); gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
gb_internal lbValue lb_const_undef(lbModule *m, Type *type); gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT); gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT, Type *value_type=nullptr);
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value); gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value); gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
@@ -583,7 +604,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block); gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_); gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_); gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name); gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t); gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
+238 -61
View File
@@ -81,7 +81,7 @@ gb_internal String lb_get_const_string(lbModule *m, lbValue value) {
} }
gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) { gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst, bool *failure_) {
LLVMTypeRef src = LLVMTypeOf(val); LLVMTypeRef src = LLVMTypeOf(val);
if (src == dst) { if (src == dst) {
return val; return val;
@@ -96,15 +96,12 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
case LLVMPointerTypeKind: case LLVMPointerTypeKind:
return LLVMConstPointerCast(val, dst); return LLVMConstPointerCast(val, dst);
case LLVMStructTypeKind: case LLVMStructTypeKind:
// GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst)); if (LLVMTypeOf(val) != dst) {
// NOTE(bill): It's not possible to do a bit cast on a struct, why was this code even here in the first place? if (failure_) *failure_ = true;
// It seems mostly to exist to get around the "anonymous -> named" struct assignments
// return LLVMConstBitCast(val, dst);
return val;
default:
GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst));
} }
return val;
}
if (failure_) *failure_ = true;
return val; return val;
} }
@@ -129,13 +126,13 @@ gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMVa
LLVMConstNull(lb_type(m, t_i32)), LLVMConstNull(lb_type(m, t_i32)),
len, len,
}; };
return llvm_const_named_struct_internal(lb_type(m, t), values, 3); return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
} else { } else {
LLVMValueRef values[2] = { LLVMValueRef values[2] = {
data, data,
len, len,
}; };
return llvm_const_named_struct_internal(lb_type(m, t), values, 2); return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
} }
} }
@@ -147,13 +144,13 @@ gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVM
LLVMConstNull(lb_type(m, t_i32)), LLVMConstNull(lb_type(m, t_i32)),
len, len,
}; };
return llvm_const_named_struct_internal(lb_type(m, t), values, 3); return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
} else { } else {
LLVMValueRef values[2] = { LLVMValueRef values[2] = {
data, data,
len, len,
}; };
return llvm_const_named_struct_internal(lb_type(m, t), values, 2); return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
} }
} }
@@ -165,10 +162,10 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
unsigned value_count = cast(unsigned)value_count_; unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(struct_type); unsigned elem_count = LLVMCountStructElementTypes(struct_type);
if (elem_count == value_count) { if (elem_count == value_count) {
return llvm_const_named_struct_internal(struct_type, values, value_count_); return llvm_const_named_struct_internal(m, struct_type, values, value_count_);
} }
Type *bt = base_type(t); Type *bt = base_type(t);
GB_ASSERT(bt->kind == Type_Struct); GB_ASSERT(bt->kind == Type_Struct || bt->kind == Type_Union);
GB_ASSERT(value_count_ == bt->Struct.fields.count); GB_ASSERT(value_count_ == bt->Struct.fields.count);
@@ -185,25 +182,40 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
} }
} }
return llvm_const_named_struct_internal(struct_type, values_with_padding, values_with_padding_count); return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
} }
gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) { gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
unsigned value_count = cast(unsigned)value_count_; unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(t); unsigned elem_count = LLVMCountStructElementTypes(t);
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count); GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
bool failure = false;
for (unsigned i = 0; i < elem_count; i++) { for (unsigned i = 0; i < elem_count; i++) {
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i); LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
values[i] = llvm_const_cast(values[i], elem_type); values[i] = llvm_const_cast(values[i], elem_type, &failure);
}
if (failure) {
return LLVMConstStructInContext(m->ctx, values, value_count, true);
} }
return LLVMConstNamedStruct(t, values, value_count); return LLVMConstNamedStruct(t, values, value_count);
} }
gb_internal LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) { gb_internal LLVMValueRef llvm_const_array(lbModule *m, LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
unsigned value_count = cast(unsigned)value_count_; unsigned value_count = cast(unsigned)value_count_;
bool failure = false;
for (unsigned i = 0; i < value_count; i++) { for (unsigned i = 0; i < value_count; i++) {
values[i] = llvm_const_cast(values[i], elem_type); values[i] = llvm_const_cast(values[i], elem_type, &failure);
} }
if (failure) {
return LLVMConstStructInContext(m->ctx, values, value_count, false);
}
for (unsigned i = 0; i < value_count; i++) {
if (elem_type != LLVMTypeOf(values[i])) {
return LLVMConstStructInContext(m->ctx, values, value_count, false);
}
}
return LLVMConstArray(elem_type, values, value_count); return LLVMConstArray(elem_type, values, value_count);
} }
@@ -461,7 +473,7 @@ gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type,
return lb_addr_load(p, v).value; return lb_addr_load(p, v).value;
} }
return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count); return llvm_const_array(m, lb_type(m, elem_type), values, cast(unsigned int)count);
} }
gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) { gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
@@ -531,13 +543,13 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
} }
if (is_type_raw_union(ft) || is_type_typeid(ft)) { if (is_type_raw_union(ft)) {
return false; return false;
} }
return lb_is_elem_const(elem, ft); return lb_is_elem_const(elem, ft);
} }
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) { gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc, Type *value_type) {
if (cc.allow_local) { if (cc.allow_local) {
cc.is_rodata = false; cc.is_rodata = false;
} }
@@ -552,13 +564,102 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
type = core_type(type); type = core_type(type);
value = convert_exact_value_for_type(value, type); value = convert_exact_value_for_type(value, type);
if (value.kind == ExactValue_Typeid) { bool is_local = cc.allow_local && m->curr_procedure != nullptr;
return lb_typeid(m, value.value_typeid);
if (is_type_union(type) && is_type_union_constantable(type)) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Union);
if (bt->Union.variants.count == 0) {
return lb_const_nil(m, original_type);
} else if (bt->Union.variants.count == 1) {
Type *t = bt->Union.variants[0];
lbValue cv = lb_const_value(m, t, value, cc);
GB_ASSERT(LLVMIsConstant(cv.value));
LLVMTypeRef llvm_type = lb_type(m, original_type);
if (is_type_union_maybe_pointer(type)) {
LLVMValueRef values[1] = {cv.value};
res.value = llvm_const_named_struct_internal(m, llvm_type, values, 1);
res.type = original_type;
return res;
} else {
unsigned tag_value = 1;
if (bt->Union.kind == UnionType_no_nil) {
tag_value = 0;
}
LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);
LLVMValueRef padding = nullptr;
LLVMValueRef values[3] = {cv.value, tag, padding};
isize value_count = 2;
if (LLVMCountStructElementTypes(llvm_type) > 2) {
value_count = 3;
padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
}
res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count);
res.type = original_type;
return res;
}
} else {
if (value_type == nullptr) {
if (value.kind == ExactValue_Compound) {
ast_node(cl, CompoundLit, value.value_compound);
if (cl->elems.count == 0) {
return lb_const_nil(m, original_type);
}
} else if (value.kind == ExactValue_Invalid) {
return lb_const_nil(m, original_type);
}
} }
if (value.kind == ExactValue_Invalid) { GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value));
i64 block_size = bt->Union.variant_block_size;
if (are_types_identical(value_type, original_type)) {
if (value.kind == ExactValue_Compound) {
ast_node(cl, CompoundLit, value.value_compound);
if (cl->elems.count == 0) {
return lb_const_nil(m, original_type); return lb_const_nil(m, original_type);
} }
} else if (value.kind == ExactValue_Invalid) {
return lb_const_nil(m, original_type);
}
GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type));
}
lbValue cv = lb_const_value(m, value_type, value, cc, value_type);
Type *variant_type = cv.type;
LLVMValueRef values[4] = {};
unsigned value_count = 0;
values[value_count++] = cv.value;
if (type_size_of(variant_type) != block_size) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
values[value_count++] = LLVMConstNull(padding_type);
}
Type *tag_type = union_tag_type(bt);
LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
i64 tag_index = union_variant_index(bt, variant_type);
GB_ASSERT(tag_index >= 0);
values[value_count++] = LLVMConstInt(llvm_tag_type, tag_index, false);
i64 used_size = block_size + type_size_of(tag_type);
i64 union_size = type_size_of(bt);
i64 padding = union_size - used_size;
if (padding > 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, 1);
values[value_count++] = LLVMConstNull(padding_type);
}
res.value = LLVMConstStructInContext(m->ctx, values, value_count, true);
return res;
}
}
if (value.kind == ExactValue_Procedure) { if (value.kind == ExactValue_Procedure) {
lbValue res = {}; lbValue res = {};
@@ -583,7 +684,23 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
return res; return res;
} }
bool is_local = cc.allow_local && m->curr_procedure != nullptr; // NOTE(bill): This has to be done AFTER the union stuff
if (value.kind == ExactValue_Invalid) {
return lb_const_nil(m, original_type);
}
if (value.kind == ExactValue_Typeid) {
return lb_typeid(m, value.value_typeid);
}
if (value.kind == ExactValue_Compound) {
ast_node(cl, CompoundLit, value.value_compound);
if (cl->elems.count == 0) {
return lb_const_nil(m, original_type);
}
}
// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type)); // GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
@@ -596,7 +713,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
GB_ASSERT(is_type_slice(type)); GB_ASSERT(is_type_slice(type));
res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value; res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value;
return res; return res;
}else { } else {
ast_node(cl, CompoundLit, value.value_compound); ast_node(cl, CompoundLit, value.value_compound);
isize count = cl->elems.count; isize count = cl->elems.count;
@@ -606,20 +723,40 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
count = gb_max(cast(isize)cl->max_count, count); count = gb_max(cast(isize)cl->max_count, count);
Type *elem = base_type(type)->Slice.elem; Type *elem = base_type(type)->Slice.elem;
Type *t = alloc_type_array(elem, count); Type *t = alloc_type_array(elem, count);
lbValue backing_array = lb_const_value(m, t, value, cc); lbValue backing_array = lb_const_value(m, t, value, cc, nullptr);
LLVMValueRef array_data = nullptr; LLVMValueRef array_data = nullptr;
if (is_local) { if (is_local) {
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs // NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
// its backing data on the stack // its backing data on the stack
lbProcedure *p = m->curr_procedure; lbProcedure *p = m->curr_procedure;
LLVMTypeRef llvm_type = lb_type(m, t); LLVMTypeRef llvm_type = lb_type(m, t);
array_data = llvm_alloca(p, llvm_type, 16); unsigned alignment = cast(unsigned)gb_max(type_align_of(t), 16);
bool do_local_copy = false;
if (do_local_copy) {
array_data = llvm_alloca(p, llvm_type, alignment);
LLVMValueRef local_copy = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
LLVMBuildStore(p->builder, backing_array.value, local_copy);
LLVMBuildMemCpy(p->builder,
array_data, alignment,
local_copy, alignment,
LLVMConstInt(lb_type(m, t_int), type_size_of(t), false)
);
} else {
array_data = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
LLVMBuildStore(p->builder, backing_array.value, array_data); LLVMBuildStore(p->builder, backing_array.value, array_data);
array_data = LLVMBuildPointerCast(p->builder, array_data, LLVMPointerType(llvm_type, 0), "");
}
{ {
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, ""); LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, "");
@@ -640,7 +777,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
String name = make_string(cast(u8 const *)str, gb_string_length(str)); String name = make_string(cast(u8 const *)str, gb_string_length(str));
Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value); Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str); array_data = LLVMAddGlobal(m->mod, LLVMTypeOf(backing_array.value), str);
LLVMSetInitializer(array_data, backing_array.value); LLVMSetInitializer(array_data, backing_array.value);
if (cc.link_section.len > 0) { if (cc.link_section.len > 0) {
@@ -651,15 +788,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
lbValue g = {}; lbValue g = {};
g.value = array_data; g.value = LLVMConstPointerCast(array_data, LLVMPointerType(lb_type(m, t), 0));
g.type = t; g.type = t;
lb_add_entity(m, e, g); lb_add_entity(m, e, g);
lb_add_member(m, name, g); lb_add_member(m, name, g);
{ {
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; LLVMValueRef ptr = g.value;
LLVMValueRef ptr = LLVMConstInBoundsGEP2(lb_type(m, t), array_data, indices, 2);
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true); LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
LLVMValueRef values[2] = {ptr, len}; LLVMValueRef values[2] = {ptr, len};
@@ -692,7 +828,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
GB_ASSERT(offset == s.len); GB_ASSERT(offset == s.len);
res.value = llvm_const_array(et, elems, cast(unsigned)count); res.value = llvm_const_array(m, et, elems, cast(unsigned)count);
return res; return res;
} }
// NOTE(bill, 2021-10-07): Allow for array programming value constants // NOTE(bill, 2021-10-07): Allow for array programming value constants
@@ -722,7 +858,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
elems[i] = single_elem.value; elems[i] = single_elem.value;
} }
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count); res.value = llvm_const_array(m, lb_type(m, elem), elems, cast(unsigned)count);
return res; return res;
} else if (is_type_matrix(type) && } else if (is_type_matrix(type) &&
value.kind != ExactValue_Invalid && value.kind != ExactValue_Invalid &&
@@ -734,7 +870,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
Type *elem = type->Matrix.elem; Type *elem = type->Matrix.elem;
lbValue single_elem = lb_const_value(m, elem, value, cc); lbValue single_elem = lb_const_value(m, elem, value, cc);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
i64 total_elem_count = matrix_type_total_internal_elems(type); i64 total_elem_count = matrix_type_total_internal_elems(type);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)total_elem_count); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)total_elem_count);
@@ -756,7 +892,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
Type *elem = type->SimdVector.elem; Type *elem = type->SimdVector.elem;
lbValue single_elem = lb_const_value(m, elem, value, cc); lbValue single_elem = lb_const_value(m, elem, value, cc);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) { for (i64 i = 0; i < count; i++) {
@@ -981,7 +1117,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (lo == i) { if (lo == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
aos_values[value_index++] = val; aos_values[value_index++] = val;
} }
@@ -996,7 +1132,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
if (index == i) { if (index == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
aos_values[value_index++] = val; aos_values[value_index++] = val;
found = true; found = true;
break; break;
@@ -1049,7 +1185,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
for (isize i = 0; i < elem_count; i++) { for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav; TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
aos_values[i] = lb_const_value(m, elem_type, tav.value, cc).value; aos_values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
} }
for (isize i = elem_count; i < type->Struct.soa_count; i++) { for (isize i = elem_count; i < type->Struct.soa_count; i++) {
aos_values[i] = nullptr; aos_values[i] = nullptr;
@@ -1116,7 +1252,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (lo == i) { if (lo == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
values[value_index++] = val; values[value_index++] = val;
} }
@@ -1131,7 +1267,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
if (index == i) { if (index == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val; values[value_index++] = val;
found = true; found = true;
break; break;
@@ -1146,12 +1282,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res; return res;
} else if (value.value_compound->tav.type == elem_type) { } else if (are_types_identical(value.value_compound->tav.type, elem_type)) {
// Compound is of array item type; expand its value to all items in array. // Compound is of array item type; expand its value to all items in array.
LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
for (isize i = 0; i < type->Array.count; i++) { for (isize i = 0; i < type->Array.count; i++) {
values[i] = lb_const_value(m, elem_type, value, cc).value; values[i] = lb_const_value(m, elem_type, value, cc, elem_type).value;
} }
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
@@ -1165,7 +1301,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
for (isize i = 0; i < elem_count; i++) { for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav; TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, cc).value; values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
} }
for (isize i = elem_count; i < type->Array.count; i++) { for (isize i = elem_count; i < type->Array.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type)); values[i] = LLVMConstNull(lb_type(m, elem_type));
@@ -1211,7 +1347,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (lo == i) { if (lo == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
values[value_index++] = val; values[value_index++] = val;
} }
@@ -1226,7 +1362,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
if (index == i) { if (index == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val; values[value_index++] = val;
found = true; found = true;
break; break;
@@ -1249,7 +1385,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
for (isize i = 0; i < elem_count; i++) { for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav; TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, cc).value; values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
} }
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) { for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type)); values[i] = LLVMConstNull(lb_type(m, elem_type));
@@ -1294,7 +1430,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (lo == i) { if (lo == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
values[value_index++] = val; values[value_index++] = val;
} }
@@ -1309,7 +1445,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
if (index == i) { if (index == i) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val; values[value_index++] = val;
found = true; found = true;
break; break;
@@ -1328,7 +1464,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
for (isize i = 0; i < elem_count; i++) { for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav; TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, cc).value; values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
} }
LLVMTypeRef et = lb_type(m, elem_type); LLVMTypeRef et = lb_type(m, elem_type);
@@ -1336,7 +1472,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
values[i] = LLVMConstNull(et); values[i] = LLVMConstNull(et);
} }
for (isize i = 0; i < total_elem_count; i++) { for (isize i = 0; i < total_elem_count; i++) {
values[i] = llvm_const_cast(values[i], et); values[i] = llvm_const_cast(values[i], et, /*failure_*/nullptr);
} }
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count); res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
@@ -1350,6 +1486,39 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (is_type_raw_union(type)) { if (is_type_raw_union(type)) {
if (is_type_raw_union_constantable(type)) {
GB_ASSERT(cl->elems.count == 1);
GB_ASSERT(cl->elems[0]->kind == Ast_FieldValue);
ast_node(fv, FieldValue, cl->elems[0]);
Entity *f = entity_of_node(fv->field);
TypeAndValue tav = fv->value->tav;
if (tav.value.kind != ExactValue_Invalid) {
lbValue value = lb_const_value(m, f->type, tav.value, cc, f->type);
LLVMValueRef values[2];
unsigned value_count = 0;
values[value_count++] = value.value;
i64 union_alignment = type_align_of(type);
i64 value_alignment = type_align_of(f->type);
i64 alignment = gb_max(gb_min(value_alignment, union_alignment), 1);
i64 union_size = type_size_of(type);
i64 value_size = lb_sizeof(LLVMTypeOf(value.value));
i64 padding = union_size-value_size;
if (padding > 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, alignment);
values[value_count++] = LLVMConstNull(padding_type);
}
LLVMValueRef res = LLVMConstStructInContext(m->ctx, values, value_count, /*packed*/padding > 0);
return {res, original_type};
}
}
return lb_const_nil(m, original_type); return lb_const_nil(m, original_type);
} }
@@ -1377,7 +1546,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i32 index = field_remapping[f->Variable.field_index]; i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) { if (elem_type_can_be_constant(f->type)) {
if (sel.index.count == 1) { if (sel.index.count == 1) {
values[index] = lb_const_value(m, f->type, tav.value, cc).value; lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
LLVMTypeRef value_type = LLVMTypeOf(value.value);
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
values[index] = value.value;
visited[index] = true; visited[index] = true;
} else { } else {
if (!visited[index]) { if (!visited[index]) {
@@ -1423,7 +1595,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
} }
if (is_constant) { if (is_constant) {
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc).value; LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc, tav.type).value;
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) { if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len); values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
} else if (is_local) { } else if (is_local) {
@@ -1477,7 +1649,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i32 index = field_remapping[f->Variable.field_index]; i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) { if (elem_type_can_be_constant(f->type)) {
values[index] = lb_const_value(m, f->type, val, cc).value; lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
LLVMTypeRef value_type = LLVMTypeOf(value.value);
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
values[index] = value.value;
visited[index] = true; visited[index] = true;
} }
} }
@@ -1503,7 +1678,9 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
if (is_constant) { if (is_constant) {
res.value = llvm_const_named_struct_internal(struct_type, values, cast(unsigned)value_count); res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
LLVMTypeRef res_type = LLVMTypeOf(res.value);
GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
return res; return res;
} else { } else {
// TODO(bill): THIS IS HACK BUT IT WORKS FOR WHAT I NEED // TODO(bill): THIS IS HACK BUT IT WORKS FOR WHAT I NEED
@@ -1517,7 +1694,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
new_values[i] = LLVMConstNull(LLVMTypeOf(old_value)); new_values[i] = LLVMConstNull(LLVMTypeOf(old_value));
} }
} }
LLVMValueRef constant_value = llvm_const_named_struct_internal(struct_type, new_values, cast(unsigned)value_count); LLVMValueRef constant_value = llvm_const_named_struct_internal(m, struct_type, new_values, cast(unsigned)value_count);
GB_ASSERT(is_local); GB_ASSERT(is_local);
lbProcedure *p = m->curr_procedure; lbProcedure *p = m->curr_procedure;
@@ -1611,7 +1788,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) { for (i64 k = lo; k < hi; k++) {
i64 offset = matrix_row_major_index_to_offset(type, k); i64 offset = matrix_row_major_index_to_offset(type, k);
GB_ASSERT(values[offset] == nullptr); GB_ASSERT(values[offset] == nullptr);
@@ -1623,7 +1800,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
i64 index = exact_value_to_i64(index_tav.value); i64 index = exact_value_to_i64(index_tav.value);
GB_ASSERT(index < max_count); GB_ASSERT(index < max_count);
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value; LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
i64 offset = matrix_row_major_index_to_offset(type, index); i64 offset = matrix_row_major_index_to_offset(type, index);
GB_ASSERT(values[offset] == nullptr); GB_ASSERT(values[offset] == nullptr);
values[offset] = val; values[offset] = val;
@@ -1647,7 +1824,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
i64 offset = 0; i64 offset = 0;
offset = matrix_row_major_index_to_offset(type, i); offset = matrix_row_major_index_to_offset(type, i);
values[offset] = lb_const_value(m, elem_type, tav.value, cc).value; values[offset] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
} }
for (isize i = 0; i < total_count; i++) { for (isize i = 0; i < total_count; i++) {
if (values[i] == nullptr) { if (values[i] == nullptr) {
+1 -1
View File
@@ -1327,7 +1327,7 @@ gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *
} }
lbModule *m = &gen->default_module; lbModule *m = &gen->default_module;
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
m = lb_module_of_entity(gen, e); m = lb_module_of_entity(gen, e, m);
} }
GB_ASSERT(m != nullptr); GB_ASSERT(m != nullptr);
+22 -6
View File
@@ -2502,7 +2502,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
} }
for (Type *vt : dst->Union.variants) { for (Type *vt : dst->Union.variants) {
if (src_type == t_llvm_bool && is_type_boolean(vt)) { if (src_type == t_llvm_bool && is_type_boolean(vt)) {
value = lb_emit_conv(p, value, vt);
lbAddr parent = lb_add_local_generated(p, t, true); lbAddr parent = lb_add_local_generated(p, t, true);
lb_emit_store_union_variant(p, parent.addr, value, vt); lb_emit_store_union_variant(p, parent.addr, value, vt);
return lb_addr_load(p, parent); return lb_addr_load(p, parent);
@@ -2563,10 +2562,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
Type *dt = t; Type *dt = t;
TEMPORARY_ALLOCATOR_GUARD();
GB_ASSERT(is_type_struct(st) || is_type_raw_union(st)); GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
Selection sel = {}; Selection sel = {};
sel.index.allocator = heap_allocator(); sel.index.allocator = temporary_allocator();
defer (array_free(&sel.index));
if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) { if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
if (sel.entity == nullptr) { if (sel.entity == nullptr) {
GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t)); GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
@@ -3929,6 +3929,20 @@ gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
return res; return res;
} }
gb_internal Type *lb_build_expr_original_const_type(Ast *expr) {
expr = unparen_expr(expr);
Type *type = type_of_expr(expr);
if (is_type_union(type)) {
if (expr->kind == Ast_CallExpr) {
if (expr->CallExpr.proc->tav.mode == Addressing_Type) {
Type *res = lb_build_expr_original_const_type(expr->CallExpr.args[0]);
return res;
}
}
}
return type_of_expr(expr);
}
gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
lbModule *m = p->module; lbModule *m = p->module;
@@ -3940,9 +3954,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type)); GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
if (tv.value.kind != ExactValue_Invalid) { if (tv.value.kind != ExactValue_Invalid) {
Type *original_type = lb_build_expr_original_const_type(expr);
// NOTE(bill): Short on constant values // NOTE(bill): Short on constant values
return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL); return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, original_type);
} else if (tv.mode == Addressing_Type) { } else if (tv.mode == Addressing_Type) {
// NOTE(bill, 2023-01-16): is this correct? I hope so at least // NOTE(bill, 2023-01-16): is this correct? I hope so at least
return lb_typeid(m, tv.type); return lb_typeid(m, tv.type);
@@ -4023,7 +4039,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
TypeAndValue tav = type_and_value_of_expr(expr); TypeAndValue tav = type_and_value_of_expr(expr);
GB_ASSERT(tav.mode == Addressing_Constant); GB_ASSERT(tav.mode == Addressing_Constant);
return lb_const_value(p->module, type, tv.value); return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, tv.type);
case_end; case_end;
case_ast_node(se, SelectorCallExpr, expr); case_ast_node(se, SelectorCallExpr, expr);
@@ -4304,7 +4320,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
GB_ASSERT(e != nullptr); GB_ASSERT(e != nullptr);
if (e->kind == Entity_Constant) { if (e->kind == Entity_Constant) {
Type *t = default_type(type_of_expr(expr)); Type *t = default_type(type_of_expr(expr));
lbValue v = lb_const_value(p->module, t, e->Constant.value); lbValue v = lb_const_value(p->module, t, e->Constant.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL, e->type);
if (LLVMIsConstant(v.value)) { if (LLVMIsConstant(v.value)) {
lbAddr g = lb_add_global_generated_from_procedure(p, t, v); lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
return g; return g;
+267 -50
View File
@@ -15,7 +15,9 @@ gb_global isize lb_global_type_info_member_offsets_index = 0;
gb_global isize lb_global_type_info_member_usings_index = 0; gb_global isize lb_global_type_info_member_usings_index = 0;
gb_global isize lb_global_type_info_member_tags_index = 0; gb_global isize lb_global_type_info_member_tags_index = 0;
gb_internal void lb_init_module(lbModule *m, Checker *c) { gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) {
lbModule *m = cast(lbModule *)data;
Checker *c = m->checker;
m->info = &c->info; m->info = &c->info;
@@ -46,6 +48,12 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
} }
module_name = gb_string_appendc(module_name, "builtin"); module_name = gb_string_appendc(module_name, "builtin");
} }
if (m->polymorphic_module == m) {
if (gb_string_length(module_name)) {
module_name = gb_string_appendc(module_name, "-");
}
module_name = gb_string_appendc(module_name, "$parapoly");
}
m->module_name = module_name; m->module_name = module_name;
m->ctx = LLVMContextCreate(); m->ctx = LLVMContextCreate();
@@ -89,15 +97,19 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->function_type_map); map_init(&m->function_type_map);
string_map_init(&m->gen_procs); string_map_init(&m->gen_procs);
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
array_init(&m->procedures_to_generate, a, 0, 1<<10); mpsc_init(&m->procedures_to_generate, a);
map_init(&m->procedure_values, 1<<11); map_init(&m->procedure_values, 1<<11);
array_init(&m->generated_procedures, a, 0, 1<<10);
} else { } else {
array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); mpsc_init(&m->procedures_to_generate, a);
map_init(&m->procedure_values, c->info.all_procedures.count*2); map_init(&m->procedure_values, c->info.all_procedures.count*2);
array_init(&m->generated_procedures, a, 0, c->info.all_procedures.count*2);
} }
array_init(&m->global_procedures_to_create, a, 0, 1024); array_init(&m->global_procedures_to_create, a, 0, 1024);
array_init(&m->global_types_to_create, a, 0, 1024); array_init(&m->global_types_to_create, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16); mpsc_init(&m->missing_procedures_to_check, a);
map_init(&m->debug_values); map_init(&m->debug_values);
string_map_init(&m->objc_classes); string_map_init(&m->objc_classes);
@@ -113,6 +125,15 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx); m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx);
return 0;
}
gb_internal void lb_init_module(lbModule *m, bool do_threading) {
if (do_threading) {
thread_pool_add_task(lb_init_module_worker_proc, m);
} else {
lb_init_module_worker_proc(m);
}
} }
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -125,6 +146,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false; return false;
} }
isize thread_count = gb_max(build_context.thread_count, 1);
isize worker_count = thread_count-1;
bool do_threading = !!(LLVMIsMultithreaded() && USE_SEPARATE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0);
String init_fullpath = c->parser->init_fullpath; String init_fullpath = c->parser->init_fullpath;
linker_data_init(gen, &c->info, init_fullpath); linker_data_init(gen, &c->info, init_fullpath);
@@ -136,7 +161,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->modules, gen->info->packages.count*2); map_init(&gen->modules, gen->info->packages.count*2);
map_init(&gen->modules_through_ctx, gen->info->packages.count*2); map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
map_init(&gen->anonymous_proc_lits, 1024);
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0; bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0;
@@ -145,9 +169,49 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
auto m = gb_alloc_item(permanent_allocator(), lbModule); auto m = gb_alloc_item(permanent_allocator(), lbModule);
m->pkg = pkg; m->pkg = pkg;
m->gen = gen; m->gen = gen;
m->checker = c;
map_set(&gen->modules, cast(void *)pkg, m); map_set(&gen->modules, cast(void *)pkg, m);
lb_init_module(m, c); lb_init_module(m, do_threading);
if (!module_per_file) {
if (LLVM_WEAK_MONOMORPHIZATION) {
auto pm = gb_alloc_item(permanent_allocator(), lbModule);
pm->pkg = pkg;
pm->gen = gen;
pm->checker = c;
m->polymorphic_module = pm;
pm->polymorphic_module = pm;
map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
lb_init_module(pm, do_threading);
}
bool allow_for_per_file = pkg->kind == Package_Runtime || module_per_file;
#if 0
if (!allow_for_per_file) {
if (pkg->files.count >= 20) {
isize proc_count = 0;
for (Entity *e : gen->info->entities) {
if (e->kind != Entity_Procedure) {
continue;
}
if (e->Procedure.is_foreign) {
continue;
}
if (e->pkg == pkg) {
proc_count += 1;
}
}
if (proc_count >= 300) {
allow_for_per_file = true;
}
}
}
#endif
if (!allow_for_per_file) {
continue; continue;
} }
// NOTE(bill): Probably per file is not a good idea, so leave this for later // NOTE(bill): Probably per file is not a good idea, so leave this for later
@@ -156,15 +220,43 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
m->file = file; m->file = file;
m->pkg = pkg; m->pkg = pkg;
m->gen = gen; m->gen = gen;
m->checker = c;
map_set(&gen->modules, cast(void *)file, m); map_set(&gen->modules, cast(void *)file, m);
lb_init_module(m, c); lb_init_module(m, do_threading);
if (LLVM_WEAK_MONOMORPHIZATION) {
auto pm = gb_alloc_item(permanent_allocator(), lbModule);
pm->file = file;
pm->pkg = pkg;
pm->gen = gen;
pm->checker = c;
m->polymorphic_module = pm;
pm->polymorphic_module = pm;
map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
lb_init_module(pm, do_threading);
} }
} }
} }
if (LLVM_WEAK_MONOMORPHIZATION) {
lbModule *m = gb_alloc_item(permanent_allocator(), lbModule);
gen->equal_module = m;
m->gen = gen;
m->checker = c;
map_set(&gen->modules, cast(void *)m, m); // point to itself just add it to the list
lb_init_module(m, do_threading);
}
}
gen->default_module.gen = gen; gen->default_module.gen = gen;
gen->default_module.checker = c;
map_set(&gen->modules, cast(void *)1, &gen->default_module); map_set(&gen->modules, cast(void *)1, &gen->default_module);
lb_init_module(&gen->default_module, c); lb_init_module(&gen->default_module, do_threading);
thread_pool_wait();
for (auto const &entry : gen->modules) { for (auto const &entry : gen->modules) {
lbModule *m = entry.value; lbModule *m = entry.value;
@@ -403,9 +495,9 @@ gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) {
return &gen->default_module; return &gen->default_module;
} }
gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) { gb_internal lbModule *lb_module_of_entity_internal(lbGenerator *gen, Entity *e, lbModule *curr_module) {
GB_ASSERT(e != nullptr);
lbModule **found = nullptr; lbModule **found = nullptr;
if (e->kind == Entity_Procedure && if (e->kind == Entity_Procedure &&
e->decl_info && e->decl_info &&
e->decl_info->code_gen_module) { e->decl_info->code_gen_module) {
@@ -428,6 +520,22 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
return &gen->default_module; return &gen->default_module;
} }
gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e, lbModule *curr_module) {
GB_ASSERT(e != nullptr);
GB_ASSERT(curr_module != nullptr);
lbModule *m = lb_module_of_entity_internal(gen, e, curr_module);
if (USE_SEPARATE_MODULES) {
if (e->kind == Entity_Procedure && e->Procedure.generated_from_polymorphic) {
if (m->polymorphic_module) {
return m->polymorphic_module;
}
}
}
return m;
}
gb_internal lbAddr lb_addr(lbValue addr) { gb_internal lbAddr lb_addr(lbValue addr) {
lbAddr v = {lbAddr_Default, addr}; lbAddr v = {lbAddr_Default, addr};
return v; return v;
@@ -1408,8 +1516,11 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
unsigned element_count = LLVMCountStructElementTypes(uvt); unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt)); GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
LLVMValueRef ptr = u.value;
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
lbValue tag_ptr = {}; lbValue tag_ptr = {};
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, u.value, 1, ""); tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
tag_ptr.type = alloc_type_pointer(tag_type); tag_ptr.type = alloc_type_pointer(tag_type);
return tag_ptr; return tag_ptr;
} }
@@ -1634,8 +1745,92 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
map_set(&m->func_raw_types, type, new_abi_fn_type); map_set(&m->func_raw_types, type, new_abi_fn_type);
return new_abi_fn_type; return new_abi_fn_type;
} }
gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *type) {
GB_ASSERT(type->kind == Type_Union);
if (type->Union.variants.count <= 0) {
return nullptr;
}
if (type->Union.variants.count == 1) {
return lb_type(m, type->Union.variants[0]);
}
i64 align = type_align_of(type);
unsigned block_size = cast(unsigned)type->Union.variant_block_size;
if (block_size == 0) {
return lb_type_padding_filler(m, block_size, align);
}
bool all_pointers = align == build_context.ptr_size;
for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) {
Type *t = type->Union.variants[i];
if (!is_type_internally_pointer_like(t)) {
all_pointers = false;
}
}
if (all_pointers) {
return lb_type(m, t_rawptr);
}
{
Type *pt = type->Union.variants[0];
for (isize i = 1; i < type->Union.variants.count; i++) {
Type *t = type->Union.variants[i];
if (!are_types_identical(pt, t)) {
goto end_check_for_all_the_same;
}
}
return lb_type(m, pt);
} end_check_for_all_the_same:;
{
Type *first_different = nullptr;
for (isize i = 0; i < type->Union.variants.count; i++) {
Type *t = type->Union.variants[i];
if (type_size_of(t) == 0) {
continue;
}
if (first_different == nullptr) {
first_different = t;
} else if (!are_types_identical(first_different, t)) {
goto end_rest_zero_except_one;
}
}
if (first_different != nullptr) {
return lb_type(m, first_different);
}
} end_rest_zero_except_one:;
// {
// LLVMTypeRef first_different = nullptr;
// for (isize i = 0; i < type->Union.variants.count; i++) {
// Type *t = type->Union.variants[i];
// if (type_size_of(t) == 0) {
// continue;
// }
// if (first_different == nullptr) {
// first_different = lb_type(m, base_type(t));
// } else {
// LLVMTypeRef llvm_t = lb_type(m, base_type(t));
// if (llvm_t != first_different) {
// goto end_rest_zero_except_one_llvm_like;
// }
// }
// }
// if (first_different != nullptr) {
// return first_different;
// }
// } end_rest_zero_except_one_llvm_like:;
return lb_type_padding_filler(m, block_size, align);
}
gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMContextRef ctx = m->ctx; LLVMContextRef ctx = m->ctx;
i64 size = type_size_of(type); // Check size i64 size = type_size_of(type); // Check size
@@ -2148,27 +2343,24 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false); return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
} }
unsigned block_size = cast(unsigned)type->Union.variant_block_size;
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3); auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
if (is_type_union_maybe_pointer(type)) { if (is_type_union_maybe_pointer(type)) {
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
array_add(&fields, variant); array_add(&fields, variant);
} else { } else if (type->Union.variants.count == 1) {
LLVMTypeRef block_type = nullptr; LLVMTypeRef block_type = lb_type(m, type->Union.variants[0]);
bool all_pointers = align == build_context.ptr_size; LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) { array_add(&fields, block_type);
Type *t = type->Union.variants[i]; array_add(&fields, tag_type);
if (!is_type_internally_pointer_like(t)) { i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
all_pointers = false; i64 padding = size - used_size;
if (padding > 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
array_add(&fields, padding_type);
} }
}
if (all_pointers) {
block_type = lb_type(m, t_rawptr);
} else { } else {
block_type = lb_type_padding_filler(m, block_size, align); LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type);
}
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
array_add(&fields, block_type); array_add(&fields, block_type);
@@ -2914,7 +3106,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
return lb_find_procedure_value_from_entity(m, e); return lb_find_procedure_value_from_entity(m, e);
} }
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
lbModule *other_module = lb_module_of_entity(m->gen, e); lbModule *other_module = lb_module_of_entity(m->gen, e, m);
if (other_module != m) { if (other_module != m) {
String name = lb_get_entity_name(other_module, e); String name = lb_get_entity_name(other_module, e);
@@ -2962,7 +3154,7 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
lbModule *other_module = m; lbModule *other_module = m;
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
other_module = lb_module_of_entity(gen, e); other_module = lb_module_of_entity(gen, e, m);
} }
if (other_module == m) { if (other_module == m) {
debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m); debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m);
@@ -2979,9 +3171,6 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
} }
if (ignore_body) { if (ignore_body) {
mutex_lock(&gen->anonymous_proc_lits_mutex);
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
GB_ASSERT(other_module != nullptr); GB_ASSERT(other_module != nullptr);
rw_mutex_shared_lock(&other_module->values_mutex); rw_mutex_shared_lock(&other_module->values_mutex);
auto *found = map_get(&other_module->values, e); auto *found = map_get(&other_module->values, e);
@@ -2989,10 +3178,10 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
if (found == nullptr) { if (found == nullptr) {
// THIS IS THE RACE CONDITION // THIS IS THE RACE CONDITION
lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false); lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false);
array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module); mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
} }
} else { } else {
array_add(&m->missing_procedures_to_check, missing_proc); mpsc_enqueue(&m->missing_procedures_to_check, missing_proc);
} }
rw_mutex_shared_lock(&m->values_mutex); rw_mutex_shared_lock(&m->values_mutex);
@@ -3010,18 +3199,16 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
lbGenerator *gen = m->gen; lbGenerator *gen = m->gen;
gb_unused(gen);
mutex_lock(&gen->anonymous_proc_lits_mutex);
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
TokenPos pos = ast_token(expr).pos;
lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr);
if (found) {
return lb_find_procedure_value_from_entity(m, (*found)->entity);
}
ast_node(pl, ProcLit, expr); ast_node(pl, ProcLit, expr);
if (pl->decl->entity.load() != nullptr) {
return lb_find_procedure_value_from_entity(m, pl->decl->entity.load());
}
TokenPos pos = ast_token(expr).pos;
// NOTE(bill): Generate a new name // NOTE(bill): Generate a new name
// parent$count // parent$count
isize name_len = prefix_name.len + 6 + 11; isize name_len = prefix_name.len + 6 + 11;
@@ -3039,30 +3226,51 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
token.string = name; token.string = name;
Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
e->file = expr->file(); e->file = expr->file();
e->scope = e->file->scope;
lbModule *target_module = m;
GB_ASSERT(target_module != nullptr);
// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
pl->decl->code_gen_module = m; pl->decl->code_gen_module = target_module;
e->decl_info = pl->decl; e->decl_info = pl->decl;
pl->decl->entity = e;
e->parent_proc_decl = pl->decl->parent; e->parent_proc_decl = pl->decl->parent;
e->Procedure.is_anonymous = true; e->Procedure.is_anonymous = true;
e->flags |= EntityFlag_ProcBodyChecked; e->flags |= EntityFlag_ProcBodyChecked;
pl->decl->entity.store(e);
if (target_module != m) {
rw_mutex_shared_lock(&target_module->values_mutex);
lbValue *found = map_get(&target_module->values, e);
rw_mutex_shared_unlock(&target_module->values_mutex);
if (found == nullptr) {
lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false);
mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module);
}
lbProcedure *p = lb_create_procedure(m, e, true);
lbValue value = {};
value.value = p->value;
value.type = p->type;
return value;
} else {
lbProcedure *p = lb_create_procedure(m, e); lbProcedure *p = lb_create_procedure(m, e);
GB_ASSERT(e->code_gen_module == m);
lbValue value = {}; lbValue value = {};
value.value = p->value; value.value = p->value;
value.type = p->type; value.type = p->type;
map_set(&gen->anonymous_proc_lits, expr, p); mpsc_enqueue(&m->procedures_to_generate, p);
array_add(&m->procedures_to_generate, p);
if (parent != nullptr) { if (parent != nullptr) {
array_add(&parent->children, p); array_add(&parent->children, p);
} else { } else {
string_map_set(&m->members, name, value); string_map_set(&m->members, name, value);
} }
return value; return value;
}
} }
@@ -3071,11 +3279,18 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
GB_ASSERT(type != nullptr); GB_ASSERT(type != nullptr);
type = default_type(type); type = default_type(type);
LLVMTypeRef actual_type = lb_type(m, type);
if (value.value != nullptr) {
LLVMTypeRef value_type = LLVMTypeOf(value.value);
GB_ASSERT_MSG(lb_sizeof(actual_type) == lb_sizeof(value_type), "%s vs %s", LLVMPrintTypeToString(actual_type), LLVMPrintTypeToString(value_type));
actual_type = value_type;
}
Scope *scope = nullptr; Scope *scope = nullptr;
Entity *e = alloc_entity_variable(scope, make_token_ident(name), type); Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
lbValue g = {}; lbValue g = {};
g.type = alloc_type_pointer(type); g.type = alloc_type_pointer(type);
g.value = LLVMAddGlobal(m->mod, lb_type(m, type), alloc_cstring(temporary_allocator(), name)); g.value = LLVMAddGlobal(m->mod, actual_type, alloc_cstring(temporary_allocator(), name));
if (value.value != nullptr) { if (value.value != nullptr) {
GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value));
LLVMSetInitializer(g.value, value.value); LLVMSetInitializer(g.value, value.value);
@@ -3083,6 +3298,8 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type))); LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type)));
} }
g.value = LLVMConstPointerCast(g.value, lb_type(m, g.type));
lb_add_entity(m, e, g); lb_add_entity(m, e, g);
lb_add_member(m, name, g); lb_add_member(m, name, g);
@@ -3145,7 +3362,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
} }
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
lbModule *other_module = lb_module_of_entity(m->gen, e); lbModule *other_module = lb_module_of_entity(m->gen, e, m);
bool is_external = other_module != m; bool is_external = other_module != m;
if (!is_external) { if (!is_external) {
+8 -9
View File
@@ -84,7 +84,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
String link_name = {}; String link_name = {};
if (ignore_body) { if (ignore_body) {
lbModule *other_module = lb_module_of_entity(m->gen, entity); lbModule *other_module = lb_module_of_entity(m->gen, entity, m);
link_name = lb_get_entity_name(other_module, entity); link_name = lb_get_entity_name(other_module, entity);
} else { } else {
link_name = lb_get_entity_name(m, entity); link_name = lb_get_entity_name(m, entity);
@@ -99,7 +99,6 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
} }
} }
lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
p->module = m; p->module = m;
@@ -837,9 +836,8 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) { gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
GB_ASSERT(pd->body != nullptr); GB_ASSERT(pd->body != nullptr);
lbModule *m = p->module; lbModule *m = p->module;
auto *min_dep_set = &m->info->minimum_dependency_set;
if (ptr_set_exists(min_dep_set, e) == false) { if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built // NOTE(bill): Nothing depends upon it so doesn't need to be built
return; return;
} }
@@ -875,7 +873,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
lb_add_entity(m, e, value); lb_add_entity(m, e, value);
array_add(&p->children, nested_proc); array_add(&p->children, nested_proc);
array_add(&m->procedures_to_generate, nested_proc); mpsc_enqueue(&m->procedures_to_generate, nested_proc);
} }
@@ -962,8 +960,8 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
arg_type != param_type) { arg_type != param_type) {
LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type); LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
LLVMTypeKind param_kind = LLVMGetTypeKind(param_type); LLVMTypeKind param_kind = LLVMGetTypeKind(param_type);
if (arg_kind == param_kind && if (arg_kind == param_kind) {
arg_kind == LLVMPointerTypeKind) { if (arg_kind == LLVMPointerTypeKind) {
// NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times // NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times
// I don't know why... // I don't know why...
args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, ""); args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, "");
@@ -971,6 +969,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
continue; continue;
} }
} }
}
GB_ASSERT_MSG( GB_ASSERT_MSG(
arg_type == param_type, arg_type == param_type,
@@ -2251,7 +2250,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
GB_ASSERT(e != nullptr); GB_ASSERT(e != nullptr);
if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
procedure = e->parent_proc_decl->entity->token.string; procedure = e->parent_proc_decl->entity.load()->token.string;
} else { } else {
procedure = str_lit(""); procedure = str_lit("");
} }
@@ -2278,7 +2277,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
elements[i] = element; elements[i] = element;
} }
LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count); LLVMValueRef backing_array = llvm_const_array(m, lb_type(m, t_load_directory_file), elements, count);
Type *array_type = alloc_type_array(t_load_directory_file, count); Type *array_type = alloc_type_array(t_load_directory_file, count);
lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type}); lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type});
+3 -5
View File
@@ -3,8 +3,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
return; return;
} }
auto *min_dep_set = &p->module->info->minimum_dependency_set;
for (Ast *ident : vd->names) { for (Ast *ident : vd->names) {
GB_ASSERT(ident->kind == Ast_Ident); GB_ASSERT(ident->kind == Ast_Ident);
Entity *e = entity_of_node(ident); Entity *e = entity_of_node(ident);
@@ -21,7 +19,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
} }
} }
if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) { if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue; continue;
} }
@@ -56,7 +54,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
if (gpd) { if (gpd) {
rw_mutex_shared_lock(&gpd->mutex); rw_mutex_shared_lock(&gpd->mutex);
for (Entity *e : gpd->procs) { for (Entity *e : gpd->procs) {
if (!ptr_set_exists(min_dep_set, e)) { if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue; continue;
} }
DeclInfo *d = decl_info_of_entity(e); DeclInfo *d = decl_info_of_entity(e);
@@ -94,7 +92,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
value.value = nested_proc->value; value.value = nested_proc->value;
value.type = nested_proc->type; value.type = nested_proc->type;
array_add(&p->module->procedures_to_generate, nested_proc); mpsc_enqueue(&p->module->procedures_to_generate, nested_proc);
array_add(&p->children, nested_proc); array_add(&p->children, nested_proc);
string_map_set(&p->module->members, name, value); string_map_set(&p->module->members, name, value);
} }
+6 -5
View File
@@ -302,7 +302,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
(name##_values)[i] = LLVMConstNull(elem); \ (name##_values)[i] = LLVMConstNull(elem); \
} \ } \
} \ } \
LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \ LLVMSetInitializer(name.addr.value, llvm_const_array(m, elem, name##_values, at->Array.count)); \
}) })
type_info_allocate_values(lb_global_type_info_member_types); type_info_allocate_values(lb_global_type_info_member_types);
@@ -394,8 +394,9 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
String proc_name = {}; String proc_name = {};
if (t->Named.type_name->parent_proc_decl) { if (t->Named.type_name->parent_proc_decl) {
DeclInfo *decl = t->Named.type_name->parent_proc_decl; DeclInfo *decl = t->Named.type_name->parent_proc_decl;
if (decl->entity && decl->entity->kind == Entity_Procedure) { Entity *e = decl->entity.load();
proc_name = decl->entity->token.string; if (e && e->kind == Entity_Procedure) {
proc_name = e->token.string;
} }
} }
TokenPos pos = t->Named.type_name->token.pos; TokenPos pos = t->Named.type_name->token.pos;
@@ -751,8 +752,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value; value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
} }
LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count); LLVMValueRef name_init = llvm_const_array(m, lb_type(m, t_string), name_values, cast(unsigned)fields.count);
LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count); LLVMValueRef value_init = llvm_const_array(m, lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
LLVMSetInitializer(name_array.value, name_init); LLVMSetInitializer(name_array.value, name_init);
LLVMSetInitializer(value_array.value, value_init); LLVMSetInitializer(value_array.value, value_init);
LLVMSetGlobalConstant(name_array.value, true); LLVMSetGlobalConstant(name_array.value, true);
+289
View File
@@ -2787,3 +2787,292 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
ExactValue value = type_and_value_of_expr(expr).value; ExactValue value = type_and_value_of_expr(expr).value;
return llvm_atomic_ordering_from_odin(value); return llvm_atomic_ordering_from_odin(value);
} }
struct lbDiagParaPolyEntry {
Entity *entity;
String canonical_name;
isize count;
isize total_code_size;
};
gb_internal isize lb_total_code_size(lbProcedure *p) {
isize instruction_count = 0;
LLVMBasicBlockRef first = LLVMGetFirstBasicBlock(p->value);
for (LLVMBasicBlockRef block = first; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
for (LLVMValueRef instr = LLVMGetFirstInstruction(block); instr != nullptr; instr = LLVMGetNextInstruction(instr)) {
instruction_count += 1;
}
}
return instruction_count;
}
gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
PtrMap<Entity * /* Parent */, lbDiagParaPolyEntry> procs = {};
map_init(&procs);
defer (map_destroy(&procs));
for (auto &entry : gen->modules) {
lbModule *m = entry.value;
for (lbProcedure *p : m->generated_procedures) {
Entity *e = p->entity;
if (e == nullptr) {
continue;
}
if (p->builder == nullptr) {
continue;
}
DeclInfo *d = e->decl_info;
Entity *para_poly_parent = d->para_poly_original;
if (para_poly_parent == nullptr) {
continue;
}
lbDiagParaPolyEntry *entry = map_get(&procs, para_poly_parent);
if (entry == nullptr) {
lbDiagParaPolyEntry entry = {};
entry.entity = para_poly_parent;
entry.count = 0;
gbString w = string_canonical_entity_name(permanent_allocator(), entry.entity);
String name = make_string_c(w);
for (isize i = 0; i < name.len; i++) {
String s = substring(name, i, name.len);
if (string_starts_with(s, str_lit(":proc"))) {
name = substring(name, 0, i);
break;
}
}
entry.canonical_name = name;
map_set(&procs, para_poly_parent, entry);
}
entry = map_get(&procs, para_poly_parent);
GB_ASSERT(entry != nullptr);
entry->count += 1;
entry->total_code_size += lb_total_code_size(p);
}
}
auto entries = array_make<lbDiagParaPolyEntry>(heap_allocator(), 0, procs.count);
defer (array_free(&entries));
for (auto &entry : procs) {
array_add(&entries, entry.value);
}
array_sort(entries, [](void const *a, void const *b) -> int {
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->total_code_size > y->total_code_size) {
return -1;
}
if (x->total_code_size < y->total_code_size) {
return +1;
}
return string_compare(x->canonical_name, y->canonical_name);
});
gb_printf("Parametric Polymorphic Procedure Diagnostics\n");
gb_printf("------------------------------------------------------------------------------------------\n");
gb_printf("Sorted by Total Instruction Count Descending (Top 100)\n\n");
gb_printf("Total Instruction Count | Instantiation Count | Average Instruction Count | Procedure Name\n");
isize max_count = 100;
for (auto &entry : entries) {
isize code_size = entry.total_code_size;
isize count = entry.count;
String name = entry.canonical_name;
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
gb_printf("%23td | %19td | %25.2f | %.*s\n", code_size, count, average, LIT(name));
if (max_count-- <= 0) {
break;
}
}
gb_printf("------------------------------------------------------------------------------------------\n");
array_sort(entries, [](void const *a, void const *b) -> int {
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->count > y->count) {
return -1;
}
if (x->count < y->count) {
return +1;
}
return string_compare(x->canonical_name, y->canonical_name);
});
gb_printf("Sorted by Total Instantiation Count Descending (Top 100)\n\n");
gb_printf("Instantiation Count | Total Instruction Count | Average Instruction Count | Procedure Name\n");
max_count = 100;
for (auto &entry : entries) {
isize code_size = entry.total_code_size;
isize count = entry.count;
String name = entry.canonical_name;
f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
gb_printf("%19td | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
if (max_count-- <= 0) {
break;
}
}
gb_printf("------------------------------------------------------------------------------------------\n");
array_sort(entries, [](void const *a, void const *b) -> int {
lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
if (x->count < y->count) {
return -1;
}
if (x->count > y->count) {
return +1;
}
if (x->total_code_size > y->total_code_size) {
return -1;
}
if (x->total_code_size < y->total_code_size) {
return +1;
}
return string_compare(x->canonical_name, y->canonical_name);
});
gb_printf("Single Instanced Parametric Polymorphic Procedures\n\n");
gb_printf("Instruction Count | Procedure Name\n");
for (auto &entry : entries) {
isize code_size = entry.total_code_size;
isize count = entry.count;
String name = entry.canonical_name;
if (count != 1) {
break;
}
gb_printf("%17td | %.*s\n", code_size, LIT(name));
}
}
struct lbDiagModuleEntry {
lbModule *m;
String name;
isize global_internal_count;
isize global_external_count;
isize proc_internal_count;
isize proc_external_count;
isize total_instruction_count;
};
gb_internal void lb_do_module_diagnostics(lbGenerator *gen) {
Array<lbDiagModuleEntry> modules = {};
array_init(&modules, heap_allocator());
defer (array_free(&modules));
for (auto &em : gen->modules) {
lbModule *m = em.value;
{
lbDiagModuleEntry entry = {};
entry.m = m;
entry.name = make_string_c(m->module_name);
array_add(&modules, entry);
}
lbDiagModuleEntry &entry = modules[modules.count-1];
for (LLVMValueRef p = LLVMGetFirstFunction(m->mod); p != nullptr; p = LLVMGetNextFunction(p)) {
LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(p);
if (block == nullptr) {
entry.proc_external_count += 1;
} else {
entry.proc_internal_count += 1;
for (; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
for (LLVMValueRef i = LLVMGetFirstInstruction(block); i != nullptr; i = LLVMGetNextInstruction(i)) {
entry.total_instruction_count += 1;
}
}
}
}
for (LLVMValueRef g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
LLVMLinkage linkage = LLVMGetLinkage(g);
if (linkage == LLVMExternalLinkage) {
entry.global_external_count += 1;
} else {
entry.global_internal_count += 1;
}
}
}
array_sort(modules, [](void const *a, void const *b) -> int {
lbDiagModuleEntry *x = cast(lbDiagModuleEntry *)a;
lbDiagModuleEntry *y = cast(lbDiagModuleEntry *)b;
if (x->total_instruction_count > y->total_instruction_count) {
return -1;
}
if (x->total_instruction_count < y->total_instruction_count) {
return +1;
}
return string_compare(x->name, y->name);
});
gb_printf("Module Diagnostics\n\n");
gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Files | Instructions/File | Instructions/Proc | Module Name\n");
gb_printf("-------------------+------------------+------------------+----------------+----------------+-------+-------------------+-------------------+------------\n");
for (auto &entry : modules) {
isize file_count = 1;
if (entry.m->file != nullptr) {
file_count = 1;
} else if (entry.m->pkg) {
file_count = entry.m->pkg->files.count;
}
f64 instructions_per_file = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)file_count);
f64 instructions_per_proc = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)entry.proc_internal_count);
gb_printf("%18td | %16td | %16td | %14td | %14td | %5td | %17.1f | %17.1f | %s \n",
entry.total_instruction_count,
entry.global_internal_count,
entry.global_external_count,
entry.proc_internal_count,
entry.proc_external_count,
file_count,
instructions_per_file,
instructions_per_proc,
entry.m->module_name);
}
}
gb_internal void lb_do_build_diagnostics(lbGenerator *gen) {
lb_do_para_poly_diagnostics(gen);
gb_printf("------------------------------------------------------------------------------------------\n");
gb_printf("------------------------------------------------------------------------------------------\n\n");
lb_do_module_diagnostics(gen);
gb_printf("------------------------------------------------------------------------------------------\n");
gb_printf("------------------------------------------------------------------------------------------\n\n");
}
+26 -2
View File
@@ -394,6 +394,8 @@ enum BuildFlagKind {
BuildFlag_IntegerDivisionByZero, BuildFlag_IntegerDivisionByZero,
BuildFlag_BuildDiagnostics,
// internal use only // internal use only
BuildFlag_InternalFastISel, BuildFlag_InternalFastISel,
BuildFlag_InternalIgnoreLazy, BuildFlag_InternalIgnoreLazy,
@@ -403,6 +405,8 @@ enum BuildFlagKind {
BuildFlag_InternalCached, BuildFlag_InternalCached,
BuildFlag_InternalNoInline, BuildFlag_InternalNoInline,
BuildFlag_InternalByValue, BuildFlag_InternalByValue,
BuildFlag_InternalWeakMonomorphization,
BuildFlag_InternalLLVMVerification,
BuildFlag_Tilde, BuildFlag_Tilde,
@@ -617,6 +621,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
@@ -626,6 +631,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all);
#if ALLOW_TILDE #if ALLOW_TILDE
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
@@ -1558,6 +1565,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
} }
break; break;
case BuildFlag_BuildDiagnostics:
build_context.build_diagnostics = true;
break;
case BuildFlag_InternalFastISel: case BuildFlag_InternalFastISel:
build_context.fast_isel = true; build_context.fast_isel = true;
break; break;
@@ -1584,6 +1595,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_InternalByValue: case BuildFlag_InternalByValue:
build_context.internal_by_value = true; build_context.internal_by_value = true;
break; break;
case BuildFlag_InternalWeakMonomorphization:
build_context.internal_weak_monomorphization = true;
break;
case BuildFlag_InternalLLVMVerification:
build_context.internal_ignore_llvm_verification = true;
break;
case BuildFlag_Tilde: case BuildFlag_Tilde:
build_context.tilde_backend = true; build_context.tilde_backend = true;
@@ -2826,7 +2844,7 @@ gb_internal int print_show_help(String const arg0, String command, String option
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons."); print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons.");
print_usage_line(2, "Errs on missing trailing commas followed by a newline."); print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
print_usage_line(2, "Errs on deprecated syntax."); print_usage_line(2, "Errs on deprecated syntax.");
print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS)."); print_usage_line(2, "Errs when the attached-brace style is not adhered to (also known as 1TBS).");
print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token."); print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token.");
} }
} }
@@ -3050,7 +3068,8 @@ gb_internal void print_show_unused(Checker *c) {
if (e->token.string == "_") { if (e->token.string == "_") {
continue; continue;
} }
if (ptr_set_exists(&info->minimum_dependency_set, e)) {
if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
continue; continue;
} }
array_add(&unused, e); array_add(&unused, e);
@@ -3618,6 +3637,11 @@ int main(int arg_count, char const **arg_ptr) {
// return 1; // return 1;
// } // }
// Warn about Windows i386 thread-local storage limitations
if (build_context.metrics.arch == TargetArch_i386 && build_context.metrics.os == TargetOs_windows) {
gb_printf_err("Warning: Thread-local storage is disabled on Windows i386.\n");
}
// Check chosen microarchitecture. If not found or ?, print list. // Check chosen microarchitecture. If not found or ?, print list.
bool print_microarch_list = true; bool print_microarch_list = true;
if (build_context.microarch.len == 0 || build_context.microarch == str_lit("native")) { if (build_context.microarch.len == 0 || build_context.microarch == str_lit("native")) {
+57 -4
View File
@@ -57,10 +57,13 @@ gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) {
usize mask = s->capacity-1; usize mask = s->capacity-1;
usize hash_index = cast(usize)hash & mask; usize hash_index = cast(usize)hash & mask;
for (usize i = 0; i < s->capacity; i++) { for (usize i = 0; i < s->capacity; i++) {
Type *key = s->keys[hash_index].type; auto *e = &s->keys[hash_index];
if (are_types_identical_unique_tuples(key, pair.type)) { u64 hash = e->hash;
Type *key = e->type;
if (hash == pair.hash &&
are_types_identical_unique_tuples(key, pair.type)) {
return hash_index; return hash_index;
} else if (key == 0) { } else if (key == nullptr) {
return -1; return -1;
} }
hash_index = (hash_index+1)&mask; hash_index = (hash_index+1)&mask;
@@ -164,6 +167,48 @@ gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it
return type_set_update(s, pair); return type_set_update(s, pair);
} }
gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m) { // returns true if it previously existsed
rwlock_acquire_upgrade(m);
if (type_set_exists(s, pair)) {
rwlock_release_upgrade(m);
return true;
}
rwlock_release_upgrade_and_acquire_write(m);
defer (rwlock_release_write(m));
if (s->keys == nullptr) {
type_set_init(s);
} else if (type_set__full(s)) {
type_set_grow(s);
}
GB_ASSERT(s->count < s->capacity);
GB_ASSERT(s->capacity >= 0);
usize mask = s->capacity-1;
usize hash = cast(usize)pair.hash;
usize hash_index = (cast(usize)hash) & mask;
GB_ASSERT(hash_index < s->capacity);
for (usize i = 0; i < s->capacity; i++) {
TypeInfoPair *key = &s->keys[hash_index];
GB_ASSERT(!are_types_identical_unique_tuples(key->type, pair.type));
if (key->hash == TYPE_SET_TOMBSTONE || key->hash == 0) {
*key = pair;
s->count++;
return false;
}
hash_index = (hash_index+1)&mask;
}
GB_PANIC("ptr set out of memory");
return false;
}
gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m) { // returns true if it previously existsed
TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)};
return type_set_update_with_mutex(s, pair, m);
}
gb_internal Type *type_set_add(TypeSet *s, Type *ptr) { gb_internal Type *type_set_add(TypeSet *s, Type *ptr) {
type_set_update(s, ptr); type_set_update(s, ptr);
@@ -328,12 +373,20 @@ gb_internal u64 type_hash_canonical_type(Type *type) {
if (type == nullptr) { if (type == nullptr) {
return 0; return 0;
} }
u64 prev_hash = type->canonical_hash.load(std::memory_order_relaxed);
if (prev_hash != 0) {
return prev_hash;
}
u64 hash = fnv64a(nullptr, 0); u64 hash = fnv64a(nullptr, 0);
TypeWriter w = {}; TypeWriter w = {};
type_writer_make_hasher(&w, &hash); type_writer_make_hasher(&w, &hash);
write_type_to_canonical_string(&w, type); write_type_to_canonical_string(&w, type);
hash = hash ? hash : 1;
return hash ? hash : 1; type->canonical_hash.store(hash, std::memory_order_relaxed);
return hash;
} }
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) { gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
+2
View File
@@ -102,6 +102,8 @@ gb_internal Type *type_set_add (TypeSet *s, Type *ptr);
gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair); gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair);
gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed
gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed
gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m);
gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m);
gb_internal bool type_set_exists (TypeSet *s, Type *ptr); gb_internal bool type_set_exists (TypeSet *s, Type *ptr);
gb_internal void type_set_remove (TypeSet *s, Type *ptr); gb_internal void type_set_remove (TypeSet *s, Type *ptr);
gb_internal void type_set_clear (TypeSet *s); gb_internal void type_set_clear (TypeSet *s);
+13
View File
@@ -1,5 +1,7 @@
#include "parser_pos.cpp" #include "parser_pos.cpp"
gb_global std::atomic<bool> g_parsing_done;
gb_internal bool in_vet_packages(AstFile *file) { gb_internal bool in_vet_packages(AstFile *file) {
if (file == nullptr) { if (file == nullptr) {
return true; return true;
@@ -176,8 +178,12 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
return nullptr; return nullptr;
} }
if (f == nullptr) { if (f == nullptr) {
if (g_parsing_done.load(std::memory_order_relaxed)) {
f = node->file();
} else {
f = node->thread_safe_file(); f = node->thread_safe_file();
} }
}
Ast *n = alloc_ast_node(f, node->kind); Ast *n = alloc_ast_node(f, node->kind);
gb_memmove(n, node, ast_node_size(node->kind)); gb_memmove(n, node, ast_node_size(node->kind));
@@ -744,6 +750,7 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token
gb_internal Ast *ast_ident(AstFile *f, Token token) { gb_internal Ast *ast_ident(AstFile *f, Token token) {
Ast *result = alloc_ast_node(f, Ast_Ident); Ast *result = alloc_ast_node(f, Ast_Ident);
result->Ident.token = token; result->Ident.token = token;
result->Ident.hash = string_hash(token.string);
return result; return result;
} }
@@ -6592,6 +6599,10 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f)
} else if (string_starts_with(lc, str_lit("vet"))) { } else if (string_starts_with(lc, str_lit("vet"))) {
f->vet_flags = parse_vet_tag(tok, lc); f->vet_flags = parse_vet_tag(tok, lc);
f->vet_flags_set = true; f->vet_flags_set = true;
} else if (string_starts_with(lc, str_lit("test"))) {
if ((build_context.command_kind & Command_test) == 0) {
return false;
}
} else if (string_starts_with(lc, str_lit("ignore"))) { } else if (string_starts_with(lc, str_lit("ignore"))) {
return false; return false;
} else if (string_starts_with(lc, str_lit("private"))) { } else if (string_starts_with(lc, str_lit("private"))) {
@@ -6962,6 +6973,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
} }
} }
g_parsing_done.store(true, std::memory_order_relaxed);
return ParseFile_None; return ParseFile_None;
} }
+19
View File
@@ -27,6 +27,24 @@ enum AddressingMode : u8 {
Addressing_SwizzleVariable = 14, // Swizzle indexed variable Addressing_SwizzleVariable = 14, // Swizzle indexed variable
}; };
gb_global String const addressing_mode_strings[] = {
str_lit("Invalid"),
str_lit("NoValue"),
str_lit("Value"),
str_lit("Context"),
str_lit("Variable"),
str_lit("Constant"),
str_lit("Type"),
str_lit("Builtin"),
str_lit("ProcGroup"),
str_lit("MapIndex"),
str_lit("OptionalOk"),
str_lit("OptionalOkPtr"),
str_lit("SoaVariable"),
str_lit("SwizzleValue"),
str_lit("SwizzleVariable"),
};
struct TypeAndValue { struct TypeAndValue {
Type * type; Type * type;
AddressingMode mode; AddressingMode mode;
@@ -396,6 +414,7 @@ struct AstSplitArgs {
AST_KIND(Ident, "identifier", struct { \ AST_KIND(Ident, "identifier", struct { \
Token token; \ Token token; \
Entity *entity; \ Entity *entity; \
u32 hash; \
}) \ }) \
AST_KIND(Implicit, "implicit", Token) \ AST_KIND(Implicit, "implicit", Token) \
AST_KIND(Uninit, "uninitialized value", Token) \ AST_KIND(Uninit, "uninitialized value", Token) \
+45 -3
View File
@@ -16,6 +16,8 @@ template <typename T> gb_internal bool ptr_set_exists (PtrSet<T> *s, T ptr);
template <typename T> gb_internal void ptr_set_remove (PtrSet<T> *s, T ptr); template <typename T> gb_internal void ptr_set_remove (PtrSet<T> *s, T ptr);
template <typename T> gb_internal void ptr_set_clear (PtrSet<T> *s); template <typename T> gb_internal void ptr_set_clear (PtrSet<T> *s);
#define FOR_PTR_SET(element, set_) for (auto *it = &(set_).keys[0], element = it ? *it : nullptr; (set_).keys != nullptr && it < &(set_).keys[(set_).capacity]; it++) if (element = *it, (*it != nullptr && *it != cast(void *)~(uintptr)(0ull)))
gb_internal gbAllocator ptr_set_allocator(void) { gb_internal gbAllocator ptr_set_allocator(void) {
return heap_allocator(); return heap_allocator();
} }
@@ -83,7 +85,7 @@ gb_internal gb_inline void ptr_set_grow(PtrSet<T> *old_set) {
PtrSet<T> new_set = {}; PtrSet<T> new_set = {};
ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16)); ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
for (T ptr : *old_set) { FOR_PTR_SET(ptr, *old_set) {
bool was_new = ptr_set_update(&new_set, ptr); bool was_new = ptr_set_update(&new_set, ptr);
GB_ASSERT(!was_new); GB_ASSERT(!was_new);
} }
@@ -134,6 +136,44 @@ gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it pre
return false; return false;
} }
template <typename T>
gb_internal bool ptr_set_update_with_mutex(PtrSet<T> *s, T ptr, RWSpinLock *m) { // returns true if it previously existsed
rwlock_acquire_upgrade(m);
if (ptr_set_exists(s, ptr)) {
rwlock_release_upgrade(m);
return true;
}
rwlock_release_upgrade_and_acquire_write(m);
defer (rwlock_release_write(m));
if (s->keys == nullptr) {
ptr_set_init(s);
} else if (ptr_set__full(s)) {
ptr_set_grow(s);
}
GB_ASSERT(s->count < s->capacity);
GB_ASSERT(s->capacity >= 0);
usize mask = s->capacity-1;
u32 hash = ptr_map_hash_key(ptr);
usize hash_index = (cast(usize)hash) & mask;
GB_ASSERT(hash_index < s->capacity);
for (usize i = 0; i < s->capacity; i++) {
T *key = &s->keys[hash_index];
GB_ASSERT(*key != ptr);
if (*key == (T)PtrSet<T>::TOMBSTONE || *key == 0) {
*key = ptr;
s->count++;
return false;
}
hash_index = (hash_index+1)&mask;
}
GB_PANIC("ptr set out of memory");
return false;
}
template <typename T> template <typename T>
gb_internal T ptr_set_add(PtrSet<T> *s, T ptr) { gb_internal T ptr_set_add(PtrSet<T> *s, T ptr) {
ptr_set_update(s, ptr); ptr_set_update(s, ptr);
@@ -157,7 +197,7 @@ gb_internal gb_inline void ptr_set_clear(PtrSet<T> *s) {
gb_zero_size(s->keys, s->capacity*gb_size_of(T)); gb_zero_size(s->keys, s->capacity*gb_size_of(T));
} }
template <typename T> /*template <typename T>
struct PtrSetIterator { struct PtrSetIterator {
PtrSet<T> *set; PtrSet<T> *set;
usize index; usize index;
@@ -201,4 +241,6 @@ gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
template <typename T> template <typename T>
gb_internal PtrSetIterator<T> end(PtrSet<T> &set) noexcept { gb_internal PtrSetIterator<T> end(PtrSet<T> &set) noexcept {
return PtrSetIterator<T>{&set, set.capacity}; return PtrSetIterator<T>{&set, set.capacity};
} }*/
+2 -1
View File
@@ -36,7 +36,8 @@ gb_internal void mpsc_destroy(MPSCQueue<T> *q) {
template <typename T> template <typename T>
gb_internal MPSCNode<T> *mpsc_alloc_node(MPSCQueue<T> *q, T const &value) { gb_internal MPSCNode<T> *mpsc_alloc_node(MPSCQueue<T> *q, T const &value) {
auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>); // auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
auto new_node = gb_alloc_item(permanent_allocator(), MPSCNode<T>);
new_node->value = value; new_node->value = value;
return new_node; return new_node;
} }
+15 -10
View File
@@ -633,23 +633,28 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr); return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
} }
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX) #elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
#include <wchar.h>
#include <iconv.h>
gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) { gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
iconv_t conv = iconv_open("WCHAR_T", "UTF-8"); String string = copy_string(heap_allocator(), make_string(cast(u8 const*)multibyte_input, input_length)); /* Guarantee NULL terminator */
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size); u8* input = string.text;
iconv_close(conv);
return cast(int)result; mbstate_t ps = { 0 };
size_t result = mbsrtowcs(output, cast(const char**)&input, output_size, &ps);
gb_free(heap_allocator(), string.text);
return (result == (size_t)-1) ? -1 : (int)result;
} }
gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) { gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) {
iconv_t conv = iconv_open("UTF-8", "WCHAR_T"); String string = copy_string(heap_allocator(), make_string(cast(u8 const*)widechar_input, input_length)); /* Guarantee NULL terminator */
size_t result = iconv(conv, cast(char**) &widechar_input, &input_length, cast(char **)&output, &output_size); u8* input = string.text;
iconv_close(conv);
return cast(int)result; mbstate_t ps = { 0 };
size_t result = wcsrtombs(output, cast(const wchar_t**)&input, output_size, &ps);
gb_free(heap_allocator(), string.text);
return (result == (size_t)-1) ? -1 : (int)result;
} }
#else #else
#error Implement system #error Implement system
+13 -5
View File
@@ -19,6 +19,11 @@ enum GrabState {
Grab_Failed = 2, Grab_Failed = 2,
}; };
enum BroadcastWaitState {
Nobody_Waiting = 0,
Someone_Waiting = 1,
};
struct ThreadPool { struct ThreadPool {
gbAllocator threads_allocator; gbAllocator threads_allocator;
Slice<Thread> threads; Slice<Thread> threads;
@@ -54,8 +59,8 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
for_array_off(i, 1, pool->threads) { for_array_off(i, 1, pool->threads) {
Thread *t = &pool->threads[i]; Thread *t = &pool->threads[i];
pool->tasks_available.fetch_add(1, std::memory_order_acquire); pool->tasks_available.store(Nobody_Waiting);
futex_broadcast(&pool->tasks_available); futex_broadcast(&t->pool->tasks_available);
thread_join_and_destroy(t); thread_join_and_destroy(t);
} }
@@ -87,8 +92,10 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) {
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed); thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
thread->pool->tasks_left.fetch_add(1, std::memory_order_release); thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed); i32 state = Someone_Waiting;
if (thread->pool->tasks_available.compare_exchange_strong(state, Nobody_Waiting)) {
futex_broadcast(&thread->pool->tasks_available); futex_broadcast(&thread->pool->tasks_available);
}
} }
GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) { GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
@@ -230,12 +237,13 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
} }
// if we've done all our work, and there's nothing to steal, go to sleep // if we've done all our work, and there's nothing to steal, go to sleep
state = pool->tasks_available.load(std::memory_order_acquire); pool->tasks_available.store(Someone_Waiting);
if (!pool->running) { break; } if (!pool->running) { break; }
futex_wait(&pool->tasks_available, state); futex_wait(&pool->tasks_available, Someone_Waiting);
main_loop_continue:; main_loop_continue:;
} }
return 0; return 0;
} }
+48 -2
View File
@@ -195,7 +195,13 @@ gb_internal void mutex_lock(RecursiveMutex *m) {
// inside the lock // inside the lock
return; return;
} }
futex_wait(&m->owner, prev_owner);
// NOTE(lucas): we are doing spin lock since futex signal is expensive on OSX. The recursive locks are
// very short lived so we don't hit this mega often and I see no perform regression on windows (with
// a performance uplift on OSX).
//futex_wait(&m->owner, prev_owner);
yield_thread();
} }
} }
gb_internal bool mutex_try_lock(RecursiveMutex *m) { gb_internal bool mutex_try_lock(RecursiveMutex *m) {
@@ -216,7 +222,9 @@ gb_internal void mutex_unlock(RecursiveMutex *m) {
return; return;
} }
m->owner.exchange(0, std::memory_order_release); m->owner.exchange(0, std::memory_order_release);
futex_signal(&m->owner); // NOTE(lucas): see comment about spin lock in mutex_lock above
// futex_signal(&m->owner);
// outside the lock // outside the lock
} }
@@ -448,6 +456,44 @@ gb_internal void semaphore_wait(Semaphore *s) {
} }
#endif #endif
static const int RWLOCK_WRITER = 1<<0;
static const int RWLOCK_UPGRADED = 1<<1;
static const int RWLOCK_READER = 1<<2;
struct RWSpinLock {
Futex bits;
};
void rwlock_release_write(RWSpinLock *l) {
l->bits.fetch_and(~(RWLOCK_WRITER | RWLOCK_UPGRADED), std::memory_order_release);
futex_signal(&l->bits);
}
bool rwlock_try_acquire_upgrade(RWSpinLock *l) {
int value = l->bits.fetch_or(RWLOCK_UPGRADED, std::memory_order_acquire);
return (value & (RWLOCK_UPGRADED | RWLOCK_WRITER)) == 0;
}
void rwlock_acquire_upgrade(RWSpinLock *l) {
while (!rwlock_try_acquire_upgrade(l)) {
futex_wait(&l->bits, RWLOCK_UPGRADED | RWLOCK_WRITER);
}
}
void rwlock_release_upgrade(RWSpinLock *l) {
l->bits.fetch_add(-RWLOCK_UPGRADED, std::memory_order_acq_rel);
}
bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) {
int expect = RWLOCK_UPGRADED;
return l->bits.compare_exchange_strong(expect, RWLOCK_WRITER, std::memory_order_acq_rel);
}
void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) {
while (!rwlock_try_release_upgrade_and_acquire_write(l)) {
futex_wait(&l->bits, RWLOCK_UPGRADED);
}
}
struct Parker { struct Parker {
Futex state; Futex state;
}; };
+106 -23
View File
@@ -206,13 +206,18 @@ struct TypeProc {
bool optional_ok; bool optional_ok;
}; };
struct TypeNamed {
String name;
Type * base;
Entity *type_name; /* Entity_TypeName */
BlockingMutex gen_types_data_mutex;
GenTypesData *gen_types_data;
};
#define TYPE_KINDS \ #define TYPE_KINDS \
TYPE_KIND(Basic, BasicType) \ TYPE_KIND(Basic, BasicType) \
TYPE_KIND(Named, struct { \ TYPE_KIND(Named, TypeNamed) \
String name; \
Type * base; \
Entity *type_name; /* Entity_TypeName */ \
}) \
TYPE_KIND(Generic, struct { \ TYPE_KIND(Generic, struct { \
i64 id; \ i64 id; \
String name; \ String name; \
@@ -334,6 +339,7 @@ struct Type {
// NOTE(bill): These need to be at the end to not affect the unionized data // NOTE(bill): These need to be at the end to not affect the unionized data
std::atomic<i64> cached_size; std::atomic<i64> cached_size;
std::atomic<i64> cached_align; std::atomic<i64> cached_align;
std::atomic<u64> canonical_hash;
std::atomic<u32> flags; // TypeFlag std::atomic<u32> flags; // TypeFlag
bool failure; bool failure;
}; };
@@ -429,11 +435,8 @@ gb_internal Selection make_selection(Entity *entity, Array<i32> index, bool indi
} }
gb_internal void selection_add_index(Selection *s, isize index) { gb_internal void selection_add_index(Selection *s, isize index) {
// IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form
// of heap allocation
// TODO(bill): Find a way to use a backing buffer for initial use as the general case is probably .count<3
if (s->index.data == nullptr) { if (s->index.data == nullptr) {
array_init(&s->index, heap_allocator()); array_init(&s->index, permanent_allocator());
} }
array_add(&s->index, cast(i32)index); array_add(&s->index, cast(i32)index);
} }
@@ -441,7 +444,7 @@ gb_internal void selection_add_index(Selection *s, isize index) {
gb_internal Selection selection_combine(Selection const &lhs, Selection const &rhs) { gb_internal Selection selection_combine(Selection const &lhs, Selection const &rhs) {
Selection new_sel = lhs; Selection new_sel = lhs;
new_sel.indirect = lhs.indirect || rhs.indirect; new_sel.indirect = lhs.indirect || rhs.indirect;
new_sel.index = array_make<i32>(heap_allocator(), lhs.index.count+rhs.index.count); new_sel.index = array_make<i32>(permanent_allocator(), lhs.index.count+rhs.index.count);
array_copy(&new_sel.index, lhs.index, 0); array_copy(&new_sel.index, lhs.index, 0);
array_copy(&new_sel.index, rhs.index, lhs.index.count); array_copy(&new_sel.index, rhs.index, lhs.index.count);
return new_sel; return new_sel;
@@ -1230,7 +1233,6 @@ gb_internal bool is_type_named(Type *t) {
} }
gb_internal bool is_type_boolean(Type *t) { gb_internal bool is_type_boolean(Type *t) {
// t = core_type(t);
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; } if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
@@ -1239,7 +1241,6 @@ gb_internal bool is_type_boolean(Type *t) {
return false; return false;
} }
gb_internal bool is_type_integer(Type *t) { gb_internal bool is_type_integer(Type *t) {
// t = core_type(t);
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; } if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
@@ -1249,6 +1250,7 @@ gb_internal bool is_type_integer(Type *t) {
} }
gb_internal bool is_type_integer_like(Type *t) { gb_internal bool is_type_integer_like(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0; return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
} }
@@ -1281,7 +1283,6 @@ gb_internal bool is_type_integer_128bit(Type *t) {
return false; return false;
} }
gb_internal bool is_type_rune(Type *t) { gb_internal bool is_type_rune(Type *t) {
// t = core_type(t);
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; } if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
@@ -1290,7 +1291,6 @@ gb_internal bool is_type_rune(Type *t) {
return false; return false;
} }
gb_internal bool is_type_numeric(Type *t) { gb_internal bool is_type_numeric(Type *t) {
// t = core_type(t);
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; } if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
@@ -1377,14 +1377,20 @@ gb_internal bool is_type_ordered_numeric(Type *t) {
gb_internal bool is_type_constant_type(Type *t) { gb_internal bool is_type_constant_type(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; } if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { switch (t->kind) {
case Type_Basic:
if (t->Basic.kind == Basic_typeid) {
return true;
}
return (t->Basic.flags & BasicFlag_ConstantType) != 0; return (t->Basic.flags & BasicFlag_ConstantType) != 0;
} case Type_BitSet:
if (t->kind == Type_BitSet) {
return true; return true;
} case Type_Proc:
if (t->kind == Type_Proc) {
return true; return true;
case Type_Array:
return is_type_constant_type(t->Array.elem);
case Type_EnumeratedArray:
return is_type_constant_type(t->EnumeratedArray.elem);
} }
return false; return false;
} }
@@ -1448,24 +1454,28 @@ gb_internal bool is_type_tuple(Type *t) {
return t->kind == Type_Tuple; return t->kind == Type_Tuple;
} }
gb_internal bool is_type_uintptr(Type *t) { gb_internal bool is_type_uintptr(Type *t) {
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.kind == Basic_uintptr); return (t->Basic.kind == Basic_uintptr);
} }
return false; return false;
} }
gb_internal bool is_type_rawptr(Type *t) { gb_internal bool is_type_rawptr(Type *t) {
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_rawptr; return t->Basic.kind == Basic_rawptr;
} }
return false; return false;
} }
gb_internal bool is_type_u8(Type *t) { gb_internal bool is_type_u8(Type *t) {
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_u8; return t->Basic.kind == Basic_u8;
} }
return false; return false;
} }
gb_internal bool is_type_u16(Type *t) { gb_internal bool is_type_u16(Type *t) {
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_u16; return t->Basic.kind == Basic_u16;
} }
@@ -1612,6 +1622,7 @@ gb_internal bool is_matrix_square(Type *t) {
gb_internal bool is_type_valid_for_matrix_elems(Type *t) { gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
if (is_type_integer(t)) { if (is_type_integer(t)) {
return true; return true;
} else if (is_type_float(t)) { } else if (is_type_float(t)) {
@@ -1839,40 +1850,49 @@ gb_internal Type *base_complex_elem_type(Type *t) {
gb_internal bool is_type_struct(Type *t) { gb_internal bool is_type_struct(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return t->kind == Type_Struct; return t->kind == Type_Struct;
} }
gb_internal bool is_type_union(Type *t) { gb_internal bool is_type_union(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return t->kind == Type_Union; return t->kind == Type_Union;
} }
gb_internal bool is_type_soa_struct(Type *t) { gb_internal bool is_type_soa_struct(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None; return t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None;
} }
gb_internal bool is_type_raw_union(Type *t) { gb_internal bool is_type_raw_union(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_Struct && t->Struct.is_raw_union); return (t->kind == Type_Struct && t->Struct.is_raw_union);
} }
gb_internal bool is_type_enum(Type *t) { gb_internal bool is_type_enum(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_Enum); return (t->kind == Type_Enum);
} }
gb_internal bool is_type_bit_set(Type *t) { gb_internal bool is_type_bit_set(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_BitSet); return (t->kind == Type_BitSet);
} }
gb_internal bool is_type_bit_field(Type *t) { gb_internal bool is_type_bit_field(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_BitField); return (t->kind == Type_BitField);
} }
gb_internal bool is_type_map(Type *t) { gb_internal bool is_type_map(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return t->kind == Type_Map; return t->kind == Type_Map;
} }
gb_internal bool is_type_union_maybe_pointer(Type *t) { gb_internal bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Union && t->Union.variants.count == 1) { if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0]; Type *v = t->Union.variants[0];
return is_type_internally_pointer_like(v); return is_type_internally_pointer_like(v);
@@ -1883,6 +1903,7 @@ gb_internal bool is_type_union_maybe_pointer(Type *t) {
gb_internal bool is_type_union_maybe_pointer_original_alignment(Type *t) { gb_internal bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Union && t->Union.variants.count == 1) { if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0]; Type *v = t->Union.variants[0];
if (is_type_internally_pointer_like(v)) { if (is_type_internally_pointer_like(v)) {
@@ -1917,6 +1938,7 @@ gb_internal TypeEndianKind type_endian_kind_of(Type *t) {
gb_internal bool is_type_endian_big(Type *t) { gb_internal bool is_type_endian_big(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
if (t->Basic.flags & BasicFlag_EndianBig) { if (t->Basic.flags & BasicFlag_EndianBig) {
return true; return true;
@@ -1933,6 +1955,7 @@ gb_internal bool is_type_endian_big(Type *t) {
} }
gb_internal bool is_type_endian_little(Type *t) { gb_internal bool is_type_endian_little(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
if (t->Basic.flags & BasicFlag_EndianLittle) { if (t->Basic.flags & BasicFlag_EndianLittle) {
return true; return true;
@@ -1950,6 +1973,7 @@ gb_internal bool is_type_endian_little(Type *t) {
gb_internal bool is_type_endian_platform(Type *t) { gb_internal bool is_type_endian_platform(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) == 0; return (t->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) == 0;
} else if (t->kind == Type_BitSet) { } else if (t->kind == Type_BitSet) {
@@ -1965,6 +1989,7 @@ gb_internal bool types_have_same_internal_endian(Type *a, Type *b) {
} }
gb_internal bool is_type_endian_specific(Type *t) { gb_internal bool is_type_endian_specific(Type *t) {
t = core_type(t); t = core_type(t);
if (t == nullptr) { return false; }
if (t->kind == Type_BitSet) { if (t->kind == Type_BitSet) {
t = bit_set_to_int(t); t = bit_set_to_int(t);
} }
@@ -2062,19 +2087,23 @@ gb_internal Type *integer_endian_type_to_platform_type(Type *t) {
gb_internal bool is_type_any(Type *t) { gb_internal bool is_type_any(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_Basic && t->Basic.kind == Basic_any); return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
} }
gb_internal bool is_type_typeid(Type *t) { gb_internal bool is_type_typeid(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
return (t->kind == Type_Basic && t->Basic.kind == Basic_typeid); return (t->kind == Type_Basic && t->Basic.kind == Basic_typeid);
} }
gb_internal bool is_type_untyped_nil(Type *t) { gb_internal bool is_type_untyped_nil(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling // NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
return (t->kind == Type_Basic && (t->Basic.kind == Basic_UntypedNil || t->Basic.kind == Basic_UntypedUninit)); return (t->kind == Type_Basic && (t->Basic.kind == Basic_UntypedNil || t->Basic.kind == Basic_UntypedUninit));
} }
gb_internal bool is_type_untyped_uninit(Type *t) { gb_internal bool is_type_untyped_uninit(Type *t) {
t = base_type(t); t = base_type(t);
if (t == nullptr) { return false; }
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling // NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUninit); return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUninit);
} }
@@ -2484,18 +2513,70 @@ gb_internal bool type_has_nil(Type *t) {
return false; return false;
} }
gb_internal bool is_type_union_constantable(Type *type) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Union);
if (bt->Union.variants.count == 0) {
return true;
} else if (bt->Union.variants.count == 1) {
return is_type_constant_type(bt->Union.variants[0]);
}
for (Type *v : bt->Union.variants) {
if (!is_type_constant_type(v)) {
return false;
}
}
return true;
}
gb_internal bool is_type_raw_union_constantable(Type *type) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Struct);
GB_ASSERT(bt->Struct.is_raw_union);
for (Entity *f : bt->Struct.fields) {
if (!is_type_constant_type(f->type)) {
return false;
}
}
// return true;
return false; // Disable raw union constants for the time being
}
gb_internal bool elem_type_can_be_constant(Type *t) { gb_internal bool elem_type_can_be_constant(Type *t) {
t = base_type(t); t = base_type(t);
if (t == t_invalid) { if (t == t_invalid) {
return false; return false;
} }
if (is_type_any(t) || is_type_union(t) || is_type_raw_union(t)) { if (is_type_any(t)) {
return false; return false;
} }
if (is_type_raw_union(t)) {
return is_type_raw_union_constantable(t);
}
if (is_type_union(t)) {
return is_type_union_constantable(t);
}
return true; return true;
} }
gb_internal bool elem_cannot_be_constant(Type *t) {
if (is_type_any(t)) {
return true;
}
if (is_type_union(t)) {
return !is_type_union_constantable(t);
}
if (is_type_raw_union(t)) {
return !is_type_raw_union_constantable(t);
}
return false;
}
gb_internal bool is_type_lock_free(Type *t) { gb_internal bool is_type_lock_free(Type *t) {
t = core_type(t); t = core_type(t);
if (t == t_invalid) { if (t == t_invalid) {
@@ -2837,6 +2918,7 @@ gb_internal bool are_types_identical(Type *x, Type *y) {
return false; return false;
} }
// MUTEX_GUARD(&g_type_mutex);
return are_types_identical_internal(x, y, false); return are_types_identical_internal(x, y, false);
} }
gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) { gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
@@ -2864,6 +2946,7 @@ gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
return false; return false;
} }
// MUTEX_GUARD(&g_type_mutex);
return are_types_identical_internal(x, y, true); return are_types_identical_internal(x, y, true);
} }
@@ -3932,7 +4015,7 @@ gb_internal i64 type_size_of(Type *t) {
TypePath path{}; TypePath path{};
type_path_init(&path); type_path_init(&path);
{ {
MUTEX_GUARD(&g_type_mutex); // MUTEX_GUARD(&g_type_mutex);
size = type_size_of_internal(t, &path); size = type_size_of_internal(t, &path);
t->cached_size.store(size); t->cached_size.store(size);
} }
@@ -3952,7 +4035,7 @@ gb_internal i64 type_align_of(Type *t) {
TypePath path{}; TypePath path{};
type_path_init(&path); type_path_init(&path);
{ {
MUTEX_GUARD(&g_type_mutex); // MUTEX_GUARD(&g_type_mutex);
t->cached_align.store(type_align_of_internal(t, &path)); t->cached_align.store(type_align_of_internal(t, &path));
} }
type_path_free(&path); type_path_free(&path);
@@ -4678,7 +4761,7 @@ gb_internal Type *alloc_type_tuple_from_field_types(Type **field_types, isize fi
} }
Type *t = alloc_type_tuple(); Type *t = alloc_type_tuple();
t->Tuple.variables = slice_make<Entity *>(heap_allocator(), field_count); t->Tuple.variables = slice_make<Entity *>(permanent_allocator(), field_count);
Scope *scope = nullptr; Scope *scope = nullptr;
for_array(i, t->Tuple.variables) { for_array(i, t->Tuple.variables) {
+3
View File
@@ -1,4 +1,7 @@
*.bmp *.bmp
*.zip *.zip
*.png *.png
*.jpg
*.qoi
*.pbm
math_big_test_library.* math_big_test_library.*
@@ -54,6 +54,28 @@ test_small_array_push_back_elems :: proc(t: ^testing.T) {
testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2 })) testing.expect(t, slice_equal(small_array.slice(&array), []int { 1, 2 }))
} }
@(test)
test_small_array_resize :: proc(t: ^testing.T) {
array: small_array.Small_Array(4, int)
for i in 0..<4 {
small_array.append(&array, i+1)
}
testing.expect(t, slice_equal(small_array.slice(&array), []int{1, 2, 3, 4}), "Expected to initialize the array with 1, 2, 3, 4")
small_array.clear(&array)
testing.expect(t, slice_equal(small_array.slice(&array), []int{}), "Expected to clear the array")
small_array.non_zero_resize(&array, 4)
testing.expect(t, slice_equal(small_array.slice(&array), []int{1, 2, 3, 4}), "Expected non_zero_resize to set length 4 with previous values")
small_array.clear(&array)
small_array.resize(&array, 4)
testing.expect(t, slice_equal(small_array.slice(&array), []int{0, 0, 0, 0}), "Expected resize to set length 4 with zeroed values")
}
slice_equal :: proc(a, b: []int) -> bool { slice_equal :: proc(a, b: []int) -> bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
+5 -1
View File
@@ -7,7 +7,7 @@ import zipfile
import hashlib import hashlib
import hmac import hmac
TEST_SUITES = ['PNG', 'XML', 'BMP'] TEST_SUITES = ['PNG', 'XML', 'BMP', 'JPG']
DOWNLOAD_BASE_PATH = sys.argv[1] + "/{}" DOWNLOAD_BASE_PATH = sys.argv[1] + "/{}"
ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}" ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}"
HMAC_KEY = "https://odin-lang.org" HMAC_KEY = "https://odin-lang.org"
@@ -280,6 +280,10 @@ HMAC_DIGESTS = {
'rletopdown.bmp': "37500893aad0b40656aa80fd5c7c5f9b35d033018b8070d8b1d7baeb34c90f90462288b13295204b90aa3e5c9be797d22a328e3714ab259334e879a09a3de175", 'rletopdown.bmp': "37500893aad0b40656aa80fd5c7c5f9b35d033018b8070d8b1d7baeb34c90f90462288b13295204b90aa3e5c9be797d22a328e3714ab259334e879a09a3de175",
'shortfile.bmp': "be3ffade7999304f00f9b7d152b5b27811ad1166d0fd43004392467a28f44b6a4ec02a23c0296bacd4f02f8041cd824b9ca6c9fc31fed27e36e572113bb47d73", 'shortfile.bmp': "be3ffade7999304f00f9b7d152b5b27811ad1166d0fd43004392467a28f44b6a4ec02a23c0296bacd4f02f8041cd824b9ca6c9fc31fed27e36e572113bb47d73",
'emblem-1024.jpg': "d7b7e3ffaa5cda04c667e3742752091d78e02aa2d3c7a63406af679ce810a0a86666b10fcab12cc7ead2fadf2f6c2e1237bc94f892a62a4c218e18a20f96dbe4",
'emblem-1024-progressive.jpg': "7a6f4b112bd7189320c58dcddb9129968bcf268798c1e0c4f2243c10b3e3d9a6962c9f142d9fd65f8fb31e9a1e899008cae22b3ffde713250d315499b412e160",
'emblem-1024-gray.jpg': "4c25aaab92451e0452cdb165833b2b5a51978c2571de9d053950944667847666ba198d3001291615acda098ebe45b7d2d53c210c492f077b04a6bfe386f8a5fd",
'unicode.xml': "e0cdc94f07fdbb15eea811ed2ae6dcf494a83d197dafe6580c740270feb0d8f5f7146d4a7d4c2d2ea25f8bd9678bc986123484b39399819a6b7262687959d1ae", 'unicode.xml': "e0cdc94f07fdbb15eea811ed2ae6dcf494a83d197dafe6580c740270feb0d8f5f7146d4a7d4c2d2ea25f8bd9678bc986123484b39399819a6b7262687959d1ae",
} }
+64 -1
View File
@@ -5,7 +5,7 @@
List of contributors: List of contributors:
Jeroen van Rijn: Initial implementation. Jeroen van Rijn: Initial implementation.
A test suite for PNG, TGA, NetPBM, QOI and BMP. A test suite for PNG, TGA, NetPBM, QOI, BMP, and JPEG.
*/ */
#+feature dynamic-literals #+feature dynamic-literals
package test_core_image package test_core_image
@@ -19,6 +19,7 @@ import pbm "core:image/netpbm"
import "core:image/png" import "core:image/png"
import "core:image/qoi" import "core:image/qoi"
import "core:image/tga" import "core:image/tga"
import "core:image/jpeg"
import "core:bytes" import "core:bytes"
import "core:hash" import "core:hash"
@@ -28,6 +29,7 @@ import "core:time"
TEST_SUITE_PATH_PNG :: ODIN_ROOT + "tests/core/assets/PNG" TEST_SUITE_PATH_PNG :: ODIN_ROOT + "tests/core/assets/PNG"
TEST_SUITE_PATH_BMP :: ODIN_ROOT + "tests/core/assets/BMP" TEST_SUITE_PATH_BMP :: ODIN_ROOT + "tests/core/assets/BMP"
TEST_SUITE_PATH_JPG :: ODIN_ROOT + "tests/core/assets/JPG"
I_Error :: image.Error I_Error :: image.Error
@@ -49,6 +51,7 @@ Blend_BG_Keep :: image.Options{.blend_background, .alpha_add_if_missing}
Return_Metadata :: image.Options{.return_metadata} Return_Metadata :: image.Options{.return_metadata}
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata} No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
Dims :: struct { Dims :: struct {
width: int, width: int,
height: int, height: int,
@@ -2360,6 +2363,66 @@ run_bmp_suite :: proc(t: ^testing.T, suite: []Test) {
return return
} }
// JPG test image
Basic_JPG_Tests := []Test{
{
"emblem-1024", {
{Default, nil, {1024, 1024, 3, 8}, 0x_46a29e0f},
{Alpha_Add, nil, {1024, 1024, 4, 8}, 0x_cae2d532},
},
},
{
"emblem-1024-progressive", {
{Default, .Unsupported_Frame_Type, {1024, 1024, 3, 8}, 0x_46a29e0f},
},
},
{
"emblem-1024-gray", {
{Default, nil, {1024, 1024, 3, 8}, 0x_4115d669},
{Alpha_Add, nil, {1024, 1024, 4, 8}, 0x_db496297},
{No_Channel_Expansion, .Unsupported_Option, {1024, 1024, 1, 8}, 0},
},
},
}
@test
jpeg_test_basic :: proc(t: ^testing.T) {
run_jpg_suite(t, Basic_JPG_Tests)
}
run_jpg_suite :: proc(t: ^testing.T, suite: []Test) {
for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH_JPG, "/", file.file, ".jpg"}, context.allocator)
defer delete(test_file)
for test in file.tests {
img, err := jpeg.load(test_file, test.options)
defer jpeg.destroy(img)
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
// No point in running the other tests if it didn't load.
(err == nil) or_continue
pixels := bytes.buffer_to_bytes(&img.pixels)
dims := Dims{img.width, img.height, img.channels, img.depth}
testing.expectf(t, test.dims == dims, "%v has %v, expected: %v.", file.file, dims, test.dims)
img_hash := hash.crc32(pixels)
testing.expectf(t, test.hash == img_hash, "%v test #1's hash is %08x, expected %08x with %v.", file.file, img_hash, test.hash, test.options)
// Optionally save to QOI file to check file loaded properly during development
when false {
test_qoi := strings.concatenate({TEST_SUITE_PATH_JPG, "/", file.file, ".qoi"}, context.temp_allocator)
save_err := qoi.save(test_qoi, img)
testing.expectf(t, save_err == nil, "expected saving to QOI not to raise error, got %v", save_err)
}
}
}
return
}
@test @test
will_it_blend :: proc(t: ^testing.T) { will_it_blend :: proc(t: ^testing.T) {
Pixel :: image.RGB_Pixel Pixel :: image.RGB_Pixel
+7 -6
View File
@@ -44,11 +44,11 @@ expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, al
testing.expect(t, pool.used_blocks == nil) testing.expect(t, pool.used_blocks == nil)
} }
expect_pool_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, out_band_size: int) { expect_pool_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, block_size, out_band_size: int) {
testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!") testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!")
pool: mem.Dynamic_Pool pool: mem.Dynamic_Pool
mem.dynamic_pool_init(&pool, out_band_size = out_band_size) mem.dynamic_pool_init(&pool, block_size = block_size, out_band_size = out_band_size)
pool_allocator := mem.dynamic_pool_allocator(&pool) pool_allocator := mem.dynamic_pool_allocator(&pool)
element, err := mem.alloc(num_bytes, allocator = pool_allocator) element, err := mem.alloc(num_bytes, allocator = pool_allocator)
@@ -69,14 +69,15 @@ test_dynamic_pool_alloc_aligned :: proc(t: ^testing.T) {
@(test) @(test)
test_dynamic_pool_alloc_unaligned :: proc(t: ^testing.T) { test_dynamic_pool_alloc_unaligned :: proc(t: ^testing.T) {
expect_pool_allocation(t, expected_used_bytes = 8, num_bytes=1, alignment=8) expect_pool_allocation(t, expected_used_bytes = 8, num_bytes = 1, alignment = 8)
expect_pool_allocation(t, expected_used_bytes = 16, num_bytes=9, alignment=8) expect_pool_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8)
} }
@(test) @(test)
test_dynamic_pool_alloc_out_of_band :: proc(t: ^testing.T) { test_dynamic_pool_alloc_out_of_band :: proc(t: ^testing.T) {
expect_pool_allocation_out_of_band(t, num_bytes = 128, out_band_size = 128) expect_pool_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128)
expect_pool_allocation_out_of_band(t, num_bytes = 129, out_band_size = 128) expect_pool_allocation_out_of_band(t, num_bytes = 129, block_size = 512, out_band_size = 128)
expect_pool_allocation_out_of_band(t, num_bytes = 513, block_size = 512, out_band_size = 128)
} }
@(test) @(test)
+1 -1
View File
@@ -21,7 +21,7 @@ test_arpa_inet :: proc(t: ^testing.T) {
dst: [posix.INET6_ADDRSTRLEN]byte dst: [posix.INET6_ADDRSTRLEN]byte
} }
res := posix.inet_pton(af, src, &addr, size_of(addr)) res := posix.inet_pton(af, src, &addr)
testing.expect_value(t, res, expect, loc) testing.expect_value(t, res, expect, loc)
if expect == .SUCCESS { if expect == .SUCCESS {
@@ -0,0 +1,156 @@
#+feature dynamic-literals
package test_internal
import "core:testing"
@test
test_type_inference_on_literals_for_various_parameters_combinations :: proc(t: ^testing.T) {
Bit_Set :: bit_set[enum{A, B, C}]
group :: proc{proc_0, proc_1, proc_2, proc_3, proc_4, proc_5}
proc_0 :: proc() -> int { return 0 }
proc_1 :: proc(Bit_Set) -> int { return 1 }
proc_2 :: proc(int, Bit_Set) -> int { return 2 }
proc_3 :: proc(f32, Bit_Set) -> int { return 3 }
proc_4 :: proc(int, int, Bit_Set) -> int { return 4 }
proc_5 :: proc(Bit_Set, int, int, int) -> int { return 5 }
testing.expect_value(t, group({.A}), 1)
testing.expect_value(t, group(9, {.A}), 2)
testing.expect_value(t, group(3.14, {.A}), 3)
testing.expect_value(t, group(9, 9, {.A}), 4)
testing.expect_value(t, group({.A}, 9, 9, 9), 5)
}
@test
test_type_inference_on_literals_with_default_args :: proc(t: ^testing.T) {
{
Bit_Set :: bit_set[enum{A, B, C}]
proc_nil :: proc() { }
proc_default_arg :: proc(a: Bit_Set={.A}) -> Bit_Set { return a }
group :: proc{proc_nil, proc_default_arg}
testing.expect_value(t, group(Bit_Set{.A}), Bit_Set{.A})
testing.expect_value(t, group({.A}), Bit_Set{.A})
}
{
Bit_Set :: bit_set[enum{A, B, C}]
proc_1 :: proc(a: Bit_Set={.A}) -> int { return 1 }
proc_2 :: proc(a: Bit_Set={.B}, b: Bit_Set={.C}) -> int { return 2 }
group :: proc{proc_1, proc_2}
testing.expect_value(t, group(), 2)
testing.expect_value(t, group(Bit_Set{.A}), 2)
testing.expect_value(t, group({.A}), 2)
testing.expect_value(t, group({.B}, {.C}), 2)
}
}
@test
test_type_inference_on_literals_for_various_types :: proc(t: ^testing.T) {
proc_nil :: proc() { }
proc_array :: proc(a: [3]f32) -> [3]f32 { return a }
group_array :: proc{proc_nil, proc_array}
testing.expect_value(t, group_array([3]f32{1.1, 2.2, 3.3}), [3]f32{1.1, 2.2, 3.3})
testing.expect_value(t, group_array({1.1, 2.2, 3.3}), [3]f32{1.1, 2.2, 3.3})
testing.expect_value(t, group_array({0=1.1, 1=2.2, 2=3.3}), [3]f32{1.1, 2.2, 3.3})
testing.expect_value(t, group_array({}), [3]f32{})
proc_slice_u8 :: proc(a: []u8) -> []u8 { return a }
group_slice_u8 :: proc{proc_nil, proc_slice_u8}
testing.expect_value(t, len(group_slice_u8([]u8{1, 2, 3})), 3)
testing.expect_value(t, len(group_slice_u8({1, 2, 3})), 3)
testing.expect_value(t, len(group_slice_u8({0=1, 1=2, 2=3})), 3)
testing.expect_value(t, len(group_slice_u8({})), 0)
testing.expect_value(t, group_slice_u8(nil) == nil, true)
proc_dynamic_array :: proc(t: ^testing.T, array: [dynamic]u8, expected_len: int) {
if expected_len < 0 {
testing.expect_value(t, array == nil, true)
} else {
testing.expect_value(t, len(array), expected_len)
}
delete(array)
}
group_dynamic_array :: proc{proc_nil, proc_dynamic_array}
group_dynamic_array(t, [dynamic]u8{1, 2, 3}, 3)
group_dynamic_array(t, {1, 2, 3}, 3)
group_dynamic_array(t, {0=1, 1=2, 2=3}, 3)
group_dynamic_array(t, {}, 0)
group_dynamic_array(t, nil, -1)
Enum :: enum{A, B, C}
proc_enum :: proc(a: Enum) -> Enum { return a }
group_enum :: proc{proc_nil, proc_enum}
testing.expect_value(t, group_enum(Enum.A), Enum.A)
testing.expect_value(t, group_enum(.A), Enum.A)
proc_enumerated_array :: proc(a: [Enum]u8) -> [Enum]u8 { return a }
group_enumerated_array :: proc{proc_nil, proc_enumerated_array}
testing.expect_value(t, group_enumerated_array([Enum]u8{.A=1, .B=2, .C=3}), [Enum]u8{.A=1, .B=2, .C=3})
testing.expect_value(t, group_enumerated_array({.A=1, .B=2, .C=3}), [Enum]u8{.A=1, .B=2, .C=3})
Bit_Set :: bit_set[enum{A, B, C}]
proc_bit_set :: proc(a: Bit_Set) -> Bit_Set { return a }
group_bit_set :: proc{proc_nil, proc_bit_set}
testing.expect_value(t, group_bit_set(Bit_Set{.A}), Bit_Set{.A})
testing.expect_value(t, group_bit_set({.A}), Bit_Set{.A})
testing.expect_value(t, group_bit_set({}), Bit_Set{})
Struct :: struct{a: int, b: int, c: int}
proc_struct :: proc(a: Struct) -> Struct { return a }
group_struct :: proc{proc_nil, proc_struct}
testing.expect_value(t, group_struct(Struct{a = 9}), Struct{a = 9})
testing.expect_value(t, group_struct({a = 9}), Struct{a = 9})
testing.expect_value(t, group_struct({}), Struct{})
Raw_Union :: struct #raw_union{int_: int, f32_: f32}
proc_raw_union :: proc(a: Raw_Union) -> Raw_Union { return a }
group_raw_union :: proc{proc_nil, proc_raw_union}
testing.expect_value(t, group_raw_union(Raw_Union{int_ = 9}).int_, 9)
testing.expect_value(t, group_raw_union({int_ = 9}).int_, 9)
testing.expect_value(t, group_raw_union({}).int_, 0)
Union :: union{int, f32}
proc_union :: proc(a: Union) -> Union { return a }
group_union :: proc{proc_nil, proc_union}
testing.expect_value(t, group_union(int(9)).(int), 9)
testing.expect_value(t, group_union({}), nil)
proc_map :: proc(t: ^testing.T, map_: map[u8]u8, expected_len: int) {
if expected_len < 0 {
testing.expect_value(t, map_ == nil, true)
} else {
testing.expect_value(t, len(map_), expected_len)
}
delete(map_)
}
group_map :: proc{proc_nil, proc_map}
group_map(t, map[u8]u8{1=1, 2=2}, 2)
group_map(t, {1=1, 2=2}, 2)
group_map(t, {}, 0)
group_map(t, nil, -1)
Bit_Field :: bit_field u16 {a: u8|4, b: u8|4, c: u8|4}
proc_bit_field :: proc(a: Bit_Field) -> Bit_Field { return a }
group_bit_field :: proc{proc_nil, proc_bit_field}
testing.expect_value(t, group_bit_field(Bit_Field{a = 1}), Bit_Field{a = 1})
testing.expect_value(t, group_bit_field({a = 1}), Bit_Field{a = 1})
testing.expect_value(t, group_bit_field({}), Bit_Field{})
SOA_Array :: #soa[2]struct{int, int}
proc_soa_array :: proc(a: SOA_Array) -> SOA_Array { return a }
group_soa_array :: proc{proc_nil, proc_soa_array}
testing.expect_value(t, len(group_soa_array(SOA_Array{{}, {}})), 2)
testing.expect_value(t, len(group_soa_array({struct{int, int}{1, 2}, struct{int, int}{1, 2}})), 2)
testing.expect_value(t, len(group_soa_array({})), 2)
testing.expect_value(t, len(soa_zip(a=[]int{1, 2}, b=[]int{3, 4})), 2)
proc_matrix :: proc(a: matrix[2,2]f32) -> matrix[2,2]f32 { return a }
group_matrix :: proc{proc_nil, proc_matrix}
testing.expect_value(t, group_matrix(matrix[2,2]f32{1, 2, 3, 4}), matrix[2,2]f32{1, 2, 3, 4})
testing.expect_value(t, group_matrix(1), (matrix[2,2]f32)(1))
testing.expect_value(t, group_matrix({1, 2, 3, 4}), matrix[2,2]f32{1, 2, 3, 4})
testing.expect_value(t, group_matrix({}), matrix[2,2]f32{})
}
+1 -1
View File
@@ -905,7 +905,7 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
API_VERSION_1_4 :: (1<<22) | (4<<12) | (0) API_VERSION_1_4 :: (1<<22) | (4<<12) | (0)
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 { MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 {
\treturn (major<<22) | (minor<<12) | (patch) \treturn (major<<22) | (minor<<12) | (patch)
} }
+1 -1
View File
@@ -9,7 +9,7 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
API_VERSION_1_4 :: (1<<22) | (4<<12) | (0) API_VERSION_1_4 :: (1<<22) | (4<<12) | (0)
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 { MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 {
return (major<<22) | (minor<<12) | (patch) return (major<<22) | (minor<<12) | (patch)
} }