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