#load "win32.odin" // IMPORTANT NOTE(bill): Do not change the order of any of this data // The compiler relies upon this _exact_ order Type_Info :: union { Member :: struct { name: string type_: ^Type_Info offset: int } Record :: struct { fields: []Member } Named: struct { name: string base: ^Type_Info } Integer: struct { size: int // in bytes signed: bool } Float: struct { size: int // in bytes } String: struct {} Boolean: struct {} Pointer: struct { elem: ^Type_Info } Procedure: struct{} Array: struct { elem: ^Type_Info count: int } Slice: struct { elem: ^Type_Info } Vector: struct { elem: ^Type_Info count: int } Struct: Record Union: Record Raw_Union: Record Enum: struct { base: ^Type_Info } } assume :: proc(cond: bool) #foreign "llvm.assume" __debug_trap :: proc() #foreign "llvm.debugtrap" __trap :: proc() #foreign "llvm.trap" read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter" bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16" bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32" bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64" byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16" byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32" byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64" fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32" fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64" // TODO(bill): make custom heap procedures heap_alloc :: proc(len: int) -> rawptr #foreign "malloc" heap_dealloc :: proc(ptr: rawptr) #foreign "free" memory_zero :: proc(data: rawptr, len: int) { llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64" llvm_memset_64bit(data, 0, len, 1, false) } memory_compare :: proc(dst, src: rawptr, len: int) -> int { // TODO(bill): make a faster `memory_compare` a, b := slice_ptr(dst as ^byte, len), slice_ptr(src as ^byte, len) for i := 0; i < len; i++ { if a[i] != b[i] { return (a[i] - b[i]) as int } } return 0 } memory_copy :: proc(dst, src: rawptr, len: int) #inline { llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64" llvm_memcpy_64bit(dst, src, len, 1, false) } memory_move :: proc(dst, src: rawptr, len: int) #inline { llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64" llvm_memmove_64bit(dst, src, len, 1, false) } __string_eq :: proc(a, b: string) -> bool { if len(a) != len(b) { return false } if ^a[0] == ^b[0] { return true } return memory_compare(^a[0], ^b[0], len(a)) == 0 } __string_cmp :: proc(a, b : string) -> int { // Translation of http://mgronhol.github.io/fast-strcmp/ n := min(len(a), len(b)) fast := n/size_of(int) + 1 offset := (fast-1)*size_of(int) curr_block := 0 if n <= size_of(int) { fast = 0 } la := slice_ptr(^a[0] as ^int, fast) lb := slice_ptr(^b[0] as ^int, fast) for ; curr_block < fast; curr_block++ { if (la[curr_block] ~ lb[curr_block]) != 0 { for pos := curr_block*size_of(int); pos < n; pos++ { if (a[pos] ~ b[pos]) != 0 { return a[pos] as int - b[pos] as int } } } } for ; offset < n; offset++ { if (a[offset] ~ b[offset]) != 0 { return a[offset] as int - b[offset] as int } } return 0 } __string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } __string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } __string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } __string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } Allocation_Mode :: enum { ALLOC, DEALLOC, DEALLOC_ALL, RESIZE, } Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr Allocator :: struct { procedure: Allocator_Proc; data: rawptr } Context :: struct { thread_ptr: rawptr user_data: rawptr user_index: int allocator: Allocator } #thread_local context: Context DEFAULT_ALIGNMENT :: 2*size_of(int) __check_context :: proc() { if context.allocator.procedure == null { context.allocator = __default_allocator() } if context.thread_ptr == null { // TODO(bill): // context.thread_ptr = current_thread_pointer() } } alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) } alloc_align :: proc(size, alignment: int) -> rawptr #inline { __check_context() a := context.allocator return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0) } dealloc :: proc(ptr: rawptr) #inline { __check_context() a := context.allocator _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0) } dealloc_all :: proc(ptr: rawptr) #inline { __check_context() a := context.allocator _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0) } resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) } resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline { __check_context() a := context.allocator return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0) } default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr { if old_memory == null { return alloc_align(new_size, alignment) } if new_size == 0 { dealloc(old_memory) return null } if new_size == old_size { return old_memory } new_memory := alloc_align(new_size, alignment) if new_memory == null { return null } memory_copy(new_memory, old_memory, min(old_size, new_size)); dealloc(old_memory) return new_memory } __default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr { using Allocation_Mode match mode { case ALLOC: return heap_alloc(size) case RESIZE: return default_resize_align(old_memory, old_size, size, alignment) case DEALLOC: heap_dealloc(old_memory) case DEALLOC_ALL: // NOTE(bill): Does nothing } return null } __default_allocator :: proc() -> Allocator { return Allocator{ __default_allocator_proc, null, } } __assert :: proc(msg: string) { file_write(file_get_standard(File_Standard.ERROR), msg as []byte) // TODO(bill): Which is better? // __trap() __debug_trap() }