From 984e36a15147cb4ed681174fb1f97f4e1735411d Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 29 Jan 2017 20:15:16 +0000 Subject: [PATCH] Dynamic arrays --- build.bat | 2 +- code/demo.odin | 15 ++- core/_preload.odin | 115 ++++++++++++++-------- core/mem.odin | 3 + core/os_windows.odin | 11 +++ src/check_expr.c | 109 +++++++++++++++++++-- src/check_stmt.c | 7 ++ src/checker.c | 6 ++ src/ir.c | 225 ++++++++++++++++++++++++++++++++++++------- src/ir_print.c | 7 ++ src/parser.c | 34 +++++-- src/tokenizer.c | 49 +++++----- src/types.c | 72 +++++++++++++- 13 files changed, 537 insertions(+), 118 deletions(-) diff --git a/build.bat b/build.bat index bf007f574..bddb6ebf1 100644 --- a/build.bat +++ b/build.bat @@ -4,7 +4,7 @@ set exe_name=odin.exe :: Debug = 0, Release = 1 -set release_mode=1 +set release_mode=0 set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR- if %release_mode% EQU 0 ( rem Debug diff --git a/code/demo.odin b/code/demo.odin index 4198cffb6..32ff09d33 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -10,8 +10,19 @@ // #import win32 "sys/windows.odin"; main :: proc() { - // syntax(); - procedure_overloading(); + array: [dynamic]int; + defer free(array); + reserve(^array, 10); + + append(^array, 2); + append(^array, 3); + append(^array, 5); + append(^array, 7); + append(^array, 11); + append(^array, 13); + for val, idx in array { + fmt.println(val, idx); + } } syntax :: proc() { diff --git a/core/_preload.odin b/core/_preload.odin index 24d1fc21f..6417769af 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -116,6 +116,7 @@ __trap :: proc() #foreign __llvm_core "llvm.trap"; read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter"; +// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) Allocator_Mode :: enum u8 { ALLOC, FREE, @@ -164,12 +165,16 @@ alloc_align :: proc(size, alignment: int) -> rawptr #inline { return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0); } +free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline { + if ptr == nil { + return; + } + a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0); +} + free_ptr :: proc(ptr: rawptr) #inline { __check_context(); - a := context.allocator; - if ptr != nil { - a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0); - } + free_ptr_with_allocator(context.allocator, ptr); } free_all :: proc() #inline { __check_context(); @@ -217,46 +222,21 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, old_memory: rawptr, old_size: int, flags: u64) -> rawptr { using Allocator_Mode; - when false { - match mode { - case ALLOC: - total_size := size + alignment + size_of(mem.AllocationHeader); - ptr := os.heap_alloc(total_size); - header := (^mem.AllocationHeader)(ptr); - ptr = mem.align_forward(header+1, alignment); - mem.allocation_header_fill(header, ptr, size); - return mem.zero(ptr, size); + match mode { + case ALLOC: + return os.heap_alloc(size); - case FREE: - os.heap_free(mem.allocation_header(old_memory)); - return nil; + case FREE: + os.heap_free(old_memory); + return nil; - case FREE_ALL: - // NOTE(bill): Does nothing + case FREE_ALL: + // NOTE(bill): Does nothing - case RESIZE: - total_size := size + alignment + size_of(mem.AllocationHeader); - ptr := os.heap_resize(mem.allocation_header(old_memory), total_size); - header := (^mem.AllocationHeader)(ptr); - ptr = mem.align_forward(header+1, alignment); - mem.allocation_header_fill(header, ptr, size); - return mem.zero(ptr, size); - } - } else { - match mode { - case ALLOC: - return os.heap_alloc(size); - - case FREE: - os.heap_free(old_memory); - return nil; - - case FREE_ALL: - // NOTE(bill): Does nothing - - case RESIZE: - return os.heap_resize(old_memory, size); - } + case RESIZE: + ptr := os.heap_resize(old_memory, size); + assert(ptr != nil); + return ptr; } return nil; @@ -336,3 +316,56 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline { return utf8.decode_rune(s); } + +Raw_Dynamic_Array :: struct #ordered { + data: rawptr, + count: int, + capacity: int, + allocator: Allocator, +}; + +__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool { + array := cast(^Raw_Dynamic_Array)array_; + + if capacity <= array.capacity { + return true; + } + + __check_context(); + if array.allocator.procedure == nil { + array.allocator = context.allocator; + } + assert(array.allocator.procedure != nil); + + old_size := array.capacity * elem_size; + new_size := capacity * elem_size; + allocator := array.allocator; + + new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0); + if new_data == nil { + return false; + } + + array.data = new_data; + array.capacity = capacity; + return true; +} + + +__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, item_ptr: rawptr) { + array := cast(^Raw_Dynamic_Array)array_; + + ok := true; + if array.data == nil || array.capacity <= array.count { + capacity := 2 * array.capacity + 8; + ok := __dynamic_array_reserve(array, elem_size, elem_align, capacity); + } + if !ok { + // TODO(bill): Better error handling for failed reservation + return; + } + data := cast(^byte)array.data; + assert(data != nil); + mem.copy(data + (elem_size*array.count), item_ptr, elem_size); + array.count += 1; +} diff --git a/core/mem.odin b/core/mem.odin index c27b709db..bf789c608 100644 --- a/core/mem.odin +++ b/core/mem.odin @@ -80,6 +80,9 @@ allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: i } } allocation_header :: proc(data: rawptr) -> ^Allocation_Header { + if data == nil { + return nil; + } p := cast(^int)data; for (p-1)^ == -1 { p = (p-1); diff --git a/core/os_windows.odin b/core/os_windows.odin index 5cc21d30a..9066c6ca0 100644 --- a/core/os_windows.odin +++ b/core/os_windows.odin @@ -248,12 +248,23 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) { heap_alloc :: proc(size: int) -> rawptr { + assert(size > 0); return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size); } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + if new_size == 0 { + heap_free(ptr); + return nil; + } + if ptr == nil { + return heap_alloc(new_size); + } return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size); } heap_free :: proc(ptr: rawptr) { + if ptr == nil { + return; + } win32.HeapFree(win32.GetProcessHeap(), 0, ptr); } diff --git a/src/check_expr.c b/src/check_expr.c index defcf7c63..3e934c77c 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1140,6 +1140,13 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { goto end; case_end; + case_ast_node(dat, DynamicArrayType, e); + Type *elem = check_type_extra(c, dat->elem, NULL); + type = make_type_dynamic_array(c->allocator, elem); + goto end; + case_end; + + case_ast_node(vt, VectorType, e); Type *elem = check_type(c, vt->elem); @@ -1984,7 +1991,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { return; } - if (token_is_shift(op)) { + if (token_is_shift(op.kind)) { check_shift(c, x, y, node); return; } @@ -2019,7 +2026,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { return; } - if (token_is_comparison(op)) { + if (token_is_comparison(op.kind)) { check_comparison(c, x, y, op.kind); return; } @@ -2138,9 +2145,9 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) { // See above note in UnaryExpr case break; } - if (token_is_comparison(be->op)) { - } - else if (token_is_shift(be->op)) { + if (token_is_comparison(be->op.kind)) { + // NOTE(bill): Do nothing as the types are fine + } else if (token_is_shift(be->op.kind)) { update_expr_type(c, be->left, type, final); } else { update_expr_type(c, be->left, type, final); @@ -2658,11 +2665,13 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id ok = true; } else if (is_type_string(type)) { ok = true; + } else if (is_type_dynamic_array(type)) { + ok = true; } if (!ok) { gbString type_str = type_to_string(type); - error_node(operand->expr, "You can only free pointers, slices, and strings, got `%s`", type_str); + error_node(operand->expr, "Invalid type for `free`, got `%s`", type_str); gb_string_free(type_str); return false; } @@ -2671,6 +2680,80 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_NoValue; } break; + + case BuiltinProc_reserve: { + // reserve :: proc(^[dynamic]Type, count: int) { + Type *type = operand->type; + if (!is_type_pointer(type)) { + gbString str = type_to_string(type); + error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str); + gb_string_free(str); + return false; + } + type = type_deref(type); + if (!is_type_dynamic_array(type)) { + gbString str = type_to_string(type); + error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str); + gb_string_free(str); + return false; + } + + AstNode *capacity = ce->args.e[1]; + Operand op = {0}; + check_expr(c, &op, capacity); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type)) { + error_node(operand->expr, "`reserve` capacities must be an integer"); + return false; + } + + operand->type = NULL; + operand->mode = Addressing_NoValue; + } break; + + case BuiltinProc_append: { + // append :: proc(^[dynamic]Type, item: Type) { + Type *type = operand->type; + if (!is_type_pointer(type)) { + gbString str = type_to_string(type); + error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str); + gb_string_free(str); + return false; + } + type = base_type(type_deref(type)); + if (!is_type_dynamic_array(type)) { + gbString str = type_to_string(type); + error_node(operand->expr, "Expected a pointer to a dynamic array, got `%s`", str); + gb_string_free(str); + return false; + } + Type *elem = type->DynamicArray.elem; + + + AstNode *item = ce->args.e[1]; + Operand op = {0}; + check_expr(c, &op, item); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = op.type; + if (!check_is_assignable_to(c, &op, elem)) { + gbString elem_str = type_to_string(elem); + gbString str = type_to_string(arg_type); + error_node(operand->expr, "Expected `%s` for `append` item, got `%s`", elem_str, str); + gb_string_free(str); + gb_string_free(elem_str); + return false; + } + + operand->type = NULL; + operand->mode = Addressing_NoValue; + } break; + + case BuiltinProc_size_of: { // size_of :: proc(Type) -> untyped int Type *type = check_type(c, ce->args.e[0]); @@ -3839,6 +3922,11 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { o->type = t->Slice.elem; o->mode = Addressing_Variable; return true; + + case Type_DynamicArray: + o->type = t->DynamicArray.elem; + o->mode = Addressing_Variable; + return true; } return false; @@ -4750,6 +4838,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case Type_Slice: valid = true; break; + + case Type_DynamicArray: + valid = true; + break; } if (!valid) { @@ -5101,6 +5193,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = write_expr_to_string(str, at->elem); case_end; + case_ast_node(at, DynamicArrayType, node); + str = gb_string_appendc(str, "[dynamic]"); + str = write_expr_to_string(str, at->elem); + case_end; + case_ast_node(vt, VectorType, node); str = gb_string_appendc(str, "[vector "); str = write_expr_to_string(str, vt->count); diff --git a/src/check_stmt.c b/src/check_stmt.c index 3a1fdf023..6fdef7cf1 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -697,6 +697,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { val = t->Array.elem; idx = t_int; break; + + case Type_DynamicArray: + // val = make_type_pointer(c->allocator, t->DynamicArray.elem); + val = t->DynamicArray.elem; + idx = t_int; + break; + case Type_Slice: // val = make_type_pointer(c->allocator, t->Slice.elem); val = t->Slice.elem; diff --git a/src/checker.c b/src/checker.c index 4c9b58ce1..7740a6711 100644 --- a/src/checker.c +++ b/src/checker.c @@ -125,6 +125,9 @@ typedef enum BuiltinProcId { BuiltinProc_new_slice, BuiltinProc_free, + BuiltinProc_reserve, + BuiltinProc_append, + BuiltinProc_size_of, BuiltinProc_size_of_val, BuiltinProc_align_of, @@ -169,6 +172,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("new_slice"), 2, false, Expr_Expr}, {STR_LIT("free"), 1, false, Expr_Stmt}, + {STR_LIT("reserve"), 2, false, Expr_Stmt}, + {STR_LIT("append"), 2, false, Expr_Stmt}, + {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("size_of_val"), 1, false, Expr_Expr}, {STR_LIT("align_of"), 1, false, Expr_Expr}, diff --git a/src/ir.c b/src/ir.c index abbef8dbd..6c01d2e12 100644 --- a/src/ir.c +++ b/src/ir.c @@ -1620,6 +1620,13 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = make_type_pointer(a, t->Maybe.elem); break; case 1: result_type = make_type_pointer(a, t_bool); break; } + } else if (is_type_dynamic_array(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->DynamicArray.elem)); break; + case 1: result_type = t_int_ptr; break; + case 2: result_type = t_int_ptr; break; + case 3: result_type = t_allocator_ptr; break; + } } else { GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ir_type(s)), index); } @@ -1667,6 +1674,13 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = t->Maybe.elem; break; case 1: result_type = t_bool; break; } + } else if (is_type_dynamic_array(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->DynamicArray.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + case 3: result_type = t_allocator; break; + } } else { GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ir_type(s)), index); } @@ -1765,29 +1779,52 @@ irValue *ir_array_elem(irProcedure *proc, irValue *array) { return ir_emit_array_ep(proc, array, v_zero32); } irValue *ir_array_len(irProcedure *proc, irValue *array) { - Type *t = ir_type(array); + Type *t = base_type(ir_type(array)); GB_ASSERT(t->kind == Type_Array); return ir_make_const_int(proc->module->allocator, t->Array.count); } irValue *ir_slice_elem(irProcedure *proc, irValue *slice) { - Type *t = ir_type(slice); + Type *t = base_type(ir_type(slice)); GB_ASSERT(t->kind == Type_Slice); return ir_emit_struct_ev(proc, slice, 0); } -irValue *ir_slice_len(irProcedure *proc, irValue *slice) { - Type *t = ir_type(slice); +irValue *ir_slice_count(irProcedure *proc, irValue *slice) { + Type *t = base_type(ir_type(slice)); GB_ASSERT(t->kind == Type_Slice); return ir_emit_struct_ev(proc, slice, 1); } +irValue *ir_dynamic_array_elem(irProcedure *proc, irValue *da) { + Type *t = ir_type(da); + GB_ASSERT(t->kind == Type_DynamicArray); + return ir_emit_struct_ev(proc, da, 0); +} +irValue *ir_dynamic_array_count(irProcedure *proc, irValue *da) { + Type *t = base_type(ir_type(da)); + GB_ASSERT_MSG(t->kind == Type_DynamicArray, "%s", type_to_string(t)); + return ir_emit_struct_ev(proc, da, 1); +} +irValue *ir_dynamic_array_capacity(irProcedure *proc, irValue *da) { + Type *t = base_type(ir_type(da)); + GB_ASSERT(t->kind == Type_DynamicArray); + return ir_emit_struct_ev(proc, da, 2); +} +irValue *ir_dynamic_array_allocator(irProcedure *proc, irValue *da) { + Type *t = base_type(ir_type(da)); + GB_ASSERT(t->kind == Type_DynamicArray); + return ir_emit_struct_ev(proc, da, 3); +} + + + irValue *ir_string_elem(irProcedure *proc, irValue *string) { - Type *t = ir_type(string); + Type *t = base_type(ir_type(string)); GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); return ir_emit_struct_ev(proc, string, 0); } irValue *ir_string_len(irProcedure *proc, irValue *string) { - Type *t = ir_type(string); + Type *t = base_type(ir_type(string)); GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t)); return ir_emit_struct_ev(proc, string, 1); } @@ -1806,7 +1843,7 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base, if (high == NULL) { switch (bt->kind) { case Type_Array: high = ir_array_len(proc, base); break; - case Type_Slice: high = ir_slice_len(proc, base); break; + case Type_Slice: high = ir_slice_count(proc, base); break; case Type_Pointer: high = v_one; break; } } @@ -2069,7 +2106,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { // []byte/[]u8 <-> string if (is_type_u8_slice(src) && is_type_string(dst)) { irValue *elem = ir_slice_elem(proc, value); - irValue *len = ir_slice_len(proc, value); + irValue *len = ir_slice_count(proc, value); return ir_emit_string(proc, elem, len); } if (is_type_string(src) && is_type_u8_slice(dst)) { @@ -2912,12 +2949,25 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv case BuiltinProc_free: { ir_emit_comment(proc, str_lit("free")); - gbAllocator allocator = proc->module->allocator; + gbAllocator a = proc->module->allocator; AstNode *node = ce->args.e[0]; TypeAndValue tav = *type_and_value_of_expression(proc->module->info, node); Type *type = base_type(tav.type); irValue *val = ir_build_expr(proc, node); + + if (is_type_dynamic_array(type)) { + irValue *da_allocator = ir_emit_struct_ev(proc, val, 3); + + irValue *ptr = ir_emit_struct_ev(proc, val, 0); + ptr = ir_emit_conv(proc, ptr, t_rawptr); + + irValue **args = gb_alloc_array(a, irValue *, 1); + args[0] = da_allocator; + args[1] = ptr; + return ir_emit_global_call(proc, "free_ptr_with_allocator", args, 2); + } + irValue *ptr = NULL; if (is_type_pointer(type)) { ptr = val; @@ -2935,11 +2985,66 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv ptr = ir_emit_conv(proc, ptr, t_rawptr); - irValue **args = gb_alloc_array(allocator, irValue *, 1); + irValue **args = gb_alloc_array(a, irValue *, 1); args[0] = ptr; return ir_emit_global_call(proc, "free_ptr", args, 1); } break; + case BuiltinProc_reserve: { + ir_emit_comment(proc, str_lit("reserve")); + gbAllocator a = proc->module->allocator; + + irValue *array_ptr = ir_build_expr(proc, ce->args.e[0]); + Type *type = ir_type(array_ptr); + GB_ASSERT(is_type_pointer(type)); + type = base_type(type_deref(type)); + GB_ASSERT(is_type_dynamic_array(type)); + Type *elem = type->DynamicArray.elem; + + irValue *elem_size = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem)); + irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem)); + + array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr); + + irValue *capacity = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t_int); + + irValue **args = gb_alloc_array(a, irValue *, 4); + args[0] = array_ptr; + args[1] = elem_size; + args[2] = elem_align; + args[3] = capacity; + return ir_emit_global_call(proc, "__dynamic_array_reserve", args, 4); + } break; + + case BuiltinProc_append: { + ir_emit_comment(proc, str_lit("append")); + gbAllocator a = proc->module->allocator; + + irValue *array_ptr = ir_build_expr(proc, ce->args.e[0]); + Type *type = ir_type(array_ptr); + GB_ASSERT(is_type_pointer(type)); + type = base_type(type_deref(type)); + GB_ASSERT(is_type_dynamic_array(type)); + Type *elem = type->DynamicArray.elem; + + irValue *elem_size = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem)); + irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem)); + + array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr); + + irValue *item_ptr = ir_add_local_generated(proc, elem); + irValue *item = ir_build_expr(proc, ce->args.e[1]); + ir_emit_store(proc, item_ptr, item); + + irValue **args = gb_alloc_array(a, irValue *, 4); + args[0] = array_ptr; + args[1] = elem_size; + args[2] = elem_align; + args[3] = ir_emit_conv(proc, item_ptr, t_rawptr); + return ir_emit_global_call(proc, "__dynamic_array_append", args, 4); + } break; + + case BuiltinProc_assert: { ir_emit_comment(proc, str_lit("assert")); irValue *cond = ir_build_expr(proc, ce->args.e[0]); @@ -3010,8 +3115,8 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv irValue *dst = ir_emit_conv(proc, ir_slice_elem(proc, dst_slice), t_rawptr); irValue *src = ir_emit_conv(proc, ir_slice_elem(proc, src_slice), t_rawptr); - irValue *len_dst = ir_slice_len(proc, dst_slice); - irValue *len_src = ir_slice_len(proc, src_slice); + irValue *len_dst = ir_slice_count(proc, dst_slice); + irValue *len_src = ir_slice_count(proc, src_slice); irValue *cond = ir_emit_comp(proc, Token_Lt, len_dst, len_src); irValue *len = ir_emit_select(proc, cond, len_dst, len_src); @@ -3038,7 +3143,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv irValue *slice = ir_emit_load(proc, slice_ptr); irValue *elem = ir_slice_elem(proc, slice); - irValue *len = ir_slice_len(proc, slice); + irValue *len = ir_slice_count(proc, slice); irValue *cap = ir_slice_cap(proc, slice); Type *elem_type = type_deref(ir_type(elem)); @@ -3522,14 +3627,32 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { } } irValue *elem = ir_slice_elem(proc, slice); - irValue *len = ir_slice_len(proc, slice); + irValue *len = ir_slice_count(proc, slice); irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); irValue *v = ir_emit_ptr_offset(proc, elem, index); return ir_make_addr(v, expr); - } break; + case Type_DynamicArray: { + irValue *dynamic_array = NULL; + if (using_addr != NULL) { + dynamic_array = ir_emit_load(proc, using_addr); + } else { + dynamic_array = ir_build_expr(proc, ie->expr); + if (deref) { + dynamic_array = ir_emit_load(proc, dynamic_array); + } + } + irValue *elem = ir_dynamic_array_elem(proc, dynamic_array); + irValue *len = ir_dynamic_array_count(proc, dynamic_array); + irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); + ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + irValue *v = ir_emit_ptr_offset(proc, elem, index); + return ir_make_addr(v, expr); + } break; + + case Type_Basic: { // Basic_string TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr)); irValue *str; @@ -3580,7 +3703,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { case Type_Slice: { Type *slice_type = type; - if (high == NULL) high = ir_slice_len(proc, base); + if (high == NULL) high = ir_slice_count(proc, base); ir_emit_slice_bounds_check(proc, se->open, low, high, false); @@ -3588,14 +3711,30 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); irValue *slice = ir_add_local_generated(proc, slice_type); - irValue *gep0 = ir_emit_struct_ep(proc, slice, 0); - irValue *gep1 = ir_emit_struct_ep(proc, slice, 1); - ir_emit_store(proc, gep0, elem); - ir_emit_store(proc, gep1, len); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len); return ir_make_addr(slice, expr); } + case Type_DynamicArray: { + Type *dynamic_array = type; + + if (high == NULL) high = ir_dynamic_array_count(proc, base); + + ir_emit_slice_bounds_check(proc, se->open, low, high, false); + + irValue *elem = ir_emit_ptr_offset(proc, ir_dynamic_array_elem(proc, base), low); + irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); + irValue *slice = ir_add_local_generated(proc, dynamic_array); + + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len); + + return ir_make_addr(slice, expr); + } + + case Type_Array: { Type *slice_type = make_type_slice(a, type->Array.elem); @@ -3607,10 +3746,8 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); irValue *slice = ir_add_local_generated(proc, slice_type); - irValue *gep0 = ir_emit_struct_ep(proc, slice, 0); - irValue *gep1 = ir_emit_struct_ep(proc, slice, 1); - ir_emit_store(proc, gep0, elem); - ir_emit_store(proc, gep1, len); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len); return ir_make_addr(slice, expr); } @@ -3973,7 +4110,7 @@ void ir_emit_increment(irProcedure *proc, irValue *addr) { } -void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, +void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, irValue *count_ptr, irValue **val_, irValue **idx_, irBlock **loop_, irBlock **done_) { irValue *count = NULL; Type *expr_type = base_type(type_deref(ir_type(expr))); @@ -3981,12 +4118,6 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, case Type_Array: count = ir_make_const_int(proc->module->allocator, expr_type->Array.count); break; - case Type_Slice: - count = ir_slice_len(proc, expr); - break; - default: - GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type)); - break; } irValue *val = NULL; @@ -4007,6 +4138,9 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, body = ir_add_block(proc, NULL, "for.index.body"); done = ir_add_block(proc, NULL, "for.index.done"); + if (count == NULL) { + count = ir_emit_load(proc, count_ptr); + } irValue *cond = ir_emit_comp(proc, Token_Lt, incr, count); ir_emit_if(proc, cond, body, done); proc->curr_block = body; @@ -4020,7 +4154,10 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, } break; case Type_Slice: { irValue *elem = ir_slice_elem(proc, expr); - // val = ir_emit_ptr_offset(proc, elem, idx); + val = ir_emit_load(proc, ir_emit_ptr_offset(proc, elem, idx)); + } break; + case Type_DynamicArray: { + irValue *elem = ir_dynamic_array_elem(proc, expr); val = ir_emit_load(proc, ir_emit_ptr_offset(proc, elem, idx)); } break; default: @@ -4578,18 +4715,38 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { Type *et = base_type(type_deref(expr_type)); switch (et->kind) { case Type_Array: { + irValue *count_ptr = NULL; irValue *array = ir_build_addr(proc, rs->expr).addr; if (is_type_pointer(type_deref(ir_type(array)))) { array = ir_emit_load(proc, array); } - ir_build_range_indexed(proc, array, val_type, &val, &index, &loop, &done); + count_ptr = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, count_ptr, ir_make_const_int(proc->module->allocator, et->Array.count)); + ir_build_range_indexed(proc, array, val_type, count_ptr, &val, &index, &loop, &done); + } break; + case Type_DynamicArray: { + irValue *count_ptr = NULL; + irValue *array = ir_build_expr(proc, rs->expr); + if (is_type_pointer(type_deref(ir_type(array)))) { + count_ptr = ir_emit_struct_ep(proc, array, 1); + array = ir_emit_load(proc, array); + } else { + count_ptr = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, count_ptr, ir_dynamic_array_count(proc, array)); + } + ir_build_range_indexed(proc, array, val_type, count_ptr, &val, &index, &loop, &done); } break; case Type_Slice: { + irValue *count_ptr = NULL; irValue *slice = ir_build_expr(proc, rs->expr); if (is_type_pointer(ir_type(slice))) { + count_ptr = ir_emit_struct_ep(proc, slice, 1); slice = ir_emit_load(proc, slice); + } else { + count_ptr = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, count_ptr, ir_slice_count(proc, slice)); } - ir_build_range_indexed(proc, slice, val_type, &val, &index, &loop, &done); + ir_build_range_indexed(proc, slice, val_type, count_ptr, &val, &index, &loop, &done); } break; case Type_Basic: { irValue *string = ir_build_expr(proc, rs->expr); diff --git a/src/ir_print.c b/src/ir_print.c index 11d46deb7..37411a905 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -194,6 +194,13 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { ir_print_type(f, m, t->Slice.elem); ir_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); return; + case Type_DynamicArray: + ir_fprintf(f, "{"); + ir_print_type(f, m, t->Slice.elem); + ir_fprintf(f, "*, i%lld, i%lld,", word_bits, word_bits); + ir_print_type(f, m, t_allocator); + ir_fprintf(f, "}"); + return; case Type_Record: { switch (t->Record.kind) { case TypeRecord_Struct: diff --git a/src/parser.c b/src/parser.c index e5d6205e7..733ba0d8d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -341,6 +341,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \ AstNode *count; \ AstNode *elem; \ }) \ + AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \ + Token token; \ + AstNode *elem; \ + }) \ AST_NODE_KIND(VectorType, "vector type", struct { \ Token token; \ AstNode *count; \ @@ -553,6 +557,8 @@ Token ast_node_token(AstNode *node) { return node->MaybeType.token; case AstNode_ArrayType: return node->ArrayType.token; + case AstNode_DynamicArrayType: + return node->DynamicArrayType.token; case AstNode_VectorType: return node->VectorType.token; case AstNode_StructType: @@ -1080,6 +1086,13 @@ AstNode *make_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) return result; } +AstNode *make_dynamic_array_type(AstFile *f, Token token, AstNode *elem) { + AstNode *result = make_node(f, AstNode_DynamicArrayType); + result->DynamicArrayType.token = token; + result->DynamicArrayType.elem = elem; + return result; +} + AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { AstNode *result = make_node(f, AstNode_VectorType); result->VectorType.token = token; @@ -1267,7 +1280,6 @@ void fix_advance_to_next_stmt(AstFile *f) { case Token_if: case Token_when: case Token_return: - case Token_range: case Token_match: case Token_defer: case Token_asm: @@ -1463,7 +1475,7 @@ AstNode *parse_value(AstFile *f) { return value; } -AstNode *parse_identifier_or_type(AstFile *f); +AstNode *parse_type_or_ident(AstFile *f); void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) { @@ -1839,7 +1851,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { break; default: { - AstNode *type = parse_identifier_or_type(f); + AstNode *type = parse_type_or_ident(f); if (type != NULL) { // TODO(bill): Is this correct??? // NOTE(bill): Sanity check as identifiers should be handled already @@ -2204,7 +2216,7 @@ void parse_check_name_list_for_reserves(AstFile *f, AstNodeArray names) { } AstNode *parse_type_attempt(AstFile *f) { - AstNode *type = parse_identifier_or_type(f); + AstNode *type = parse_type_or_ident(f); if (type != NULL) { // TODO(bill): Handle? } @@ -2415,7 +2427,7 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) { if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) { Token tok = f->curr_token; next_token(f); - AstNode *type = parse_identifier_or_type(f); + AstNode *type = parse_type_or_ident(f); if (type == NULL) { error(tok, "variadic field missing type after `...`"); type = make_bad_expr(f, tok, f->curr_token); @@ -2561,7 +2573,7 @@ AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, Str return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace); } -AstNode *parse_identifier_or_type(AstFile *f) { +AstNode *parse_type_or_ident(AstFile *f) { switch (f->curr_token.kind) { case Token_Ident: { AstNode *e = parse_identifier(f); @@ -2597,7 +2609,6 @@ AstNode *parse_identifier_or_type(AstFile *f) { } case Token_OpenBracket: { - f->expr_level++; Token token = expect_token(f, Token_OpenBracket); AstNode *count_expr = NULL; bool is_vector = false; @@ -2607,16 +2618,23 @@ AstNode *parse_identifier_or_type(AstFile *f) { } else if (f->curr_token.kind == Token_vector) { next_token(f); if (f->curr_token.kind != Token_CloseBracket) { + f->expr_level++; count_expr = parse_expr(f, false); + f->expr_level--; } else { syntax_error(f->curr_token, "Vector type missing count"); } is_vector = true; + } else if (f->curr_token.kind == Token_dynamic) { + next_token(f); + expect_token(f, Token_CloseBracket); + return make_dynamic_array_type(f, token, parse_type(f)); } else if (f->curr_token.kind != Token_CloseBracket) { + f->expr_level++; count_expr = parse_expr(f, false); + f->expr_level--; } expect_token(f, Token_CloseBracket); - f->expr_level--; if (is_vector) { return make_vector_type(f, token, count_expr, parse_type(f)); } diff --git a/src/tokenizer.c b/src/tokenizer.c index 800107466..63d1192be 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -80,41 +80,40 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \ \ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ - TOKEN_KIND(Token_type, "type"), \ - TOKEN_KIND(Token_proc, "proc"), \ - TOKEN_KIND(Token_macro, "macro"), \ - TOKEN_KIND(Token_match, "match"), \ - TOKEN_KIND(Token_break, "break"), \ - TOKEN_KIND(Token_continue, "continue"), \ - TOKEN_KIND(Token_fallthrough, "fallthrough"), \ - TOKEN_KIND(Token_case, "case"), \ - TOKEN_KIND(Token_default, "default"), \ - TOKEN_KIND(Token_then, "then"), \ + TOKEN_KIND(Token_when, "when"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ TOKEN_KIND(Token_for, "for"), \ TOKEN_KIND(Token_in, "in"), \ - TOKEN_KIND(Token_when, "when"), \ - TOKEN_KIND(Token_range, "range"), \ + TOKEN_KIND(Token_break, "break"), \ + TOKEN_KIND(Token_continue, "continue"), \ + TOKEN_KIND(Token_fallthrough, "fallthrough"), \ + TOKEN_KIND(Token_match, "match"), \ + TOKEN_KIND(Token_type, "type"), \ + TOKEN_KIND(Token_default, "default"), \ + TOKEN_KIND(Token_case, "case"), \ TOKEN_KIND(Token_defer, "defer"), \ TOKEN_KIND(Token_return, "return"), \ TOKEN_KIND(Token_give, "give"), \ + TOKEN_KIND(Token_proc, "proc"), \ + TOKEN_KIND(Token_macro, "macro"), \ TOKEN_KIND(Token_struct, "struct"), \ TOKEN_KIND(Token_union, "union"), \ TOKEN_KIND(Token_raw_union, "raw_union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_vector, "vector"), \ + TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \ TOKEN_KIND(Token_immutable, "immutable"), \ TOKEN_KIND(Token_thread_local, "thread_local"), \ - TOKEN_KIND(Token_asm, "asm"), \ - TOKEN_KIND(Token_push_allocator, "push_allocator"), \ - TOKEN_KIND(Token_push_context, "push_context"), \ TOKEN_KIND(Token_cast, "cast"), \ TOKEN_KIND(Token_transmute, "transmute"), \ TOKEN_KIND(Token_down_cast, "down_cast"), \ TOKEN_KIND(Token_union_cast, "union_cast"), \ + TOKEN_KIND(Token_push_allocator, "push_allocator"), \ + TOKEN_KIND(Token_push_context, "push_context"), \ + TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \ TOKEN_KIND(Token_Count, "") @@ -264,20 +263,20 @@ void compiler_error(char *fmt, ...) { -gb_inline bool token_is_literal(Token t) { - return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1); +gb_inline bool token_is_literal(TokenKind t) { + return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1); } -gb_inline bool token_is_operator(Token t) { - return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1); +gb_inline bool token_is_operator(TokenKind t) { + return gb_is_between(t, Token__OperatorBegin+1, Token__OperatorEnd-1); } -gb_inline bool token_is_keyword(Token t) { - return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1); +gb_inline bool token_is_keyword(TokenKind t) { + return gb_is_between(t, Token__KeywordBegin+1, Token__KeywordEnd-1); } -gb_inline bool token_is_comparison(Token t) { - return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1); +gb_inline bool token_is_comparison(TokenKind t) { + return gb_is_between(t, Token__ComparisonBegin+1, Token__ComparisonEnd-1); } -gb_inline bool token_is_shift(Token t) { - return t.kind == Token_Shl || t.kind == Token_Shr; +gb_inline bool token_is_shift(TokenKind t) { + return t == Token_Shl || t == Token_Shr; } gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); } diff --git a/src/types.c b/src/types.c index 7bb849fff..2612ee72c 100644 --- a/src/types.c +++ b/src/types.c @@ -97,6 +97,7 @@ typedef struct TypeRecord { TYPE_KIND(Basic, BasicType) \ TYPE_KIND(Pointer, struct { Type *elem; }) \ TYPE_KIND(Array, struct { Type *elem; i64 count; }) \ + TYPE_KIND(DynamicArray, struct { Type *elem; }) \ TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \ TYPE_KIND(Slice, struct { Type *elem; }) \ TYPE_KIND(Maybe, struct { Type *elem; }) \ @@ -385,6 +386,12 @@ Type *make_type_array(gbAllocator a, Type *elem, i64 count) { return t; } +Type *make_type_dynamic_array(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_DynamicArray); + t->DynamicArray.elem = elem; + return t; +} + Type *make_type_vector(gbAllocator a, Type *elem, i64 count) { Type *t = alloc_type(a, Type_Vector); t->Vector.elem = elem; @@ -616,6 +623,10 @@ bool is_type_array(Type *t) { t = base_type(t); return t->kind == Type_Array; } +bool is_type_dynamic_array(Type *t) { + t = base_type(t); + return t->kind == Type_DynamicArray; +} bool is_type_slice(Type *t) { t = base_type(t); return t->kind == Type_Slice; @@ -690,6 +701,7 @@ bool type_has_nil(Type *t) { return false; } break; case Type_Slice: + case Type_DynamicArray: case Type_Proc: case Type_Pointer: case Type_Maybe: @@ -750,6 +762,12 @@ bool are_types_identical(Type *x, Type *y) { } break; + case Type_DynamicArray: + if (y->kind == Type_DynamicArray) { + return are_types_identical(x->DynamicArray.elem, y->DynamicArray.elem); + } + break; + case Type_Vector: if (y->kind == Type_Vector) { return (x->Vector.count == y->Vector.count) && are_types_identical(x->Vector.elem, y->Vector.elem); @@ -973,7 +991,10 @@ gb_global Entity *entity__any_data = NULL; gb_global Entity *entity__string_data = NULL; gb_global Entity *entity__string_count = NULL; gb_global Entity *entity__slice_count = NULL; -gb_global Entity *entity__slice_capacity = NULL; + +gb_global Entity *entity__dynamic_array_count = NULL; +gb_global Entity *entity__dynamic_array_capacity = NULL; +gb_global Entity *entity__dynamic_array_allocator = NULL; Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel); @@ -1142,6 +1163,39 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n sel.entity = entity__slice_count; return sel; } + } else if (type->kind == Type_DynamicArray) { + String data_str = str_lit("data"); + String count_str = str_lit("count"); + String capacity_str = str_lit("capacity"); + String allocator_str = str_lit("allocator"); + + if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 0); + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->DynamicArray.elem), false, 0); + return sel; + } else if (str_eq(field_name, count_str)) { + selection_add_index(&sel, 1); + if (entity__dynamic_array_count == NULL) { + entity__dynamic_array_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1); + } + sel.entity = entity__dynamic_array_count; + return sel; + } else if (str_eq(field_name, capacity_str)) { + selection_add_index(&sel, 2); + if (entity__dynamic_array_capacity == NULL) { + entity__dynamic_array_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2); + } + sel.entity = entity__dynamic_array_capacity; + return sel; + } else if (str_eq(field_name, allocator_str)) { + selection_add_index(&sel, 3); + if (entity__dynamic_array_allocator == NULL) { + entity__dynamic_array_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 3); + } + sel.entity = entity__dynamic_array_allocator; + return sel; + } } if (type->kind != Type_Record) { @@ -1351,6 +1405,14 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type type_path_pop(path); return align; } + + case Type_DynamicArray: + // data, count, capacity, allocator + return s.word_size; + + case Type_Slice: + return s.word_size; + case Type_Vector: { Type *elem = t->Vector.elem; type_path_push(path, elem); @@ -1537,6 +1599,9 @@ i64 type_size_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypeP return alignment*(count-1) + size; } break; + case Type_DynamicArray: + return 3*s.word_size + type_size_of(s, allocator, t_allocator); + case Type_Vector: { i64 count, bit_size, total_size_in_bits, total_size; count = t->Vector.count; @@ -1736,6 +1801,11 @@ gbString write_type_to_string(gbString str, Type *type) { str = write_type_to_string(str, type->Array.elem); break; + case Type_DynamicArray: + str = gb_string_appendc(str, "[dynamic]"); + str = write_type_to_string(str, type->DynamicArray.elem); + break; + case Type_Record: { switch (type->Record.kind) { case TypeRecord_Struct: