From b1562edccf9ea972ec8caf5faebea07cf27559bb Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 5 Feb 2017 18:17:55 +0000 Subject: [PATCH] Add `types.odin`; Begin work on `map` --- code/demo.odin | 6 +- core/_preload.odin | 12 ++++ core/hash.odin | 55 ++++++++++++++++- core/types.odin | 146 +++++++++++++++++++++++++++++++++++++++++++++ src/build.c | 3 + src/check_expr.c | 93 +++++++++++++++++++++++++---- src/checker.c | 8 +++ src/ir.c | 8 +++ src/ir_print.c | 12 ++++ src/types.c | 139 +++++++++++++++++++++++++++++++++--------- 10 files changed, 442 insertions(+), 40 deletions(-) create mode 100644 core/types.odin diff --git a/code/demo.odin b/code/demo.odin index beb56b950..edcdaa49f 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -11,7 +11,11 @@ main :: proc() { - T0 :: struct #align 8 {}; + Value :: type f32; + m0: map[int]Value; + m1: map[string]Value; + m2: map[f32]Value; + // fm: map[128, int]f32; /* { diff --git a/core/_preload.odin b/core/_preload.odin index 2bb00c7f6..093c5fcc4 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -4,6 +4,7 @@ #import "fmt.odin"; #import "mem.odin"; #import "utf8.odin"; +#import "hash.odin"; // IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a // #shared_global_scope due to the internals of the compiler. @@ -346,6 +347,16 @@ Raw_Dynamic_Array :: struct #ordered { allocator: Allocator, }; +Raw_Dynamic_Map :: struct #ordered { + hashes: [dynamic]int, + entries: Raw_Dynamic_Array, +}; + +__default_hash :: proc(data: rawptr, len: int) -> u64 { + return hash.murmur64(data, len); +} + + __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool { array := cast(^Raw_Dynamic_Array)array_; @@ -398,3 +409,4 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, array.count += item_count; return array.count; } + diff --git a/core/hash.odin b/core/hash.odin index b2d6f0226..66b6c1291 100644 --- a/core/hash.odin +++ b/core/hash.odin @@ -50,13 +50,66 @@ fnv32a :: proc(data: rawptr, len: int) -> u32 { fnv64a :: proc(data: rawptr, len: int) -> u64 { s := slice_ptr(cast(^u8)data, len); - h :u64 = 0xcbf29ce484222325; + h: u64 = 0xcbf29ce484222325; for i in 0.. u32 { + compile_assert(ODIN_ENDIAN == "little"); + + SEED :: 0x9747b28c; + + key := cast(^u8)data; + h: u32 = SEED; + + if len > 3 { + key_x4 := cast(^u32)key; + i := len>>2; + for { + k := key_x4^; key_x4 += 1; + k *= 0xcc9e2d51; + k = (k << 15) | (k >> 17); + k *= 0x1b873593; + h ~= k; + h = (h << 13) | (h >> 19); + h += (h << 2) + 0xe6546b64; + i -= 1; + if i == 0 { + break; + } + } + key = cast(^u8)key_x4; + } + if len&3 != 0 { + i := len&3; + k: u32 = 0; + key += i-1; + for { + k <<= 8; + k |= cast(u32)key^; + key -= 1; + i -= 1; + if i == 0 { + break; + } + } + k *= 0xcc9e2d51; + k = (k << 15) | (k >> 17); + k *= 0x1b873593; + h ~= k; + } + + h ~= cast(u32)len; + h ~= h >> 16; + h *= 0x85ebca6b; + h ~= h >> 13; + h *= 0xc2b2ae35; + h ~= h >> 16; + return h; +} murmur64 :: proc(data_: rawptr, len: int) -> u64 { SEED :: 0x9747b28c; diff --git a/core/types.odin b/core/types.odin new file mode 100644 index 000000000..5464346d1 --- /dev/null +++ b/core/types.odin @@ -0,0 +1,146 @@ +is_signed :: proc(info: ^Type_Info) -> bool { + if is_integer(info) { + i := cast(^Type_Info.Integer)info; + return i.signed; + } + if is_float(info) { + return true; + } + return false; +} +is_integer :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Integer: return true; + } + return false; +} +is_float :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Float: return true; + } + return false; +} +is_any :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Any: return true; + } + return false; +} +is_string :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.String: return true; + } + return false; +} +is_boolean :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Boolean: return true; + } + return false; +} +is_pointer :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Pointer: return true; + } + return false; +} +is_maybe :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Maybe: return true; + } + return false; +} +is_procedure :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Procedure: return true; + } + return false; +} +is_array :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Array: return true; + } + return false; +} +is_dynamic_array :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Dynamic_Array: return true; + } + return false; +} +is_slice :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Slice: return true; + } + return false; +} +is_vector :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Vector: return true; + } + return false; +} +is_tuple :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Tuple: return true; + } + return false; +} +is_struct :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Struct: return true; + } + return false; +} +is_union :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Union: return true; + } + return false; +} +is_raw_union :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Raw_Union: return true; + } + return false; +} +is_enum :: proc(info: ^Type_Info) -> bool { + if info == nil { return false; } + + match type i in type_info_base(info) { + case Type_Info.Enum: return true; + } + return false; +} diff --git a/src/build.c b/src/build.c index d90bcb5c3..3afc05047 100644 --- a/src/build.c +++ b/src/build.c @@ -1,6 +1,7 @@ typedef struct BuildContext { String ODIN_OS; // target operating system String ODIN_ARCH; // target architecture + String ODIN_ENDIAN; // target endian String ODIN_VENDOR; // compiler vendor String ODIN_VERSION; // compiler version String ODIN_ROOT; // Odin ROOT @@ -243,9 +244,11 @@ void init_build_context(BuildContext *bc) { #if defined(GB_SYSTEM_WINDOWS) bc->ODIN_OS = str_lit("windows"); bc->ODIN_ARCH = str_lit("amd64"); + bc->ODIN_ENDIAN = str_lit("little"); #elif defined(GB_SYSTEM_OSX) bc->ODIN_OS = str_lit("osx"); bc->ODIN_ARCH = str_lit("amd64"); + bc->ODIN_ENDIAN = str_lit("little"); #else #error Implement system #endif diff --git a/src/check_expr.c b/src/check_expr.c index c001a903f..15b5c55a7 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -910,7 +910,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { } -void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint) { +void check_ident (Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint) { GB_ASSERT(n->kind == AstNode_Ident); o->mode = Addressing_Invalid; o->expr = n; @@ -1051,7 +1051,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Type o->type = type; } -i64 check_array_count(Checker *c, AstNode *e) { +i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { if (e == NULL) { return 0; } @@ -1059,7 +1059,11 @@ i64 check_array_count(Checker *c, AstNode *e) { check_expr(c, &o, e); if (o.mode != Addressing_Constant) { if (o.mode != Addressing_Invalid) { - error_node(e, "Array count must be a constant"); + if (is_map) { + error_node(e, "Fixed map count must be a constant"); + } else { + error_node(e, "Array count must be a constant"); + } } return 0; } @@ -1067,18 +1071,59 @@ i64 check_array_count(Checker *c, AstNode *e) { if (is_type_untyped(type) || is_type_integer(type)) { if (o.value.kind == ExactValue_Integer) { i64 count = o.value.value_integer; - if (count >= 0) { - return count; + if (is_map) { + if (count > 0) { + return count; + } + error_node(e, "Invalid fixed map count"); + } else { + if (count >= 0) { + return count; + } + error_node(e, "Invalid array count"); } - error_node(e, "Invalid array count"); return 0; } } - error_node(e, "Array count must be an integer"); + if (is_map) { + error_node(e, "Fixed map count must be an integer"); + } else { + error_node(e, "Array count must be an integer"); + } return 0; } +void check_map_type(Checker *c, Type *type, AstNode *node) { + GB_ASSERT(type->kind == Type_Map); + ast_node(mt, MapType, node); + + i64 count = check_array_or_map_count(c, mt->count, true); + Type *key = check_type_extra(c, mt->key, NULL); + Type *value = check_type_extra(c, mt->value, NULL); + + if (!is_type_valid_for_keys(key)) { + if (is_type_boolean(key)) { + error_node(node, "A boolean cannot be used as a key for a map"); + } else { + gbString str = type_to_string(key); + error_node(node, "Invalid type of a key for a map, got `%s`", str); + gb_string_free(str); + } + } + + if (count > 0) { + count = 0; + error_node(node, "Fixed map types are not yet implemented"); + } + + type->Map.count = count; + type->Map.key = key; + type->Map.value = value; + + // error_node(node, "`map` types are not yet implemented"); +} + Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { ExactValue null_value = {ExactValue_Invalid}; Type *type = NULL; @@ -1092,7 +1137,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { switch (e->kind) { case_ast_node(i, Ident, e); Operand o = {0}; - check_identifier(c, &o, e, named_type, NULL); + check_ident(c, &o, e, named_type, NULL); switch (o.mode) { case Addressing_Invalid: @@ -1169,7 +1214,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { case_ast_node(at, ArrayType, e); if (at->count != NULL) { Type *elem = check_type_extra(c, at->elem, NULL); - type = make_type_array(c->allocator, elem, check_array_count(c, at->count)); + type = make_type_array(c->allocator, elem, check_array_or_map_count(c, at->count, false)); } else { Type *elem = check_type(c, at->elem); type = make_type_slice(c->allocator, elem); @@ -1188,7 +1233,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { case_ast_node(vt, VectorType, e); Type *elem = check_type(c, vt->elem); Type *be = base_type(elem); - i64 count = check_array_count(c, vt->count); + i64 count = check_array_or_map_count(c, vt->count, false); if (is_type_vector(be) || (!is_type_boolean(be) && !is_type_numeric(be))) { err_str = type_to_string(elem); error_node(vt->elem, "Vector element type must be numerical or a boolean, got `%s`", err_str); @@ -1246,6 +1291,12 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { goto end; case_end; + case_ast_node(mt, MapType, e); + type = alloc_type(c->allocator, Type_Map); + set_base_type(named_type, type); + check_map_type(c, type, e); + goto end; + case_end; case_ast_node(ce, CallExpr, e); Operand o = {0}; @@ -2750,6 +2801,26 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_NoValue; } break; + case BuiltinProc_clear: { + 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; + } + + operand->type = NULL; + operand->mode = Addressing_NoValue; + } break; + case BuiltinProc_append: { // append :: proc(^[dynamic]Type, item: ...Type) { Type *type = operand->type; @@ -4038,7 +4109,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_end; case_ast_node(i, Ident, node); - check_identifier(c, o, node, NULL, type_hint); + check_ident(c, o, node, NULL, type_hint); case_end; case_ast_node(bl, BasicLit, node); diff --git a/src/checker.c b/src/checker.c index cd0a9ddef..938a09f40 100644 --- a/src/checker.c +++ b/src/checker.c @@ -28,6 +28,7 @@ typedef enum BuiltinProcId { BuiltinProc_free, BuiltinProc_reserve, + BuiltinProc_clear, BuiltinProc_append, BuiltinProc_size_of, @@ -69,6 +70,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("free"), 1, false, Expr_Stmt}, {STR_LIT("reserve"), 2, false, Expr_Stmt}, + {STR_LIT("clear"), 1, false, Expr_Stmt}, {STR_LIT("append"), 1, true, Expr_Expr}, {STR_LIT("size_of"), 1, false, Expr_Expr}, @@ -610,6 +612,7 @@ void init_universal_scope(BuildContext *bc) { // TODO(bill): Set through flags in the compiler add_global_string_constant(a, str_lit("ODIN_OS"), bc->ODIN_OS); add_global_string_constant(a, str_lit("ODIN_ARCH"), bc->ODIN_ARCH); + add_global_string_constant(a, str_lit("ODIN_ENDIAN"), bc->ODIN_ENDIAN); add_global_string_constant(a, str_lit("ODIN_VENDOR"), bc->ODIN_VENDOR); add_global_string_constant(a, str_lit("ODIN_VERSION"), bc->ODIN_VERSION); add_global_string_constant(a, str_lit("ODIN_ROOT"), bc->ODIN_ROOT); @@ -1141,6 +1144,11 @@ void init_preload(Checker *c) { t_context_ptr = make_type_pointer(c->allocator, t_context); } + if (t_raw_dynamic_array == NULL) { + Entity *e = find_core_entity(c, str_lit("Raw_Dynamic_Array")); + t_raw_dynamic_array = e->type; + } + c->done_preload = true; } diff --git a/src/ir.c b/src/ir.c index ab948ab96..c6df0f237 100644 --- a/src/ir.c +++ b/src/ir.c @@ -2987,6 +2987,14 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv return ir_emit_global_call(proc, "__dynamic_array_reserve", args, 4); } break; + case BuiltinProc_clear: { + ir_emit_comment(proc, str_lit("reserve")); + irValue *array_ptr = ir_build_expr(proc, ce->args.e[0]); + irValue *count_ptr = ir_emit_struct_ep(proc, array_ptr, 1); + ir_emit_store(proc, count_ptr, v_zero); + return NULL; + } break; + case BuiltinProc_append: { ir_emit_comment(proc, str_lit("append")); gbAllocator a = proc->module->allocator; diff --git a/src/ir_print.c b/src/ir_print.c index a2e5b147d..f3f9510c8 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -292,6 +292,18 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { } ir_fprintf(f, ")*"); } return; + + case Type_Map: { + if (t->Map.count > 0) { + // ir_fprintf(f, "void"); + } else { + ir_fprintf(f, "{"); + ir_print_type(f, m, t_raw_dynamic_array); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_raw_dynamic_array); + ir_fprintf(f, "}"); + } + } break; } } diff --git a/src/types.c b/src/types.c index 5443f85f1..bd3e5133d 100644 --- a/src/types.c +++ b/src/types.c @@ -95,35 +95,40 @@ typedef struct TypeRecord { Entity * enum_max_value; } TypeRecord; -#define TYPE_KINDS \ - TYPE_KIND(Basic, BasicType) \ - TYPE_KIND(Pointer, struct { Type *elem; }) \ +#define TYPE_KINDS \ + 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(DynamicArray, struct { Type *elem; }) \ TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \ - TYPE_KIND(Slice, struct { Type *elem; }) \ - TYPE_KIND(Maybe, struct { Type *elem; }) \ - TYPE_KIND(Record, TypeRecord) \ - TYPE_KIND(Named, struct { \ - String name; \ - Type * base; \ - Entity *type_name; /* Entity_TypeName */ \ - }) \ - TYPE_KIND(Tuple, struct { \ - Entity **variables; /* Entity_Variable */ \ - i32 variable_count; \ - bool are_offsets_set; \ - i64 * offsets; \ - }) \ - TYPE_KIND(Proc, struct { \ - Scope *scope; \ - Type * params; /* Type_Tuple */ \ - Type * results; /* Type_Tuple */ \ - i32 param_count; \ - i32 result_count; \ - bool variadic; \ - ProcCallingConvention calling_convention; \ - }) + TYPE_KIND(Slice, struct { Type *elem; }) \ + TYPE_KIND(Maybe, struct { Type *elem; }) \ + TYPE_KIND(Record, TypeRecord) \ + TYPE_KIND(Named, struct { \ + String name; \ + Type * base; \ + Entity *type_name; /* Entity_TypeName */ \ + }) \ + TYPE_KIND(Tuple, struct { \ + Entity **variables; /* Entity_Variable */ \ + i32 variable_count; \ + bool are_offsets_set; \ + i64 * offsets; \ + }) \ + TYPE_KIND(Proc, struct { \ + Scope *scope; \ + Type * params; /* Type_Tuple */ \ + Type * results; /* Type_Tuple */ \ + i32 param_count; \ + i32 result_count; \ + bool variadic; \ + ProcCallingConvention calling_convention; \ + }) \ + TYPE_KIND(Map, struct { \ + i64 count; /* 0 if dynamic */ \ + Type *key; \ + Type *value; \ + }) \ @@ -319,6 +324,7 @@ gb_global Type *t_allocator_ptr = NULL; gb_global Type *t_context = NULL; gb_global Type *t_context_ptr = NULL; +gb_global Type *t_raw_dynamic_array = NULL; @@ -477,6 +483,23 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun return t; } +bool is_type_valid_for_keys(Type *t); + +Type *make_type_map(gbAllocator a, i64 count, Type *key, Type *value) { + Type *t = alloc_type(a, Type_Map); + if (key != NULL) { + GB_ASSERT(is_type_valid_for_keys(key)); + } + t->Map.count = count; + t->Map.key = key; + t->Map.value = value; + return t; +} + + + + + Type *type_deref(Type *t) { if (t != NULL) { @@ -679,6 +702,21 @@ bool is_type_enum(Type *t) { return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum); } +bool is_type_map(Type *t) { + t = base_type(t); + return t->kind == Type_Map; +} + +bool is_type_fixed_map(Type *t) { + t = base_type(t); + return t->kind == Type_Map && t->Map.count > 0; +} +bool is_type_dynamic_map(Type *t) { + t = base_type(t); return t->kind == Type_Map && t->Map.count == 0; +} + + + bool is_type_any(Type *t) { t = base_type(t); @@ -691,6 +729,28 @@ bool is_type_untyped_nil(Type *t) { +bool is_type_valid_for_keys(Type *t) { + t = base_type(base_enum_type(t)); + if (is_type_untyped(t)) { + return false; + } + if (is_type_integer(t)) { + return true; + } + if (is_type_float(t)) { + return true; + } + if (is_type_string(t)) { + return true; + } + if (is_type_pointer(t)) { + return true; + } + + return false; +} + + bool is_type_indexable(Type *t) { return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t); } @@ -1458,6 +1518,14 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type return align; } + case Type_Map: { + if (t->Map.count == 0) { // Dynamic + // NOTE(bill): same as a dynamic array + return s.word_size; + } + GB_PANIC("TODO(bill): Fixed map alignment"); + } break; + case Type_Record: { switch (t->Record.kind) { case TypeRecord_Struct: @@ -1667,6 +1735,14 @@ i64 type_size_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypeP return align_formula(size, align); } + case Type_Map: { + if (t->Map.count == 0) { // Dynamic + // NOTE(bill): same as a two dynamic arrays + return 2 * type_size_of_internal(s, allocator, t_raw_dynamic_array, path); + } + GB_PANIC("TODO(bill): Fixed map size"); + } + case Type_Tuple: { i64 count, align, size; count = t->Tuple.variable_count; @@ -1924,6 +2000,15 @@ gbString write_type_to_string(gbString str, Type *type) { } } break; + case Type_Map: { + str = gb_string_appendc(str, "map["); + if (type->Map.count > 0) { + str = gb_string_appendc(str, gb_bprintf("%lld, ", type->Map.count)); + } + str = write_type_to_string(str, type->Map.key); + str = gb_string_appendc(str, "]"); + str = write_type_to_string(str, type->Map.value); + } break; case Type_Named: if (type->Named.type_name != NULL) {