From 817ae643c5a469bf2f237525086288a2632fa500 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 13 Sep 2016 12:11:52 +0100 Subject: [PATCH] Remove len(), cap() and replace with selectors; fix defer in match --- code/demo.odin | 8 +- code/file.odin | 4 +- code/print.odin | 228 ++++++++++++++++++++++++++++--------- code/runtime.odin | 171 +++++++++++++++------------- src/checker/checker.cpp | 6 - src/checker/expr.cpp | 67 ----------- src/checker/type.cpp | 84 +++++++++++++- src/codegen/codegen.cpp | 16 ++- src/codegen/print_llvm.cpp | 2 +- src/codegen/ssa.cpp | 156 +++++++++---------------- src/parser.cpp | 45 +++++--- 11 files changed, 454 insertions(+), 333 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 64af72d26..b15888012 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,6 +1,10 @@ #load "basic.odin" main :: proc() { - println("% % % %", "Hellope", true, 6.28, [4]int{1, 2, 3, 4}) - println("%0 %1 %0", "Hellope", 34) + println("% % % %", "Hellope", true, 6.28, {4}int{1, 2, 3, 4}) + x: struct #ordered { + x, y: int + z: f32 + } + println("%", x) } diff --git a/code/file.odin b/code/file.odin index dc97e909e..a424e6d73 100644 --- a/code/file.odin +++ b/code/file.odin @@ -31,7 +31,7 @@ file_close :: proc(f: ^File) { file_write :: proc(f: ^File, buf: []byte) -> bool { bytes_written: i32 - return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0 + return WriteFile(f.handle, ^buf[0], buf.count as i32, ^bytes_written, null) != 0 } File_Standard :: type enum { @@ -95,7 +95,7 @@ read_entire_file :: proc(name: string) -> (string, bool) { ReadFile(f.handle as HANDLE, ^data[total_read], to_read, ^single_read_length, null) if single_read_length <= 0 { - delete(data) + free(^data[0]) return "", false } diff --git a/code/print.odin b/code/print.odin index 2a32f3c9e..01a972ebb 100644 --- a/code/print.odin +++ b/code/print.odin @@ -3,22 +3,12 @@ #load "file.odin" print_byte_buffer :: proc(buf: ^[]byte, b: []byte) { - // NOTE(bill): This is quite a hack - // TODO(bill): Should I allow the raw editing of a slice by exposing its - // internal members? - Raw_Bytes :: struct #ordered { - data: ^byte - len: int - cap: int - } - - slice := buf as ^Raw_Bytes - if slice.len < slice.cap { - n := min(slice.cap-slice.len, len(b)) + if buf.count < buf.capacity { + n := min(buf.capacity-buf.count, b.count) if n > 0 { - offset := ptr_offset(slice.data, slice.len) + offset := ptr_offset(buf.data, buf.count) memory_copy(offset, ^b[0], n) - slice.len += n + buf.count += n } } } @@ -29,7 +19,7 @@ print_string_to_buffer :: proc(buf: ^[]byte, s: string) { byte_reverse :: proc(b: []byte) { - n := len(b) + n := b.count for i := 0; i < n/2; i++ { b[i], b[n-1-i] = b[n-1-i], b[i] } @@ -184,6 +174,128 @@ print__f64 :: proc(buffer: ^[]byte, f: f64, decimal_places: int) { } } +print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) { + if ti == null { return } + + using Type_Info + match type info : ti { + case Named: + print_string_to_buffer(buf, info.name) + case Integer: + match { + case ti == type_info(int): + print_string_to_buffer(buf, "int") + case ti == type_info(uint): + print_string_to_buffer(buf, "uint") + default: + if info.signed { + print_string_to_buffer(buf, "i") + } else { + print_string_to_buffer(buf, "u") + } + print_int_to_buffer(buf, 8*info.size) + } + + case Float: + match info.size { + case 4: print_string_to_buffer(buf, "f32") + case 8: print_string_to_buffer(buf, "f64") + } + case String: print_string_to_buffer(buf, "string") + case Boolean: print_string_to_buffer(buf, "bool") + case Pointer: + print_string_to_buffer(buf, "^") + print_type_to_buffer(buf, info.elem) + case Procedure: + print_string_to_buffer(buf, "proc") + if info.params == null { + print_string_to_buffer(buf, "()") + } else { + count := (info.params as ^Tuple).fields.count + if count == 1 { print_string_to_buffer(buf, "(") } + print_type_to_buffer(buf, info.params) + if count == 1 { print_string_to_buffer(buf, ")") } + } + if info.results != null { + print_string_to_buffer(buf, " -> ") + print_type_to_buffer(buf, info.results) + } + case Tuple: + count := info.fields.count + if count != 1 { print_string_to_buffer(buf, "(") } + for i := 0; i < count; i++ { + if i > 0 { print_string_to_buffer(buf, ", ") } + + f := info.fields[i] + + if f.name.count > 0 { + print_string_to_buffer(buf, f.name) + print_string_to_buffer(buf, ": ") + } + print_type_to_buffer(buf, f.type_info) + } + if count != 1 { print_string_to_buffer(buf, ")") } + + case Array: + print_string_to_buffer(buf, "[") + print_int_to_buffer(buf, info.count) + print_string_to_buffer(buf, "]") + print_type_to_buffer(buf, info.elem) + case Slice: + print_string_to_buffer(buf, "[") + print_string_to_buffer(buf, "]") + print_type_to_buffer(buf, info.elem) + case Vector: + print_string_to_buffer(buf, "{") + print_int_to_buffer(buf, info.count) + print_string_to_buffer(buf, "}") + print_type_to_buffer(buf, info.elem) + + case Struct: + print_string_to_buffer(buf, "struct ") + if info.packed { print_string_to_buffer(buf, "#packed ") } + if info.ordered { print_string_to_buffer(buf, "#ordered ") } + print_string_to_buffer(buf, "{") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Union: + print_string_to_buffer(buf, "union {") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Raw_Union: + print_string_to_buffer(buf, "raw_union {") + for i := 0; i < info.fields.count; i++ { + if i > 0 { + print_string_to_buffer(buf, ", ") + } + print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, ": ") + print_type_to_buffer(buf, info.fields[i].type_info) + } + print_string_to_buffer(buf, "}") + + case Enum: + print_string_to_buffer(buf, "enum ") + print_type_to_buffer(buf, info.base) + print_string_to_buffer(buf, "{}") + } +} print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { @@ -197,7 +309,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { case Struct: print_string_to_buffer(buf, info.name) print_string_to_buffer(buf, "{") - for i := 0; i < len(b.fields); i++ { + for i := 0; i < b.fields.count; i++ { f := b.fields[i]; if i > 0 { print_string_to_buffer(buf, ", ") @@ -282,7 +394,9 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { case Array: print_string_to_buffer(buf, "[") - for i := 0; i < info.len; i++ { + defer print_string_to_buffer(buf, "]") + + for i := 0; i < info.count; i++ { if i > 0 { print_string_to_buffer(buf, ", ") } @@ -292,55 +406,66 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) { elem.type_info = info.elem print_any_to_buffer(buf, elem) } - print_string_to_buffer(buf, "]") case Slice: - slice := arg.data as ^struct { data: rawptr; len, cap: int } + slice := arg.data as ^[]byte print_string_to_buffer(buf, "[") - for i := 0; i < slice.len; i++ { + defer print_string_to_buffer(buf, "]") + + for i := 0; i < slice.count; i++ { if i > 0 { print_string_to_buffer(buf, ", ") } elem: any - elem.data = (slice.data as int + i*info.elem_size) as rawptr + elem.data = ptr_offset(slice.data, i*info.elem_size) elem.type_info = info.elem print_any_to_buffer(buf, elem) } - print_string_to_buffer(buf, "]") case Vector: print_string_to_buffer(buf, "<") - for i := 0; i < info.len; i++ { + defer print_string_to_buffer(buf, ">") + + for i := 0; i < info.count; i++ { if i > 0 { print_string_to_buffer(buf, ", ") } elem: any - elem.data = (arg.data as int + i*info.elem_size) as rawptr + elem.data = ptr_offset(arg.data as ^byte, i*info.elem_size) elem.type_info = info.elem print_any_to_buffer(buf, elem) } - print_string_to_buffer(buf, ">") case Struct: - print_string_to_buffer(buf, "(struct ") - for i := 0; i < len(info.fields); i++ { + print_string_to_buffer(buf, "struct") + print_string_to_buffer(buf, "{") + defer print_string_to_buffer(buf, "}") + + for i := 0; i < info.fields.count; i++ { if i > 0 { print_string_to_buffer(buf, ", ") } print_any_to_buffer(buf, info.fields[i].name) + print_string_to_buffer(buf, " = ") + a: any + a.data = ptr_offset(arg.data as ^byte, info.fields[i].offset) + a.type_info = info.fields[i].type_info + print_any_to_buffer(buf, a) } - print_string_to_buffer(buf, ")") - case Union: print_string_to_buffer(buf, "(union)") - case Raw_Union: print_string_to_buffer(buf, "(raw_union)") + + case Union: + print_string_to_buffer(buf, "(union)") + case Raw_Union: + print_string_to_buffer(buf, "(raw_union)") case Procedure: - print_string_to_buffer(buf, "(procedure 0x") + print_type_to_buffer(buf, arg.type_info) + print_string_to_buffer(buf, " @ 0x") print_pointer_to_buffer(buf, (arg.data as ^rawptr)^) - print_string_to_buffer(buf, ")") + default: - print_string_to_buffer(buf, "") } } @@ -373,7 +498,7 @@ print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) { parse_int :: proc(s: string, offset: int) -> (int, int) { result := 0 - for ; offset < len(s); offset++ { + for ; offset < s.count; offset++ { c := s[offset] as rune if !is_digit(c) { break @@ -389,8 +514,9 @@ print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) { prev := 0 implicit_index := 0 - for i := 0; i < len(fmt); i++ { + for i := 0; i < fmt.count; i++ { r := fmt[i] as rune + index := implicit_index if r != #rune "%" { continue @@ -398,26 +524,22 @@ print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) { print_string_to_buffer(buf, fmt[prev:i]) i++ // Skip % - if i >= len(fmt) { - return + if i < fmt.count { + next := fmt[i] as rune + + if next == #rune "%" { + print_string_to_buffer(buf, "%") + i++ + prev = i + continue + } + + if is_digit(next) { + index, i = parse_int(fmt, i) + } } - next := fmt[i] as rune - if next == #rune "%" { - print_string_to_buffer(buf, "%") - i++ - prev = i - continue - } - - index := implicit_index - set_prev := true - - if is_digit(next) { - index, i = parse_int(fmt, i) - } - - if 0 <= index && index < len(args) { + if 0 <= index && index < args.count { print_any_to_buffer(buf, args[index]) implicit_index = index+1 } else { diff --git a/code/runtime.odin b/code/runtime.odin index 5f42c39c5..21a00c8b9 100644 --- a/code/runtime.odin +++ b/code/runtime.odin @@ -11,6 +11,8 @@ Type_Info :: union { } Record :: struct #ordered { fields: []Member + packed: bool + ordered: bool } @@ -38,7 +40,7 @@ Type_Info :: union { Array: struct #ordered { elem: ^Type_Info elem_size: int - len: int + count: int } Slice: struct #ordered { elem: ^Type_Info @@ -47,7 +49,7 @@ Type_Info :: union { Vector: struct #ordered { elem: ^Type_Info elem_size: int - len: int + count: int } Tuple: Record Struct: Record @@ -81,7 +83,7 @@ heap_alloc :: proc(len: int) -> rawptr { return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len) } -heap_dealloc :: proc(ptr: rawptr) { +heap_free :: proc(ptr: rawptr) { _ = HeapFree(GetProcessHeap(), 0, ptr) } @@ -108,18 +110,18 @@ memory_copy :: proc(dst, src: rawptr, len: int) #inline { } __string_eq :: proc(a, b: string) -> bool { - if len(a) != len(b) { + if a.count != b.count { return false } if ^a[0] == ^b[0] { return true } - return memory_compare(^a[0], ^b[0], len(a)) == 0 + return memory_compare(^a[0], ^b[0], a.count) == 0 } __string_cmp :: proc(a, b : string) -> int { // Translation of http://mgronhol.github.io/fast-strcmp/ - n := min(len(a), len(b)) + n := min(a.count, b.count) fast := n/size_of(int) + 1 offset := (fast-1)*size_of(int) @@ -159,20 +161,64 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > - -Allocation_Mode :: enum { - ALLOC, - DEALLOC, - DEALLOC_ALL, - RESIZE, +__assert :: proc(msg: string) { + file_write(file_get_standard(File_Standard.ERROR), msg as []byte) + __debug_trap() } -Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, flags: u64) -> rawptr +__bounds_check_error :: proc(file: string, line, column: int, + index, count: int) { + if 0 <= index && index < count { + return + } + // TODO(bill): Probably reduce the need for `print` in the runtime if possible + println_err("%(%:%) Index % is out of bounds range [0, %)", + file, line, column, index, count) + __debug_trap() +} + +__slice_expr_error :: proc(file: string, line, column: int, + low, high, max: int) { + if 0 <= low && low <= high && high <= max { + return + } + println_err("%(%:%) Invalid slice indices: [%:%:%]", + file, line, column, low, high, max) + __debug_trap() +} +__substring_expr_error :: proc(file: string, line, column: int, + low, high: int) { + if 0 <= low && low <= high { + return + } + println_err("%(%:%) Invalid substring indices: [%:%:%]", + file, line, column, low, high) + __debug_trap() +} + + + + + + + + + + Allocator :: struct { - procedure: Allocator_Proc; + Mode :: enum { + ALLOC, + FREE, + FREE_ALL, + RESIZE, + } + Proc :: type proc(allocator_data: rawptr, mode: Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64) -> rawptr + + + procedure: Proc; data: rawptr } @@ -180,24 +226,26 @@ Allocator :: struct { Context :: struct { thread_ptr: rawptr + allocator: Allocator + user_data: rawptr user_index: int - - allocator: Allocator } -#thread_local context: Context +#thread_local __context: Context + DEFAULT_ALIGNMENT :: 2*size_of(int) -__check_context :: proc() { - if context.allocator.procedure == null { - context.allocator = __default_allocator() +__check_context :: proc(c: ^Context) { + assert(c != null) + if c.allocator.procedure == null { + c.allocator = __default_allocator() } - if context.thread_ptr == null { + if c.thread_ptr == null { // TODO(bill): - // context.thread_ptr = current_thread_pointer() + // c.thread_ptr = current_thread_pointer() } } @@ -205,28 +253,28 @@ __check_context :: proc() { alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) } alloc_align :: proc(size, alignment: int) -> rawptr #inline { - __check_context() - a := context.allocator - return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0) + __check_context(^__context) + a := __context.allocator + return a.procedure(a.data, Allocator.Mode.ALLOC, size, alignment, null, 0, 0) } -dealloc :: proc(ptr: rawptr) #inline { - __check_context() - a := context.allocator - _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0) +free :: proc(ptr: rawptr) #inline { + __check_context(^__context) + a := __context.allocator + _ = a.procedure(a.data, Allocator.Mode.FREE, 0, 0, ptr, 0, 0) } -dealloc_all :: proc(ptr: rawptr) #inline { - __check_context() - a := context.allocator - _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0) +free_all :: proc() #inline { + __check_context(^__context) + a := __context.allocator + _ = a.procedure(a.data, Allocator.Mode.FREE_ALL, 0, 0, null, 0, 0) } resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) } resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline { - __check_context() - a := context.allocator - return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0) + __check_context(^__context) + a := __context.allocator + return a.procedure(a.data, Allocator.Mode.RESIZE, new_size, alignment, ptr, old_size, 0) } @@ -237,7 +285,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: } if new_size == 0 { - dealloc(old_memory) + free(old_memory) return null } @@ -251,24 +299,24 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: } memory_copy(new_memory, old_memory, min(old_size, new_size)); - dealloc(old_memory) + free(old_memory) return new_memory } -__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, +__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr { - using Allocation_Mode + using Allocator.Mode match mode { case ALLOC: return heap_alloc(size) case RESIZE: return default_resize_align(old_memory, old_size, size, alignment) - case DEALLOC: - heap_dealloc(old_memory) + case FREE: + heap_free(old_memory) return null - case DEALLOC_ALL: + case FREE_ALL: // NOTE(bill): Does nothing } @@ -277,40 +325,11 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, __default_allocator :: proc() -> Allocator { return Allocator{ - __default_allocator_proc, - null, + procedure = __default_allocator_proc, + data = null, } } -__assert :: proc(msg: string) { - file_write(file_get_standard(File_Standard.ERROR), msg as []byte) - __debug_trap() -} - -__bounds_check_error :: proc(file: string, line, column: int, - index, count: int) { - println_err("%(%:%) Index % is out of bounds range [0, %)", - file, line, column, index, count) - __debug_trap() -} - -__slice_expr_error :: proc(file: string, line, column: int, - low, high, max: int) { - print_err("%(%:%) Invalid slice indices: [%:%:%]\n", - file, line, column, low, high, max) - __debug_trap() -} -__substring_expr_error :: proc(file: string, line, column: int, - low, high: int) { - print_err("%(%:%) Invalid substring indices: [%:%:%]\n", - file, line, column, low, high) - __debug_trap() -} - - - - - diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 735ed827c..eb1ab9674 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -124,7 +124,6 @@ enum BuiltinProcId { BuiltinProc_new, BuiltinProc_new_slice, - BuiltinProc_delete, BuiltinProc_size_of, BuiltinProc_size_of_val, @@ -139,8 +138,6 @@ enum BuiltinProcId { BuiltinProc_compile_assert, BuiltinProc_assert, - BuiltinProc_len, - BuiltinProc_cap, BuiltinProc_copy, BuiltinProc_append, @@ -168,7 +165,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("new"), 1, false, Expr_Expr}, {STR_LIT("new_slice"), 2, true, Expr_Expr}, - {STR_LIT("delete"), 1, false, Expr_Stmt}, {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("size_of_val"), 1, false, Expr_Expr}, @@ -183,8 +179,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("compile_assert"), 1, false, Expr_Stmt}, {STR_LIT("assert"), 1, false, Expr_Stmt}, - {STR_LIT("len"), 1, false, Expr_Expr}, - {STR_LIT("cap"), 1, false, Expr_Expr}, {STR_LIT("copy"), 2, false, Expr_Expr}, {STR_LIT("append"), 2, false, Expr_Expr}, diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 0c89d1580..979b4d215 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -2095,21 +2095,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; operand->type = make_type_slice(c->allocator, type); } break; - case BuiltinProc_delete: { - // delete :: proc(ptr: ^T) - Type *type = get_base_type(operand->type); - if (!is_type_pointer(type) && !is_type_slice(type)) { - gbString type_str = type_to_string(operand->type); - defer (gb_string_free(type_str)); - error(&c->error_collector, ast_node_token(call), - "Expected a pointer or slice to `delete`, got `%s`", - type_str); - return false; - } - - operand->mode = Addressing_NoValue; - operand->type = NULL; - } break; case BuiltinProc_size_of: { // size_of :: proc(Type) -> int @@ -2289,58 +2274,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; - // TODO(bill): Should these be procedures and are their names appropriate? - case BuiltinProc_len: - case BuiltinProc_cap: { - Type *t = get_base_type(operand->type); - - AddressingMode mode = Addressing_Invalid; - ExactValue value = {}; - - switch (t->kind) { - case Type_Basic: - if (id == BuiltinProc_len) { - if (is_type_string(t)) { - if (operand->mode == Addressing_Constant) { - mode = Addressing_Constant; - value = make_exact_value_integer(operand->value.value_string); - } else { - mode = Addressing_Value; - } - } - } - break; - - case Type_Array: - mode = Addressing_Constant; - value = make_exact_value_integer(t->Array.count); - break; - - case Type_Vector: - mode = Addressing_Constant; - value = make_exact_value_integer(t->Vector.count); - break; - - case Type_Slice: - mode = Addressing_Value; - break; - } - - if (mode == Addressing_Invalid) { - gbString str = expr_to_string(operand->expr); - error(&c->error_collector, ast_node_token(operand->expr), - "Invalid expression `%s` for `%.*s`", - str, LIT(bp->name)); - gb_string_free(str); - return false; - } - - operand->mode = mode; - operand->type = t_int; - operand->value = value; - - } break; - case BuiltinProc_copy: { // copy :: proc(x, y: []Type) -> int Type *dest_type = NULL, *src_type = NULL; diff --git a/src/checker/type.cpp b/src/checker/type.cpp index b4b1c491e..68851641a 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -350,6 +350,7 @@ gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_byte = &basic_type_aliases[Basic_byte]; gb_global Type *t_rune = &basic_type_aliases[Basic_rune]; + gb_global Type *t_type_info = NULL; gb_global Type *t_type_info_ptr = NULL; gb_global Type *t_type_info_member = NULL; @@ -588,7 +589,9 @@ b32 are_types_identical(Type *x, Type *y) { case TypeRecord_Struct: case TypeRecord_RawUnion: case TypeRecord_Union: - if (x->Record.field_count == y->Record.field_count) { + if (x->Record.field_count == y->Record.field_count && + x->Record.struct_is_packed == y->Record.struct_is_packed && + x->Record.struct_is_ordered == y->Record.struct_is_ordered) { for (isize i = 0; i < x->Record.field_count; i++) { if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) { return false; @@ -709,6 +712,8 @@ void selection_add_index(Selection *s, isize index) { gb_global Entity *entity__any_type_info = NULL; gb_global Entity *entity__any_data = NULL; +gb_global Entity *entity__string_data = NULL; +gb_global Entity *entity__string_count = NULL; Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) { GB_ASSERT(type_ != NULL); @@ -717,6 +722,7 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se return empty_selection; } + gbAllocator a = gb_heap_allocator(); Type *type = type_deref(type_); b32 is_ptr = type != type_; type = get_base_type(type); @@ -729,12 +735,12 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se if (entity__any_type_info == NULL) { Token token = {Token_Identifier}; token.string = type_info_str; - entity__any_type_info = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false, 0); + entity__any_type_info = make_entity_field(a, NULL, token, t_type_info_ptr, false, 0); } if (entity__any_data == NULL) { Token token = {Token_Identifier}; token.string = data_str; - entity__any_data = make_entity_field(gb_heap_allocator(), NULL, token, t_rawptr, false, 1); + entity__any_data = make_entity_field(a, NULL, token, t_rawptr, false, 1); } if (are_strings_equal(field_name, type_info_str)) { @@ -747,9 +753,81 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se return sel; } } break; + case Basic_string: { + String data_str = make_string("data"); + String count_str = make_string("count"); + if (entity__string_data == NULL) { + Token token = {Token_Identifier}; + token.string = data_str; + entity__string_data = make_entity_field(a, NULL, token, make_type_pointer(a, t_byte), false, 0); + } + + if (entity__string_count == NULL) { + Token token = {Token_Identifier}; + token.string = count_str; + entity__string_count = make_entity_field(a, NULL, token, t_int, false, 1); + } + + if (are_strings_equal(field_name, data_str)) { + selection_add_index(&sel, 0); + sel.entity = entity__string_data; + return sel; + } else if (are_strings_equal(field_name, count_str)) { + selection_add_index(&sel, 1); + sel.entity = entity__string_count; + return sel; + } + } break; } return sel; + } else if (type->kind == Type_Array) { + String count_str = make_string("count"); + // NOTE(bill):U nderlying memory address cannot be changed + if (are_strings_equal(field_name, count_str)) { + Token token = {Token_Identifier}; + token.string = count_str; + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, token, t_int, make_exact_value_integer(type->Array.count)); + return sel; + } + } else if (type->kind == Type_Vector) { + String count_str = make_string("count"); + // NOTE(bill): Vectors are not addressable + if (are_strings_equal(field_name, count_str)) { + Token token = {Token_Identifier}; + token.string = count_str; + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, token, t_int, make_exact_value_integer(type->Vector.count)); + return sel; + } + } else if (type->kind == Type_Slice) { + String data_str = make_string("data"); + String count_str = make_string("count"); + String capacity_str = make_string("capacity"); + + if (are_strings_equal(field_name, data_str)) { + selection_add_index(&sel, 0); + Token token = {Token_Identifier}; + token.string = data_str; + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, token, make_type_pointer(a, type->Slice.elem), false, 0); + return sel; + } else if (are_strings_equal(field_name, count_str)) { + selection_add_index(&sel, 1); + Token token = {Token_Identifier}; + token.string = count_str; + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, token, t_int, false, 1); + return sel; + } else if (are_strings_equal(field_name, capacity_str)) { + selection_add_index(&sel, 2); + Token token = {Token_Identifier}; + token.string = capacity_str; + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, token, t_int, false, 2); + return sel; + } } if (type->kind != Type_Record) { diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 4df5daccf..888df4a1a 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -330,6 +330,13 @@ void ssa_gen_tree(ssaGen *s) { case TypeRecord_Struct: { tag = ssa_add_local_generated(proc, t_type_info_struct); + { + ssaValue *packed = ssa_make_const_bool(a, t->Record.struct_is_packed); + ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_one32, t_bool_ptr), packed); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_two32, t_bool_ptr), ordered); + } + ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet @@ -456,9 +463,12 @@ void ssa_gen_tree(ssaGen *s) { ssaValue *results = ssa_emit_struct_gep(proc, tag, v_one32, t_type_info_ptr_ptr); ssaValue *variadic = ssa_emit_struct_gep(proc, tag, v_two32, t_bool_ptr); - - ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params)); - ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results)); + if (t->Proc.params) { + ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params)); + } + if (t->Proc.results) { + ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results)); + } ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic)); // TODO(bill): Type_Info for procedures diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 0bff5b564..7ec3b369d 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -121,7 +121,7 @@ void ssa_print_encoded_local(ssaFileBuffer *f, String name) { void ssa_print_encoded_global(ssaFileBuffer *f, String name, b32 global_scope = false) { ssa_fprintf(f, "@"); - if (!global_scope) { + if (!global_scope && !are_strings_equal(name, make_string("main"))) { ssa_fprintf(f, "."); } ssa_print_escape_string(f, name, true); diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index ee4b33119..f1e992d7b 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1116,10 +1116,16 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, type)); } break; + case Basic_string: + e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, sel.entity->type)); + break; + default: GB_PANIC("un-gep-able type"); break; } + } else if (type->kind == Type_Slice) { + e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, sel.entity->type)); } else { GB_PANIC("un-gep-able type"); } @@ -1159,10 +1165,16 @@ ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Se e = ssa_emit_struct_ev(proc, e, index, type); } break; + case Basic_string: + e = ssa_emit_struct_ev(proc, e, index, sel.entity->type); + break; + default: GB_PANIC("un-ev-able type"); break; } + } else if (type->kind == Type_Slice) { + e = ssa_emit_struct_gep(proc, e, index, make_type_pointer(proc->module->allocator, sel.entity->type)); } else { GB_PANIC("un-ev-able type"); } @@ -1194,6 +1206,9 @@ isize ssa_type_info_index(CheckerInfo *info, Type *type) { } } } + if (entry_index < 0) { + gb_printf_err("%s\n", type_to_string(type)); + } GB_ASSERT(entry_index >= 0); return entry_index; } @@ -1599,7 +1614,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg ssaValue *result = ssa_add_local_generated(proc, t_any); ssaValue *data = NULL; - if (false && value->kind == ssaValue_Instr && + if (value->kind == ssaValue_Instr && value->Instr.kind == ssaInstr_Load) { // NOTE(bill): Addressable value data = value->Instr.Load.address; @@ -1704,37 +1719,16 @@ void ssa_array_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ss if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { return; } - ssa_emit_comment(proc, make_string("ArrayBoundsCheck")); - index = ssa_emit_conv(proc, index, t_int); - len = ssa_emit_conv(proc, len, t_int); - - Token le = {Token_LtEq}; - Token lt = {Token_Lt}; - Token cmp_and = {Token_And}; // NOTE(bill): Doesn't need to be logical - ssaValue *c0 = ssa_emit_comp(proc, le, v_zero, index); - ssaValue *c1 = ssa_emit_comp(proc, lt, index, len); - ssaValue *cond = ssa_emit_comp(proc, cmp_and, c0, c1); - - ssaBlock *then = ssa_add_block(proc, NULL, make_string("abc.then")); - ssaBlock *done = ssa__make_block(proc, NULL, make_string("abc.done")); - - ssa_emit_if(proc, cond, done, then); - proc->curr_block = then; - gbAllocator a = proc->module->allocator; ssaValue **args = gb_alloc_array(a, ssaValue *, 5); args[0] = ssa_emit_global_string(proc, token.pos.file); args[1] = ssa_make_const_int(a, token.pos.line); args[2] = ssa_make_const_int(a, token.pos.column); - args[3] = index; - args[4] = len; + args[3] = ssa_emit_conv(proc, index, t_int); + args[4] = ssa_emit_conv(proc, len, t_int); ssa_emit_global_call(proc, "__bounds_check_error", args, 5); - - ssa_emit_jump(proc, done); - gb_array_append(proc->blocks, done); - proc->curr_block = done; } void ssa_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) { @@ -1742,43 +1736,20 @@ void ssa_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaV return; } - low = ssa_emit_conv(proc, low, t_int); - high = ssa_emit_conv(proc, high, t_int); - max = ssa_emit_conv(proc, max, t_int); - - Token le = {Token_LtEq}; - Token cmp_and = {Token_And}; // NOTE(bill): Doesn't need to be logical - ssaValue *c0 = ssa_emit_comp(proc, le, v_zero, low); - ssaValue *c1 = ssa_emit_comp(proc, le, low, high); - ssaValue *c2 = ssa_emit_comp(proc, le, high, max); - ssaValue *cond = NULL; - cond = ssa_emit_comp(proc, cmp_and, c0, c1); - cond = ssa_emit_comp(proc, cmp_and, cond, c2); - - ssaBlock *then = ssa_add_block(proc, NULL, make_string("seb.then")); - ssaBlock *done = ssa__make_block(proc, NULL, make_string("seb.done")); - - ssa_emit_if(proc, cond, done, then); - proc->curr_block = then; - gbAllocator a = proc->module->allocator; ssaValue **args = gb_alloc_array(a, ssaValue *, 6); args[0] = ssa_emit_global_string(proc, token.pos.file); args[1] = ssa_make_const_int(a, token.pos.line); args[2] = ssa_make_const_int(a, token.pos.column); - args[3] = low; - args[4] = high; - args[5] = max; + args[3] = ssa_emit_conv(proc, low, t_int); + args[4] = ssa_emit_conv(proc, high, t_int); + args[5] = ssa_emit_conv(proc, max, t_int); if (!is_substring) { ssa_emit_global_call(proc, "__slice_expr_error", args, 6); } else { ssa_emit_global_call(proc, "__substring_expr_error", args, 5); } - - ssa_emit_jump(proc, done); - gb_array_append(proc->blocks, done); - proc->curr_block = done; } @@ -2109,26 +2080,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_emit_load(proc, slice); } break; - case BuiltinProc_delete: { - ssa_emit_comment(proc, make_string("delete")); - // delete :: proc(ptr: ^Type) - // delete :: proc(slice: []Type) - gbAllocator allocator = proc->module->allocator; - - ssaValue *value = ssa_build_expr(proc, ce->args[0]); - - if (is_type_slice(ssa_type(value))) { - Type *etp = get_base_type(ssa_type(value)); - etp = make_type_pointer(allocator, etp->Slice.elem); - value = ssa_emit(proc, ssa_make_instr_extract_value(proc, value, 0, etp)); - } - - ssaValue **args = gb_alloc_array(allocator, ssaValue *, 1); - args[0] = ssa_emit_conv(proc, value, t_rawptr, true); - return ssa_emit_global_call(proc, "dealloc", args, 1); - } break; - - case BuiltinProc_assert: { ssa_emit_comment(proc, make_string("assert")); ssaValue *cond = ssa_build_expr(proc, ce->args[0]); @@ -2174,25 +2125,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return NULL; } break; - case BuiltinProc_len: { - ssa_emit_comment(proc, make_string("len")); - // len :: proc(v: Type) -> int - // NOTE(bill): len of an array is a constant expression - ssaValue *v = ssa_build_expr(proc, ce->args[0]); - Type *t = get_base_type(ssa_type(v)); - if (t == t_string) - return ssa_string_len(proc, v); - else if (t->kind == Type_Slice) - return ssa_slice_len(proc, v); - } break; - case BuiltinProc_cap: { - ssa_emit_comment(proc, make_string("cap")); - // cap :: proc(v: Type) -> int - // NOTE(bill): cap of an array is a constant expression - ssaValue *v = ssa_build_expr(proc, ce->args[0]); - Type *t = get_base_type(ssa_type(v)); - return ssa_slice_cap(proc, v); - } break; case BuiltinProc_copy: { ssa_emit_comment(proc, make_string("copy")); // copy :: proc(dst, src: []Type) -> int @@ -3393,9 +3325,15 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_array_append(proc->blocks, body); } proc->curr_block = body; + + // TODO(bill): Handle fallthrough scope exit correctly + proc->scope_index++; ssa_push_target_list(proc, done, NULL, fall); ssa_build_stmt_list(proc, cc->stmts); + ssa_emit_defer_stmts(proc, ssaDefer_Default, body); ssa_pop_target_list(proc); + proc->scope_index--; + ssa_emit_jump(proc, done); proc->curr_block = next_cond; } @@ -3404,9 +3342,14 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssa_emit_jump(proc, default_block); gb_array_append(proc->blocks, default_block); proc->curr_block = default_block; + + // TODO(bill): Handle fallthrough scope exit correctly + proc->scope_index++; ssa_push_target_list(proc, done, NULL, default_fall); ssa_build_stmt_list(proc, default_stmts); + ssa_emit_defer_stmts(proc, ssaDefer_Default, default_block); ssa_pop_target_list(proc); + proc->scope_index--; } ssa_emit_jump(proc, done); @@ -3490,9 +3433,14 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_array_append(proc->blocks, body); proc->curr_block = body; + + proc->scope_index++; ssa_push_target_list(proc, done, NULL, NULL); ssa_build_stmt_list(proc, cc->stmts); + ssa_emit_defer_stmts(proc, ssaDefer_Default, body); ssa_pop_target_list(proc); + proc->scope_index--; + ssa_emit_jump(proc, done); proc->curr_block = next_cond; } @@ -3501,9 +3449,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { ssa_emit_jump(proc, default_block); gb_array_append(proc->blocks, default_block); proc->curr_block = default_block; + + proc->scope_index++; ssa_push_target_list(proc, done, NULL, NULL); ssa_build_stmt_list(proc, default_stmts); + ssa_emit_defer_stmts(proc, ssaDefer_Default, default_block); ssa_pop_target_list(proc); + proc->scope_index--; } ssa_emit_jump(proc, done); @@ -3513,24 +3465,18 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_ast_node(bs, BranchStmt, node); ssaBlock *block = NULL; + #define branch_case(x) case GB_JOIN2(Token_, x): \ + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { \ + block = GB_JOIN3(t->, x, _); \ + } break switch (bs->token.kind) { - case Token_break: { - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->break_; - } - } break; - case Token_continue: { - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->continue_; - } - } break; - case Token_fallthrough: { - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->fallthrough_; - } - } break; + branch_case(break); + branch_case(continue); + branch_case(fallthrough); } - if (block != NULL && bs->token.kind != Token_fallthrough) { + // TODO(bill): Handle fallthrough scope exit correctly + // if (block != NULL && bs->token.kind != Token_fallthrough) { + if (block != NULL) { ssa_emit_defer_stmts(proc, ssaDefer_Branch, block); } switch (bs->token.kind) { diff --git a/src/parser.cpp b/src/parser.cpp index 356d51da3..f6db8267e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -64,6 +64,7 @@ enum ProcTag { ProcTag_foreign = GB_BIT(2), ProcTag_inline = GB_BIT(3), ProcTag_no_inline = GB_BIT(4), + ProcTag_no_context = GB_BIT(5), }; enum VarDeclTag { @@ -1173,14 +1174,16 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) { next_token(f); } - } else if (are_strings_equal(tag_name, make_string("inline"))) { - check_proc_add_tag(f, tag_expr, tags, ProcTag_inline, tag_name); - } else if (are_strings_equal(tag_name, make_string("no_inline"))) { - check_proc_add_tag(f, tag_expr, tags, ProcTag_no_inline, tag_name); } else if (are_strings_equal(tag_name, make_string("bounds_check"))) { check_proc_add_tag(f, tag_expr, tags, ProcTag_bounds_check, tag_name); } else if (are_strings_equal(tag_name, make_string("no_bounds_check"))) { check_proc_add_tag(f, tag_expr, tags, ProcTag_no_bounds_check, tag_name); + } else if (are_strings_equal(tag_name, make_string("inline"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_inline, tag_name); + } else if (are_strings_equal(tag_name, make_string("no_inline"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_no_inline, tag_name); + } else if (are_strings_equal(tag_name, make_string("no_context"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_no_context, tag_name); } else { ast_file_err(f, ast_node_token(tag_expr), "Unknown procedure tag"); } @@ -1490,17 +1493,16 @@ AstNode *parse_binary_expr(AstFile *f, b32 lhs, i32 prec_in) { } switch (op.kind) { - // case Token_DoublePrime: { - // AstNode *proc = parse_identifier(f); - // AstNode *right = parse_binary_expr(f, false, prec+1); - // expression->next = right; - // gbArray(AstNode *) args; - // gb_array_init_reserve(args, gb_arena_allocator(&f->arena), 2); - // gb_array_append(args, expression); - // gb_array_append(args, right); - // expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token); - // continue; - // } break; + case Token_DoublePrime: { + AstNode *proc = parse_identifier(f); + AstNode *right = parse_binary_expr(f, false, prec+1); + gbArray(AstNode *) args; + gb_array_init_reserve(args, gb_arena_allocator(&f->arena), 2); + gb_array_append(args, expression); + gb_array_append(args, right); + expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token); + continue; + } break; case Token_as: case Token_transmute: @@ -2074,6 +2076,18 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) { AstNodeArray values = NULL; AstNode *type = NULL; + gb_for_array(i, names) { + AstNode *name = names[i]; + if (name->kind == AstNode_Ident) { + String n = name->Ident.string; + // NOTE(bill): Check for reserved identifiers + if (are_strings_equal(n, make_string("context"))) { + ast_file_err(f, ast_node_token(name), "`context` is a reserved identifier"); + break; + } + } + } + if (allow_token(f, Token_Colon)) { if (!allow_token(f, Token_type)) { type = parse_identifier_or_type(f); @@ -2449,6 +2463,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_Rune: case Token_String: case Token_OpenParen: + case Token_proc: // Unary Operators case Token_Add: case Token_Sub: