From 5406acc8fa9aded68e47b25c3464cb7eee713c8c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 26 Jan 2024 12:47:39 +0000 Subject: Fix `type_ptr_set_update` --- src/types.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/types.cpp') diff --git a/src/types.cpp b/src/types.cpp index 574e628c5..2f39d5caa 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -825,11 +825,13 @@ gb_internal void type_path_pop(TypePath *tp) { #define FAILURE_SIZE 0 #define FAILURE_ALIGNMENT 0 +gb_internal bool type_ptr_set_exists(PtrSet *s, Type *t); + gb_internal bool type_ptr_set_update(PtrSet *s, Type *t) { if (t == nullptr) { return true; } - if (ptr_set_exists(s, t)) { + if (type_ptr_set_exists(s, t)) { return true; } ptr_set_add(s, t); -- cgit v1.2.3 From 68df35b378d59f9813f5af81e61080c5f1b20e23 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 17:33:29 +0000 Subject: Add `#field_align(N)` It sets the minimum alignment for the fields within a struct. This cannot be used with `#packed`, but can be used with `#align(N)`. If `#align(N)` is less than `#field_align(N)`, then a warning will be printed. --- core/odin/ast/ast.odin | 1 + core/odin/parser/parser.odin | 7 +++++++ src/check_type.cpp | 40 +++++++++++++++++++++++++--------------- src/parser.cpp | 25 ++++++++++++++++++++----- src/parser.hpp | 1 + src/types.cpp | 14 ++++++++++---- 6 files changed, 64 insertions(+), 24 deletions(-) (limited to 'src/types.cpp') diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 67a26d6f2..f6bcbab4e 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -768,6 +768,7 @@ Struct_Type :: struct { tok_pos: tokenizer.Pos, poly_params: ^Field_List, align: ^Expr, + field_align: ^Expr, where_token: tokenizer.Token, where_clauses: []^Expr, is_packed: bool, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 3383f3514..fc7a2c792 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2547,6 +2547,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { poly_params: ^ast.Field_List align: ^ast.Expr + field_align: ^ast.Expr is_packed: bool is_raw_union: bool is_no_copy: bool @@ -2578,6 +2579,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) } align = parse_expr(p, true) + case "field_align": + if field_align != nil { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + field_align = parse_expr(p, true) case "raw_union": if is_raw_union { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) @@ -2620,6 +2626,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)) st.poly_params = poly_params st.align = align + st.field_align = field_align st.is_packed = is_packed st.is_raw_union = is_raw_union st.is_no_copy = is_no_copy diff --git a/src/check_type.cpp b/src/check_type.cpp index a95026711..5cb1eb9cc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -219,13 +219,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice 1) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &v); - error(node, "#align too large, %.*s", LIT(str)); + error(node, "#%s too large, %.*s", msg, LIT(str)); gb_free(a, str.text); return false; } i64 align = big_int_to_i64(&v); if (align < 1 || !gb_is_power_of_two(cast(isize)align)) { - error(node, "#align must be a power of 2, got %lld", align); + error(node, "#%s must be a power of 2, got %lld", msg, align); return false; } *align_ = align; @@ -251,7 +251,7 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) } } - error(node, "#align must be an integer"); + error(node, "#%s must be an integer", msg); return false; } @@ -645,16 +645,26 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); } - if (st->align != nullptr) { - if (st->is_packed) { - syntax_error(st->align, "'#align' cannot be applied with '#packed'"); - return; - } - i64 custom_align = 1; - if (check_custom_align(ctx, st->align, &custom_align)) { - struct_type->Struct.custom_align = custom_align; - } +#define ST_ALIGN(_name) if (st->_name != nullptr) { \ + if (st->is_packed) { \ + syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \ + return; \ + } \ + i64 align = 1; \ + if (check_custom_align(ctx, st->_name, &align, #_name)) { \ + struct_type->Struct.custom_##_name = align; \ + } \ } + + ST_ALIGN(field_align); + ST_ALIGN(align); + if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) { + warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)", + cast(long long)struct_type->Struct.custom_align, + cast(long long)struct_type->Struct.custom_field_align); + } + +#undef ST_ALIGN } gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_union(union_type)); @@ -746,7 +756,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no if (ut->align != nullptr) { i64 custom_align = 1; - if (check_custom_align(ctx, ut->align, &custom_align)) { + if (check_custom_align(ctx, ut->align, &custom_align, "align")) { if (variants.count == 0) { error(ut->align, "An empty union cannot have a custom alignment"); } else { diff --git a/src/parser.cpp b/src/parser.cpp index 2671054df..b16a88de5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -383,10 +383,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f); break; case Ast_StructType: - n->StructType.fields = clone_ast_array(n->StructType.fields, f); + n->StructType.fields = clone_ast_array(n->StructType.fields, f); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f); - n->StructType.align = clone_ast(n->StructType.align, f); - n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f); + n->StructType.align = clone_ast(n->StructType.align, f); + n->StructType.field_align = clone_ast(n->StructType.field_align, f); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants, f); @@ -1125,7 +1126,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, - Ast *align, + Ast *align, Ast *field_align, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; @@ -1136,6 +1137,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, i result->StructType.is_raw_union = is_raw_union; result->StructType.is_no_copy = is_no_copy; result->StructType.align = align; + result->StructType.field_align = field_align; result->StructType.where_token = where_token; result->StructType.where_clauses = slice_from_array(where_clauses); return result; @@ -2507,6 +2509,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_raw_union = false; bool no_copy = false; Ast *align = nullptr; + Ast *field_align = nullptr; if (allow_token(f, Token_OpenParen)) { isize param_count = 0; @@ -2543,6 +2546,18 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { error_line("\tSuggestion: #align(%s)", s); gb_string_free(s); } + } else if (tag.string == "field_align") { + if (field_align) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + field_align = parse_expr(f, true); + if (field_align && field_align->kind != Ast_ParenExpr) { + ERROR_BLOCK(); + gbString s = expr_to_string(field_align); + syntax_warning(tag, "#field_align requires parentheses around the expression"); + error_line("\tSuggestion: #field_align(%s)", s); + gb_string_free(s); + } } else if (tag.string == "raw_union") { if (is_raw_union) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); @@ -2591,7 +2606,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses); } break; case Token_union: { diff --git a/src/parser.hpp b/src/parser.hpp index cc1836ef3..1edb1f9dd 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -713,6 +713,7 @@ AST_KIND(_TypeBegin, "", bool) \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ + Ast *field_align; \ Token where_token; \ Slice where_clauses; \ bool is_packed; \ diff --git a/src/types.cpp b/src/types.cpp index 2f39d5caa..b99d469e4 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -137,6 +137,7 @@ struct TypeStruct { Scope * scope; i64 custom_align; + i64 custom_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; @@ -3668,10 +3669,15 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); } -gb_internal i64 *type_set_offsets_of(Slice const &fields, bool is_packed, bool is_raw_union) { +gb_internal i64 *type_set_offsets_of(Slice const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) { gbAllocator a = permanent_allocator(); auto offsets = gb_alloc_array(a, i64, fields.count); i64 curr_offset = 0; + + if (min_field_align == 0) { + min_field_align = 1; + } + if (is_raw_union) { for_array(i, fields) { offsets[i] = 0; @@ -3692,7 +3698,7 @@ gb_internal i64 *type_set_offsets_of(Slice const &fields, bool is_pack offsets[i] = -1; } else { Type *t = fields[i]->type; - i64 align = gb_max(type_align_of(t), 1); + i64 align = gb_max(type_align_of(t), min_field_align); i64 size = gb_max(type_size_of( t), 0); curr_offset = align_formula(curr_offset, align); offsets[i] = curr_offset; @@ -3709,7 +3715,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Struct.offset_mutex); if (!t->Struct.are_offsets_set) { t->Struct.are_offsets_being_processed = true; - t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union); + t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align); t->Struct.are_offsets_being_processed = false; t->Struct.are_offsets_set = true; return true; @@ -3718,7 +3724,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Tuple.mutex); if (!t->Tuple.are_offsets_set) { t->Tuple.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1); t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; -- cgit v1.2.3 From 59933b244ded0ab2476535b18875de95cd9f47bc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 8 Feb 2024 13:41:02 +0000 Subject: Allow polymorphic checking with `intrinsics.type_is_subtype_of(Derived_Type, Poly_Type)` --- src/check_builtin.cpp | 2 +- src/check_type.cpp | 14 ++++++++++++-- src/types.cpp | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1cb43ec1..4e374add6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5686,7 +5686,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->value = exact_value_bool(is_type_subtype_of_and_allow_polymorphic(op_src.type, op_dst.type)); operand->mode = Addressing_Constant; operand->type = t_untyped_bool; } break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 4d0901605..15bba5319 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,5 +1,6 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand); +gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array const *operands); gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -394,7 +395,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly bool *is_polymorphic_, Ast *node, Array *poly_operands) { Type *polymorphic_params_type = nullptr; - bool can_check_fields = true; GB_ASSERT(is_polymorphic_ != nullptr); if (polymorphic_params == nullptr) { @@ -404,6 +404,17 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly return polymorphic_params_type; } + + // bool is_variadic = false; + // isize variadic_index = 0; + // bool success = false; + // isize specialization_count = 0; + // polymorphic_params_type = check_get_params(ctx, ctx->scope, polymorphic_params, &is_variadic, &variadic_index, &success, &specialization_count, poly_operands); + // if (success) { + // return nullptr; + // } + + bool can_check_fields = true; ast_node(field_list, FieldList, polymorphic_params); Slice params = field_list->list; if (params.count != 0) { @@ -565,7 +576,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly if (!*is_polymorphic_) { *is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr; } - return polymorphic_params_type; } diff --git a/src/types.cpp b/src/types.cpp index b99d469e4..c4b03c967 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4093,7 +4093,7 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } -gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { +gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false, bool allow_polymorphic=false) { Type *prev_src = src; src = type_deref(src); if (!src_is_ptr) { @@ -4105,11 +4105,19 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return 0; } + bool dst_is_polymorphic = is_type_polymorphic(dst); + for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } + if (allow_polymorphic && dst_is_polymorphic) { + Type *fb = base_type(type_deref(f->type)); + if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) { + return true; + } + } if (are_types_identical(f->type, dst)) { return level+1; @@ -4119,7 +4127,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return level+1; } } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic); if (nested_level > 0) { return nested_level; } @@ -4135,6 +4143,13 @@ gb_internal bool is_type_subtype_of(Type *src, Type *dst) { return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); } +gb_internal bool is_type_subtype_of_and_allow_polymorphic(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src), true); +} gb_internal bool has_type_got_objc_class_attribute(Type *t) { -- cgit v1.2.3 From 5c4485f65767366c14dfd9a98945a5479ae0e449 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 9 Feb 2024 15:18:29 +0000 Subject: Add `#load_directory(path: string) > []runtime.Load_Directory_File` --- base/runtime/core.odin | 8 ++ src/check_builtin.cpp | 187 +++++++++++++++++++++++++++++++++------------- src/check_expr.cpp | 5 +- src/checker.cpp | 15 ++++ src/checker.hpp | 18 +++++ src/llvm_backend_proc.cpp | 67 +++++++++++++---- src/string.cpp | 12 +++ src/types.cpp | 4 + 8 files changed, 247 insertions(+), 69 deletions(-) (limited to 'src/types.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index fbdf33085..85e64242d 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -296,6 +296,14 @@ Source_Code_Location :: struct { procedure: string, } +/* + Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File` +*/ +Load_Directory_File :: struct { + name: string, + data: []byte, // immutable data +} + Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> ! // Allocation Stuff diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4e374add6..d39be37a9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1264,6 +1264,139 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand } +gb_internal int file_cache_sort_cmp(void const *x, void const *y) { + LoadFileCache const *a = *(LoadFileCache const **)(x); + LoadFileCache const *b = *(LoadFileCache const **)(y); + return string_compare(a->path, b->path); +} + +gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) { + ast_node(ce, CallExpr, call); + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "load_directory"); + + if (ce->args.count != 1) { + error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count); + return LoadDirective_Error; + } + + Ast *arg = ce->args[0]; + Operand o = {}; + check_expr(c, &o, arg); + if (o.mode != Addressing_Constant) { + error(arg, "'#%.*s' expected a constant string argument", LIT(name)); + return LoadDirective_Error; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str); + gb_string_free(str); + return LoadDirective_Error; + } + + GB_ASSERT(o.value.kind == ExactValue_String); + + init_core_load_directory_file(c->checker); + + operand->type = t_load_directory_file_slice; + operand->mode = Addressing_Value; + + + String original_string = o.value.value_string; + String path; + if (gb_path_is_absolute((char*)original_string.text)) { + path = original_string; + } else { + String base_dir = dir_from_path(get_file_path_string(call->file_id)); + + BlockingMutex *ignore_mutex = nullptr; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + gb_unused(ok); + } + MUTEX_GUARD(&c->info->load_directory_mutex); + + + gbFileError file_error = gbFileError_None; + + Array file_caches = {}; + + LoadDirectoryCache **cache_ptr = string_map_get(&c->info->load_directory_cache, path); + LoadDirectoryCache *cache = cache_ptr ? *cache_ptr : nullptr; + if (cache) { + file_error = cache->file_error; + } + defer ({ + if (cache == nullptr) { + LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache); + new_cache->path = path; + new_cache->files = file_caches; + new_cache->file_error = file_error; + string_map_set(&c->info->load_directory_cache, path, new_cache); + + map_set(&c->info->load_directory_map, call, new_cache); + } else { + cache->file_error = file_error; + } + }); + + + LoadDirectiveResult result = LoadDirective_Success; + + + if (cache == nullptr) { + Array list = {}; + ReadDirectoryError rd_err = read_directory(path, &list); + defer (array_free(&list)); + + if (list.count == 1) { + GB_ASSERT(path != list[0].fullpath); + } + + + switch (rd_err) { + case ReadDirectory_InvalidPath: + error(call, "%.*s error - invalid path: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_NotExists: + error(call, "%.*s error - path does not exist: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Permission: + error(call, "%.*s error - unknown error whilst reading path, %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_NotDir: + error(call, "%.*s error - expected a directory, got a file: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + case ReadDirectory_Empty: + error(call, "%.*s error - empty directory: %.*s", LIT(name), LIT(original_string)); + return LoadDirective_NotFound; + case ReadDirectory_Unknown: + error(call, "%.*s error - unknown error whilst reading path %.*s", LIT(name), LIT(original_string)); + return LoadDirective_Error; + } + + isize files_to_reserve = list.count+1; // always reserve 1 + + file_caches = array_make(heap_allocator(), 0, files_to_reserve); + + for (FileInfo fi : list) { + LoadFileCache *cache = nullptr; + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) { + array_add(&file_caches, cache); + } else { + result = LoadDirective_Error; + } + } + + gb_sort_array(file_caches.data, file_caches.count, file_cache_sort_cmp); + + } + + return result; +} + + gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); @@ -1291,6 +1424,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->mode = Addressing_Value; } else if (name == "load") { return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success; + } else if (name == "load_directory") { + return check_load_directory_directive(c, operand, call, type_hint, true) == LoadDirective_Success; } else if (name == "load_hash") { if (ce->args.count != 2) { if (ce->args.count == 0) { @@ -1408,58 +1543,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o return true; } return false; - } else if (name == "load_or") { - error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'"); - - if (ce->args.count != 2) { - if (ce->args.count == 0) { - error(ce->close, "'#load_or' expects 2 arguments, got 0"); - } else { - error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count); - } - return false; - } - - Ast *arg = ce->args[0]; - Operand o = {}; - check_expr(c, &o, arg); - if (o.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant string argument"); - return false; - } - - if (!is_type_string(o.type)) { - gbString str = type_to_string(o.type); - error(arg, "'#load_or' expected a constant string, got %s", str); - gb_string_free(str); - return false; - } - - Ast *default_arg = ce->args[1]; - Operand default_op = {}; - check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice); - if (default_op.mode != Addressing_Constant) { - error(arg, "'#load_or' expected a constant '[]byte' argument"); - return false; - } - - if (!are_types_identical(base_type(default_op.type), t_u8_slice)) { - gbString str = type_to_string(default_op.type); - error(arg, "'#load_or' expected a constant '[]byte', got %s", str); - gb_string_free(str); - return false; - } - GB_ASSERT(o.value.kind == ExactValue_String); - String original_string = o.value.value_string; - - operand->type = t_u8_slice; - operand->mode = Addressing_Constant; - LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, original_string, false, &cache)) { - operand->value = exact_value_string(cache->data); - } else { - operand->value = default_op.value; - } } else if (name == "assert") { if (ce->args.count != 1 && ce->args.count != 2) { error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9b71208cd..11eb4b533 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7107,8 +7107,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c name == "defined" || name == "config" || name == "load" || - name == "load_hash" || - name == "load_or" + name == "load_directory" || + name == "load_hash" ) { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -7958,6 +7958,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A name == "config" || name == "load" || name == "load_hash" || + name == "load_directory" || name == "load_or" ) { error(node, "'#%.*s' must be used as a call", LIT(name)); diff --git a/src/checker.cpp b/src/checker.cpp index 457ee6146..569a3c76f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1257,6 +1257,9 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used + + string_map_init(&i->load_directory_cache); + map_init(&i->load_directory_map); } gb_internal void destroy_checker_info(CheckerInfo *i) { @@ -1280,6 +1283,8 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); + string_map_destroy(&i->load_directory_cache); + map_destroy(&i->load_directory_map); } gb_internal CheckerContext make_checker_context(Checker *c) { @@ -2958,6 +2963,16 @@ gb_internal void init_core_source_code_location(Checker *c) { t_source_code_location_ptr = alloc_type_pointer(t_source_code_location); } +gb_internal void init_core_load_directory_file(Checker *c) { + if (t_load_directory_file != nullptr) { + return; + } + t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File")); + t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file); + t_load_directory_file_slice = alloc_type_slice(t_load_directory_file); +} + + gb_internal void init_core_map_type(Checker *c) { if (t_map_info != nullptr) { return; diff --git a/src/checker.hpp b/src/checker.hpp index 9da0f2950..9aee82257 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -340,6 +340,19 @@ struct LoadFileCache { StringMap hashes; }; + +struct LoadDirectoryFile { + String file_name; + String data; +}; + +struct LoadDirectoryCache { + String path; + gbFileError file_error; + Array files; +}; + + struct GenProcsData { Array procs; RwMutex mutex; @@ -416,6 +429,11 @@ struct CheckerInfo { BlockingMutex instrumentation_mutex; Entity *instrumentation_enter_entity; Entity *instrumentation_exit_entity; + + + BlockingMutex load_directory_mutex; + StringMap load_directory_cache; + PtrMap load_directory_map; // Key: Ast_CallExpr * }; struct CheckerContext { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e0aca2c10..9419f9a3c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1693,24 +1693,61 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); String name = bd->name.string; - GB_ASSERT(name == "location"); - String procedure = p->entity->token.string; - TokenPos pos = ast_token(ce->proc).pos; - if (ce->args.count > 0) { - Ast *ident = unselector_expr(ce->args[0]); - GB_ASSERT(ident->kind == Ast_Ident); - Entity *e = entity_of_node(ident); - GB_ASSERT(e != nullptr); - - if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { - procedure = e->parent_proc_decl->entity->token.string; - } else { - procedure = str_lit(""); + if (name == "location") { + String procedure = p->entity->token.string; + TokenPos pos = ast_token(ce->proc).pos; + if (ce->args.count > 0) { + Ast *ident = unselector_expr(ce->args[0]); + GB_ASSERT(ident->kind == Ast_Ident); + Entity *e = entity_of_node(ident); + GB_ASSERT(e != nullptr); + + if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { + procedure = e->parent_proc_decl->entity->token.string; + } else { + procedure = str_lit(""); + } + pos = e->token.pos; + } - pos = e->token.pos; + return lb_emit_source_code_location_as_global(p, procedure, pos); + } else if (name == "load_directory") { + lbModule *m = p->module; + TEMPORARY_ALLOCATOR_GUARD(); + LoadDirectoryCache *cache = map_must_get(&m->info->load_directory_map, expr); + isize count = cache->files.count; + + LLVMValueRef *elements = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for_array(i, cache->files) { + LoadFileCache *file = cache->files[i]; + String file_name = filename_without_directory(file->path); + + LLVMValueRef values[2] = {}; + values[0] = lb_const_string(m, file_name).value; + values[1] = lb_const_string(m, file->data).value; + LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values)); + elements[i] = element; + } + + LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count); + + Type *array_type = alloc_type_array(t_load_directory_file, count); + lbAddr backing_array_addr = lb_add_global_generated(m, array_type, {backing_array, array_type}, nullptr); + lb_make_global_private_const(backing_array_addr); + + LLVMValueRef backing_array_ptr = backing_array_addr.addr.value; + backing_array_ptr = LLVMConstPointerCast(backing_array_ptr, lb_type(m, t_load_directory_file_ptr)); + + LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false)); + + lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr); + lb_make_global_private_const(addr); + + return lb_addr_load(p, addr); + } else { + GB_PANIC("UNKNOWN DIRECTIVE: %.*s", LIT(name)); } - return lb_emit_source_code_location_as_global(p, procedure, pos); } case BuiltinProc_type_info_of: { diff --git a/src/string.cpp b/src/string.cpp index 9fb933b1b..bd703b2a6 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -293,6 +293,18 @@ gb_internal String filename_from_path(String s) { return make_string(nullptr, 0); } + +gb_internal String filename_without_directory(String s) { + isize j = 0; + for (j = s.len-1; j >= 0; j--) { + if (s[j] == '/' || + s[j] == '\\') { + break; + } + } + return substring(s, gb_max(j+1, 0), s.len); +} + gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) { isize len = x.len+y.len; u8 *data = gb_alloc_array(a, u8, len+1); diff --git a/src/types.cpp b/src/types.cpp index c4b03c967..8275b87ba 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -679,6 +679,10 @@ gb_global Type *t_allocator_error = nullptr; gb_global Type *t_source_code_location = nullptr; gb_global Type *t_source_code_location_ptr = nullptr; +gb_global Type *t_load_directory_file = nullptr; +gb_global Type *t_load_directory_file_ptr = nullptr; +gb_global Type *t_load_directory_file_slice = nullptr; + gb_global Type *t_map_info = nullptr; gb_global Type *t_map_cell_info = nullptr; gb_global Type *t_raw_map = nullptr; -- cgit v1.2.3 From c291fffce1972fcfc631dd4c6f623e3f5055f74b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 12 Feb 2024 12:29:37 +0000 Subject: Add metadata type to `map`s debug information to aid debuggers knowing what is in it it --- src/check_type.cpp | 29 +++++++++++++++++++++++++++++ src/llvm_backend_debug.cpp | 2 +- src/types.cpp | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'src/types.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index d216ae3e6..5c70500d1 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2265,6 +2265,35 @@ gb_internal void init_map_internal_types(Type *type) { GB_ASSERT(key != nullptr); GB_ASSERT(value != nullptr); + Scope *metadata_scope = create_scope(nullptr, nullptr); + + Type *metadata_type = alloc_type_struct(); + metadata_type->Struct.fields = slice_make(permanent_allocator(), 3); + metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved); + metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved); + metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved); + metadata_type->Struct.scope = metadata_scope; + + gb_unused(type_size_of(metadata_type)); + + // NOTE(bill): [0]^struct{key: Key, value: Value, hash: uintptr} + // This is a zero array to a pointer to keep the alignment to that of a pointer, and not effective the size of the final struct + metadata_type = alloc_type_array(alloc_type_pointer(metadata_type), 0);; + + + Scope *scope = create_scope(nullptr, nullptr); + Type *debug_type = alloc_type_struct(); + debug_type->Struct.fields = slice_make(permanent_allocator(), 4); + debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), t_uintptr, false, 0, EntityState_Resolved); + debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved); + debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved); + debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved); + debug_type->Struct.scope = scope; + + gb_unused(type_size_of(debug_type)); + + type->Map.debug_metadata_type = debug_type; + type->Map.lookup_result_type = make_optional_ok_type(value); } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index e053c5b40..894c60729 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -710,7 +710,7 @@ gb_internal void lb_debug_complete_types(lbModule *m) { case Type_Map: GB_ASSERT(t_raw_map != nullptr); - bt = base_type(t_raw_map); + bt = base_type(t->Map.debug_metadata_type); /*fallthrough*/ case Type_Struct: if (file == nullptr) { diff --git a/src/types.cpp b/src/types.cpp index 8275b87ba..04fb06582 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -231,6 +231,7 @@ struct TypeProc { Type *key; \ Type *value; \ Type *lookup_result_type; \ + Type *debug_metadata_type; \ }) \ TYPE_KIND(Struct, TypeStruct) \ TYPE_KIND(Union, TypeUnion) \ -- cgit v1.2.3 From d496dbf3a0ee05819ab6e802939b4219cfa9c7fe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Feb 2024 16:54:41 +0000 Subject: Fix race condition with #soa --- src/check_type.cpp | 6 ++---- src/threading.cpp | 16 ++++++++++++++++ src/types.cpp | 17 ++++++----------- 3 files changed, 24 insertions(+), 15 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 66f8b1185..8a140d95e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -632,9 +632,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * scope_reserve(ctx->scope, min_field_count); - rw_mutex_lock(&struct_type->Struct.fields_mutex); - defer (rw_mutex_unlock(&struct_type->Struct.fields_mutex)); - if (st->is_raw_union && min_field_count > 1) { struct_type->Struct.is_raw_union = true; context = str_lit("struct #raw_union"); @@ -662,6 +659,7 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * gb_unused(where_clause_ok); } check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); + wait_signal_set(&struct_type->Struct.fields_wait_signal); } #define ST_ALIGN(_name) if (st->_name != nullptr) { \ @@ -2553,8 +2551,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e GB_ASSERT(is_type_struct(elem)); Type *old_struct = base_type(elem); - RW_MUTEX_GUARD(&old_struct->Struct.fields_mutex); + wait_signal_until_available(&old_struct->Struct.fields_wait_signal); field_count = old_struct->Struct.fields.count; soa_struct = alloc_type_struct(); diff --git a/src/threading.cpp b/src/threading.cpp index b8bc9b118..731394126 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -107,6 +107,22 @@ gb_internal void thread_set_name (Thread *t, char const *name); gb_internal void yield_thread(void); gb_internal void yield_process(void); +struct Wait_Signal { + Futex futex; +}; + +gb_internal void wait_signal_until_available(Wait_Signal *ws) { + if (ws->futex.load() == 0) { + futex_wait(&ws->futex, 1); + } +} + +gb_internal void wait_signal_set(Wait_Signal *ws) { + ws->futex.store(1); + futex_broadcast(&ws->futex); +} + + struct MutexGuard { MutexGuard() = delete; diff --git a/src/types.cpp b/src/types.cpp index 04fb06582..2f1994574 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -144,7 +144,7 @@ struct TypeStruct { Type * soa_elem; i32 soa_count; StructSoaKind soa_kind; - RwMutex fields_mutex; + Wait_Signal fields_wait_signal; BlockingMutex offset_mutex; // for settings offsets bool is_polymorphic; @@ -2969,9 +2969,8 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { isize max_count = 0; switch (type->kind) { case Type_Struct: - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); max_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); break; case Type_Tuple: max_count = type->Tuple.variables.count; break; } @@ -2982,8 +2981,7 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { switch (type->kind) { case Type_Struct: { - rw_mutex_shared_lock(&type->Struct.fields_mutex); - defer (rw_mutex_shared_unlock(&type->Struct.fields_mutex)); + wait_signal_until_available(&type->Struct.fields_wait_signal); for (isize i = 0; i < max_count; i++) { Entity *f = type->Struct.fields[i]; if (f->kind == Entity_Variable) { @@ -3048,9 +3046,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } if (type->kind == Type_Struct) { - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); isize field_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->flags&EntityFlag_Using) { @@ -3079,9 +3076,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } if (type->kind == Type_Struct) { - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); Scope *s = type->Struct.scope; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (s != nullptr) { Entity *found = scope_lookup_current(s, field_name); if (found != nullptr && found->kind != Entity_Variable) { @@ -3129,9 +3125,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } - rw_mutex_shared_lock(&type->Struct.fields_mutex); + wait_signal_until_available(&type->Struct.fields_wait_signal); isize field_count = type->Struct.fields.count; - rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { -- cgit v1.2.3 From 213b2fd0f8059751b0cdd05b904c49109b21d474 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 14:01:23 +0000 Subject: Add `bit_field` as a keyword --- core/odin/tokenizer/token.odin | 2 ++ src/tokenizer.cpp | 1 + src/types.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/types.cpp') diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 23808cf44..cd8953841 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -137,6 +137,7 @@ Token_Kind :: enum u32 { Union, // union Enum, // enum Bit_Set, // bit_set + Bit_Field, // bit_field Map, // map Dynamic, // dynamic Auto_Cast, // auto_cast @@ -270,6 +271,7 @@ tokens := [Token_Kind.COUNT]string { "union", "enum", "bit_set", + "bit_field", "map", "dynamic", "auto_cast", diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index dd9908be5..3d5348074 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_union, "union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_bit_set, "bit_set"), \ + TOKEN_KIND(Token_bit_field, "bit_field"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_auto_cast, "auto_cast"), \ diff --git a/src/types.cpp b/src/types.cpp index 2f1994574..78d281715 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4114,8 +4114,10 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi } if (allow_polymorphic && dst_is_polymorphic) { Type *fb = base_type(type_deref(f->type)); - if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) { - return true; + if (fb->kind == Type_Struct) { + if (fb->Struct.polymorphic_parent == dst) { + return true; + } } } -- cgit v1.2.3 From a4b8c1ea1779ce93349b203aaf56c5aeca316b61 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 15:55:20 +0000 Subject: Begin work adding `bit_field` --- base/runtime/core.odin | 9 +++ base/runtime/print.odin | 14 ++++ core/encoding/json/marshal.odin | 3 + core/fmt/fmt.odin | 65 +++++++++++++++++ core/reflect/reflect.odin | 10 +++ core/reflect/types.odin | 31 ++++++++ src/check_type.cpp | 152 ++++++++++++++++++++++++++++++++++++++++ src/checker.cpp | 18 +++++ src/llvm_backend.cpp | 8 ++- src/llvm_backend_debug.cpp | 36 ++++++++++ src/llvm_backend_general.cpp | 4 +- src/llvm_backend_type.cpp | 67 ++++++++++++++++++ src/parser.cpp | 78 +++++++++++++++++++++ src/parser.hpp | 15 ++++ src/parser_pos.cpp | 3 + src/types.cpp | 24 +++++++ 16 files changed, 535 insertions(+), 2 deletions(-) (limited to 'src/types.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 85e64242d..dcc1e7476 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -181,6 +181,13 @@ Type_Info_Matrix :: struct { Type_Info_Soa_Pointer :: struct { elem: ^Type_Info, } +Type_Info_Bit_Field :: struct { + backing_type: ^Type_Info, + names: []string, + types: []^Type_Info, + bit_sizes: []uintptr, + bit_offsets: []uintptr, +} Type_Info_Flag :: enum u8 { Comparable = 0, @@ -223,6 +230,7 @@ Type_Info :: struct { Type_Info_Relative_Multi_Pointer, Type_Info_Matrix, Type_Info_Soa_Pointer, + Type_Info_Bit_Field, }, } @@ -256,6 +264,7 @@ Typeid_Kind :: enum u8 { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } #assert(len(Typeid_Kind) < 32) diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 41ff9e1bb..c93c2ab49 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) { } print_byte(']') + case Type_Info_Bit_Field: + print_string("bit_field ") + print_type(info.backing_type) + print_string(" {") + for name, i in info.names { + if i > 0 { print_string(", ") } + print_string(name) + print_string(": ") + print_type(info.types[i]) + print_string(" | ") + print_u64(u64(info.bit_sizes[i])) + } + print_byte('}') + case Type_Info_Simd_Vector: print_string("#simd[") diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index e9285364b..e237892c3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -228,6 +228,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Matrix: return .Unsupported_Type + case runtime.Type_Info_Bit_Field: + return .Unsupported_Type + case runtime.Type_Info_Array: opt_write_start(w, opt, '[') or_return for i in 0.. (res: u64) { + for i in 0.. 0 { + io.write_string(fi.writer, ", ") + } + if hash { + fmt_write_indent(fi) + } + + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) + + + bit_offset := info.bit_offsets[i] + bit_size := info.bit_sizes[i] + + value := read_bits(([^]byte)(v.data), bit_offset, bit_size) + + fmt_value(fi, any{&value, info.types[i].id}, verb) + if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } + + } +} + + + // Formats a value based on its type and formatting verb // // Inputs: @@ -2611,6 +2673,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Matrix: fmt_matrix(fi, v, verb, info) + + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v, verb, info) } } // Formats a complex number based on the given formatting verb diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 0af23b18e..de5dec2e3 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -35,6 +35,7 @@ Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer +Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value @@ -70,6 +71,7 @@ Type_Kind :: enum { Relative_Multi_Pointer, Matrix, Soa_Pointer, + Bit_Field, } @@ -106,6 +108,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer case Type_Info_Matrix: return .Matrix case Type_Info_Soa_Pointer: return .Soa_Pointer + case Type_Info_Bit_Field: return .Bit_Field } } @@ -1604,6 +1607,13 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ } } return true + + case Type_Info_Bit_Field: + x, y := a, b + x.id = v.backing_type.id + y.id = v.backing_type.id + return equal(x, y, including_indirect_array_recursion, recursion_level+0) + } runtime.print_typeid(a.id) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index cbe108d82..2b96dd4fb 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -174,6 +174,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { if x.row_count != y.row_count { return false } if x.column_count != y.column_count { return false } return are_types_identical(x.elem, y.elem) + + case Type_Info_Bit_Field: + y := b.variant.(Type_Info_Bit_Field) or_return + if !are_types_identical(x.backing_type, y.backing_type) { return false } + if len(x.names) != len(y.names) { return false } + for _, i in x.names { + if x.names[i] != y.names[i] { + return false + } + if !are_types_identical(x.types[i], y.types[i]) { + return false + } + if x.bit_sizes[i] != y.bit_sizes[i] { + return false + } + } + return true } return false @@ -639,6 +656,20 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - } io.write_byte(w, ']', &n) or_return + case Type_Info_Bit_Field: + io.write_string(w, "bit_field ", &n) or_return + write_type(w, info.backing_type, &n) or_return + io.write_string(w, " {", &n) or_return + for name, i in info.names { + if i > 0 { io.write_string(w, ", ", &n) or_return } + io.write_string(w, name, &n) or_return + io.write_string(w, ": ", &n) or_return + write_type(w, info.types[i], &n) or_return + io.write_string(w, " | ", &n) or_return + io.write_u64(w, u64(info.bit_sizes[i]), 10, &n) or_return + } + io.write_string(w, "}", &n) or_return + case Type_Info_Simd_Vector: io.write_string(w, "#simd[", &n) or_return io.write_i64(w, i64(info.count), 10, &n) or_return diff --git a/src/check_type.cpp b/src/check_type.cpp index 8a140d95e..8afac2fc5 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -925,6 +925,144 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam enum_type->Enum.max_value_index = max_value_index; } +gb_internal bool is_valid_bit_field_backing_type(Type *type) { + if (type == nullptr) { + return nullptr; + } + type = base_type(type); + if (is_type_untyped(type)) { + return false; + } + if (is_type_integer(type)) { + return true; + } + if (type->kind == Type_Array) { + return is_type_integer(type->Array.elem); + } + return false; +} + +gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) { + ast_node(bf, BitFieldType, node); + GB_ASSERT(is_type_bit_field(bit_field_type)); + + Type *backing_type = check_type(ctx, bf->backing_type); + if (backing_type == nullptr || !is_valid_bit_field_backing_type(backing_type)) { + error(node, "Backing type for a bit_field must be an integer or an array of an integer"); + return; + } + + bit_field_type->BitField.backing_type = backing_type; + bit_field_type->BitField.scope = ctx->scope; + + auto fields = array_make(permanent_allocator(), 0, bf->fields.count); + auto bit_sizes = array_make (permanent_allocator(), 0, bf->fields.count); + + u64 maximum_bit_size = 8 * type_size_of(backing_type); + u64 total_bit_size = 0; + + for_array(i, bf->fields) { + i32 field_src_index = cast(i32)i; + Ast *field = bf->fields[i]; + if (field->kind != Ast_BitFieldField) { + error(field, "Invalid AST for a bit_field"); + continue; + } + ast_node(f, BitFieldField, field); + if (f->name == nullptr || f->name->kind != Ast_Ident) { + error(field, "A bit_field's field name must be an identifier"); + continue; + } + CommentGroup *docs = f->docs; + CommentGroup *comment = f->comment; + + String name = f->name->Ident.token.string; + + if (f->type == nullptr) { + error(field, "A bit_field's field must have a type"); + continue; + } + + Type *type = check_type(ctx, f->type); + if (type_size_of(type) > 8) { + error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type)); + } + + if (is_type_untyped(type)) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s); + gb_string_free(s); + } else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) { + gbString s = type_to_string(type); + error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s); + gb_string_free(s); + } + + if (f->bit_size == nullptr) { + error(field, "A bit_field's field must have a specified bit size"); + continue; + } + + + Operand o = {}; + check_expr(ctx, &o, f->bit_size); + if (o.mode != Addressing_Constant) { + error(f->bit_size, "A bit_field's specified bit size must be a constant"); + o.mode = Addressing_Invalid; + } + if (o.value.kind == ExactValue_Float) { + o.value = exact_value_to_integer(o.value); + } + + ExactValue bit_size = o.value; + + if (bit_size.kind != ExactValue_Integer) { + gbString s = expr_to_string(f->bit_size); + error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s); + gb_string_free(s); + } + + if (scope_lookup_current(ctx->scope, name) != nullptr) { + error(f->name, "'%.*s' is already declared in this bit_field", LIT(name)); + } else { + i64 bit_size_i64 = exact_value_to_i64(bit_size); + u8 bit_size_u8 = 0; + if (bit_size_i64 <= 0) { + error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64); + bit_size_i64 = 1; + } + if (bit_size_i64 > 64) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64); + bit_size_i64 = 64; + } + bit_size_u8 = cast(u8)bit_size_i64; + + Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index); + e->Variable.docs = docs; + e->Variable.comment = comment; + + add_entity(ctx, ctx->scope, nullptr, e); + array_add(&fields, e); + array_add(&bit_sizes, bit_size_u8); + add_entity_use(ctx, field, e); + } + } + + GB_ASSERT(fields.count <= bf->fields.count); + + if (total_bit_size > maximum_bit_size) { + gbString s = type_to_string(backing_type); + error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu", + cast(unsigned long long)total_bit_size, + s, + cast(unsigned long long)maximum_bit_size); + gb_string_free(s); + } + + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); +} + gb_internal bool is_type_valid_bit_set_range(Type *t) { if (is_type_integer(t)) { return true; @@ -3051,6 +3189,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T return true; case_end; + case_ast_node(bf, BitFieldType, e); + bool ips = ctx->in_polymorphic_specialization; + defer (ctx->in_polymorphic_specialization = ips); + ctx->in_polymorphic_specialization = false; + + *type = alloc_type_bit_field(); + set_base_type(named_type, *type); + check_open_scope(ctx, e); + check_bit_field_type(ctx, *type, named_type, e); + check_close_scope(ctx); + (*type)->BitField.node = e; + return true; + case_end; + case_ast_node(pt, ProcType, e); bool ips = ctx->in_polymorphic_specialization; diff --git a/src/checker.cpp b/src/checker.cpp index 569a3c76f..5827fc695 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -313,6 +313,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) { case Ast_StructType: node->StructType.scope = scope; break; case Ast_UnionType: node->UnionType.scope = scope; break; case Ast_EnumType: node->EnumType.scope = scope; break; + case Ast_BitFieldType: node->BitFieldType.scope = scope; break; default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); } } @@ -334,6 +335,7 @@ gb_internal Scope *scope_of_node(Ast *node) { case Ast_StructType: return node->StructType.scope; case Ast_UnionType: return node->UnionType.scope; case Ast_EnumType: return node->EnumType.scope; + case Ast_BitFieldType: return node->BitFieldType.scope; } GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); return nullptr; @@ -355,6 +357,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) { case Ast_EnumType: case Ast_UnionType: case Ast_BitSetType: + case Ast_BitFieldType: scope->flags |= ScopeFlag_Type; break; } @@ -2060,6 +2063,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_type_info_type_internal(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_type_info_type_internal(c, f->type); + } + break; case Type_Generic: break; @@ -2309,6 +2318,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_min_dep_type_info(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_min_dep_type_info(c, f->type); + } + break; + default: GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind])); break; @@ -2907,6 +2923,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); + t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -2936,6 +2953,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); + t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); } gb_internal void init_mem_allocator(Checker *c) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fa76ac22f..45d903b43 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2719,6 +2719,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { { // Type info member buffer // NOTE(bill): Removes need for heap allocation by making it global memory isize count = 0; + isize offsets_extra = 0; for (Type *t : m->info->type_info_types) { isize index = lb_type_info_index(m->info, t, false); @@ -2736,6 +2737,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { case Type_Tuple: count += t->Tuple.variables.count; break; + case Type_BitField: + count += t->BitField.fields.count; + // Twice is needed for the bit_offsets + offsets_extra += t->BitField.fields.count; + break; } } @@ -2752,7 +2758,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count); lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count); - lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count); + lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count+offsets_extra); lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count); lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count); } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index f45cf0cbc..7d3692a53 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -461,6 +461,42 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { lb_debug_type(m, type->Matrix.elem), subscripts, gb_count_of(subscripts)); } + + case Type_BitField: { + LLVMMetadataRef parent_scope = nullptr; + LLVMMetadataRef scope = nullptr; + LLVMMetadataRef file = nullptr; + unsigned line = 0; + u64 size_in_bits = 8*cast(u64)type_size_of(type); + u32 align_in_bits = 8*cast(u32)type_align_of(type); + LLVMDIFlags flags = LLVMDIFlagZero; + + unsigned element_count = cast(unsigned)type->BitField.fields.count; + LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); + + u64 offset_in_bits = 0; + for (unsigned i = 0; i < element_count; i++) { + Entity *f = type->BitField.fields[i]; + u8 bit_size = type->BitField.bit_sizes[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + unsigned field_line = 0; + LLVMDIFlags field_flags = LLVMDIFlagZero; + elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line, + bit_size, offset_in_bits, offset_in_bits, + field_flags, lb_debug_type(m, f->type) + ); + + offset_in_bits += bit_size; + } + + + return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line, + size_in_bits, align_in_bits, flags, + nullptr, elements, element_count, 0, nullptr, + "", 0 + ); + } } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f0f5327c6..2102420f8 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2216,7 +2216,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } return LLVMStructTypeInContext(ctx, fields, field_count, false); } - + + case Type_BitField: + return lb_type_internal(m, type->BitField.backing_type); } GB_PANIC("Invalid type %s", type_to_string(type)); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e291e40a5..3567a550b 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -1788,6 +1788,73 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lb_emit_store(p, tag, res); } break; + + case Type_BitField: + { + tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_field_ptr); + LLVMValueRef vals[5] = {}; + + vals[0] = lb_type_info(m, t->BitField.backing_type).value; + isize count = t->BitField.fields.count; + if (count > 0) { + i64 names_offset = 0; + i64 types_offset = 0; + i64 bit_sizes_offset = 0; + i64 bit_offsets_offset = 0; + lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset); + lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset); + lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset); + lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset); + + u64 bit_offset = 0; + for (isize source_index = 0; source_index < count; source_index++) { + Entity *f = t->BitField.fields[source_index]; + u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index]; + + lbValue index = lb_const_int(m, t_int, source_index); + if (f->token.string.len > 0) { + lbValue name = lb_emit_ptr_offset(p, memory_names, index); + lb_emit_store(p, name, lb_const_string(m, f->token.string)); + } + lbValue type_ptr = lb_emit_ptr_offset(p, memory_types, index); + lbValue bit_size_ptr = lb_emit_ptr_offset(p, memory_bit_sizes, index); + lbValue bit_offset_ptr = lb_emit_ptr_offset(p, memory_bit_offsets, index); + + lb_emit_store(p, type_ptr, lb_type_info(m, f->type)); + lb_emit_store(p, bit_size_ptr, lb_const_int(m, t_uintptr, bit_size)); + lb_emit_store(p, bit_offset_ptr, lb_const_int(m, t_uintptr, bit_offset)); + + // lb_global_type_info_member_types_values [types_offset +source_index] = get_type_info_ptr(m, f->type); + // lb_global_type_info_member_offsets_values[bit_sizes_offset +source_index] = lb_const_int(m, t_uintptr, bit_size).value; + // lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value; + // if (f->token.string.len > 0) { + // lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value; + // } + + bit_offset += bit_size; + } + + lbValue cv = lb_const_int(m, t_int, count); + vals[1] = llvm_const_slice(m, memory_names, cv); + vals[2] = llvm_const_slice(m, memory_types, cv); + vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); + vals[4] = llvm_const_slice(m, memory_bit_offsets, cv); + } + + for (isize i = 0; i < gb_count_of(vals); i++) { + if (vals[i] == nullptr) { + vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); + } + } + + lbValue res = {}; + res.type = type_deref(tag.type); + res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals)); + lb_emit_store(p, tag, res); + + break; + } + } diff --git a/src/parser.cpp b/src/parser.cpp index 78ac29dfd..70da9414d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -350,6 +350,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->Field.names = clone_ast_array(n->Field.names, f); n->Field.type = clone_ast(n->Field.type, f); break; + case Ast_BitFieldField: + n->BitFieldField.name = clone_ast(n->BitFieldField.name, f); + n->BitFieldField.type = clone_ast(n->BitFieldField.type, f); + n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f); + break; case Ast_FieldList: n->FieldList.list = clone_ast_array(n->FieldList.list, f); break; @@ -406,6 +411,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->BitSetType.elem = clone_ast(n->BitSetType.elem, f); n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f); break; + case Ast_BitFieldType: + n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f); + n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f); + break; case Ast_MapType: n->MapType.count = clone_ast(n->MapType.count, f); n->MapType.key = clone_ast(n->MapType.key, f); @@ -1045,6 +1054,17 @@ gb_internal Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast return result; } +gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, + CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_BitFieldField); + result->BitFieldField.name = name; + result->BitFieldField.type = type; + result->BitFieldField.bit_size = bit_size; + result->BitFieldField.docs = docs; + result->BitFieldField.comment = comment; + return result; +} + gb_internal Ast *ast_field_list(AstFile *f, Token token, Array const &list) { Ast *result = alloc_ast_node(f, Ast_FieldList); result->FieldList.token = token; @@ -1178,6 +1198,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under return result; } +gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array const &fields, Token close) { + Ast *result = alloc_ast_node(f, Ast_BitFieldType); + result->BitFieldType.token = token; + result->BitFieldType.backing_type = backing_type; + result->BitFieldType.open = open; + result->BitFieldType.fields = slice_from_array(fields); + result->BitFieldType.close = close; + return result; +} + + gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *result = alloc_ast_node(f, Ast_MapType); result->MapType.token = token; @@ -2549,6 +2580,53 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return ast_matrix_type(f, token, row_count, column_count, type); } break; + case Token_bit_field: { + Token token = expect_token(f, Token_bit_field); + isize prev_level; + + prev_level = f->expr_level; + f->expr_level = -1; + + Ast *backing_type = parse_type_or_ident(f); + if (backing_type == nullptr) { + Token token = advance_token(f); + syntax_error(token, "Expected a backing type for a 'bit_field'"); + backing_type = ast_bad_expr(f, token, f->curr_token); + } + + skip_possible_newline_for_literal(f); + Token open = expect_token_after(f, Token_OpenBrace, "bit_field"); + + + auto fields = array_make(ast_allocator(f), 0, 0); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = nullptr; + CommentGroup *comment = nullptr; + + Ast *name = parse_ident(f); + expect_token(f, Token_Colon); + Ast *type = parse_type(f); + expect_token(f, Token_Or); + Ast *bit_size = parse_expr(f, true); + + Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, docs, comment); + array_add(&fields, bf_field); + + if (!allow_field_separator(f)) { + break; + } + } + + Token close = expect_closing_brace_of_field_list(f); + + f->expr_level = prev_level; + + return ast_bit_field_type(f, token, backing_type, open, fields, close); + } + + case Token_struct: { Token token = expect_token(f, Token_struct); Ast *polymorphic_params = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index 1edb1f9dd..ff77c88c7 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -650,6 +650,13 @@ AST_KIND(_DeclEnd, "", bool) \ CommentGroup * docs; \ CommentGroup * comment; \ }) \ + AST_KIND(BitFieldField, "bit field field", struct { \ + Ast * name; \ + Ast * type; \ + Ast * bit_size; \ + CommentGroup *docs; \ + CommentGroup *comment; \ + }) \ AST_KIND(FieldList, "field list", struct { \ Token token; \ Slice list; \ @@ -742,6 +749,14 @@ AST_KIND(_TypeBegin, "", bool) \ Ast * elem; \ Ast * underlying; \ }) \ + AST_KIND(BitFieldType, "bit field type", struct { \ + Scope *scope; \ + Token token; \ + Ast * backing_type; \ + Token open; \ + Slice fields; /* BitFieldField */ \ + Token close; \ + }) \ AST_KIND(MapType, "map type", struct { \ Token token; \ Ast *count; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index f49c40f16..b2e12999b 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) { case Ast_UnionType: return node->UnionType.token; case Ast_EnumType: return node->EnumType.token; case Ast_BitSetType: return node->BitSetType.token; + case Ast_BitFieldType: return node->BitFieldType.token; case Ast_MapType: return node->MapType.token; case Ast_MatrixType: return node->MatrixType.token; } @@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) { return ast_end_token(node->BitSetType.underlying); } return ast_end_token(node->BitSetType.elem); + case Ast_BitFieldType: + return node->BitFieldType.close; case Ast_MapType: return ast_end_token(node->MapType.value); case Ast_MatrixType: return ast_end_token(node->MatrixType.elem); } diff --git a/src/types.cpp b/src/types.cpp index 78d281715..1c28e6583 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -282,6 +282,13 @@ struct TypeProc { Type *generic_column_count; \ i64 stride_in_bytes; \ }) \ + TYPE_KIND(BitField, struct { \ + Scope * scope; \ + Type * backing_type; \ + Slice fields; \ + Slice bit_sizes; \ + Ast * node; \ + }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -355,6 +362,7 @@ enum Typeid_Kind : u8 { Typeid_Relative_Multi_Pointer, Typeid_Matrix, Typeid_SoaPointer, + Typeid_Bit_Field, }; // IMPORTANT NOTE(bill): This must match the same as the in core.odin @@ -641,6 +649,7 @@ gb_global Type *t_type_info_relative_pointer = nullptr; gb_global Type *t_type_info_relative_multi_pointer = nullptr; gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr; +gb_global Type *t_type_info_bit_field = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -670,6 +679,7 @@ gb_global Type *t_type_info_relative_pointer_ptr = nullptr; gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr; +gb_global Type *t_type_info_bit_field_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -1040,6 +1050,11 @@ gb_internal Type *alloc_type_enum() { return t; } +gb_internal Type *alloc_type_bit_field() { + Type *t = alloc_type(Type_BitField); + return t; +} + gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) { GB_ASSERT(is_type_pointer(pointer_type)); GB_ASSERT(is_type_integer(base_integer)); @@ -1707,6 +1722,10 @@ gb_internal bool is_type_bit_set(Type *t) { t = base_type(t); return (t->kind == Type_BitSet); } +gb_internal bool is_type_bit_field(Type *t) { + t = base_type(t); + return (t->kind == Type_BitField); +} gb_internal bool is_type_map(Type *t) { t = base_type(t); return t->kind == Type_Map; @@ -3568,6 +3587,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Slice: return build_context.int_size; + case Type_BitField: + return type_align_of_internal(t->BitField.backing_type, path); case Type_Tuple: { i64 max = 1; @@ -3943,6 +3964,9 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { return stride_in_bytes * t->Matrix.column_count; } + case Type_BitField: + return type_size_of_internal(t->BitField.backing_type, path); + case Type_RelativePointer: return type_size_of_internal(t->RelativePointer.base_integer, path); case Type_RelativeMultiPointer: -- cgit v1.2.3 From 5a84a0822596fac47dd35bf1c2f1d9bb60bbe5c1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 17:24:42 +0000 Subject: Add general support for `bit_field`s --- base/runtime/internal.odin | 22 ++++++++++++ core/fmt/fmt.odin | 10 ++++-- src/check_expr.cpp | 86 ++++++++++++++++++++++++++++++++++++++------ src/check_stmt.cpp | 10 ++++++ src/check_type.cpp | 21 +++++++++-- src/checker.hpp | 1 + src/entity.cpp | 2 ++ src/llvm_backend.hpp | 8 +++++ src/llvm_backend_expr.cpp | 16 +++++++++ src/llvm_backend_general.cpp | 51 ++++++++++++++++++++++++-- src/parser.hpp | 1 + src/types.cpp | 34 ++++++++++++++++++ 12 files changed, 245 insertions(+), 17 deletions(-) (limited to 'src/types.cpp') diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 691f76ff1..62bee8620 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1034,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 { } } + + + +__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) { + for i in 0..value.kind == ExactValue_Integer) { gbString b = type_to_string(type); i64 sz = type_size_of(type); + i64 bit_size = 8*sz; + bool size_changed = false; + if (max_bit_size > 0) { + size_changed = (bit_size != max_bit_size); + bit_size = gb_min(bit_size, max_bit_size); + } BigInt *bi = &o->value.value_integer; if (is_type_unsigned(type)) { if (big_int_is_neg(bi)) { @@ -2083,25 +2089,36 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } else { BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz); + BigInt bits = big_int_make_i64(bit_size); big_int_shl_eq(&max_size, &bits); big_int_sub_eq(&max_size, &one); String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } } } else { BigInt zero = big_int_make_u64(0); BigInt one = big_int_make_u64(1); BigInt max_size = big_int_make_u64(1); - BigInt bits = big_int_make_i64(8*sz - 1); + BigInt bits = big_int_make_i64(bit_size - 1); big_int_shl_eq(&max_size, &bits); + + String max_size_str = {}; if (big_int_is_neg(bi)) { big_int_neg(&max_size, &max_size); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); - error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); } else { big_int_sub_eq(&max_size, &one); - String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + max_size_str = big_int_to_string(temporary_allocator(), &max_size); + } + + if (size_changed) { + error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str)); + } else { error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); } } @@ -2112,7 +2129,7 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } return false; } -gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { +gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); defer( @@ -2143,7 +2160,7 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { error_line("\tSuggestion: the expression may be casted to %s\n", b); - } else if (check_integer_exceed_suggestion(c, o, type)) { + } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; } } @@ -2217,13 +2234,18 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { + i64 max_bit_size = 0; + if (ctx->bit_field_bit_size) { + max_bit_size = ctx->bit_field_bit_size; + } + if (are_types_identical(o->type, type)) { error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b); } else { error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c); } - check_assignment_error_suggestion(ctx, o, type); + check_assignment_error_suggestion(ctx, o, type, max_bit_size); } } else { error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); @@ -2234,6 +2256,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ } gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { + if (o->expr && o->expr->kind == Ast_SelectorExpr) { + if (o->expr->SelectorExpr.is_bit_field) { + return true; + } + } if (o->mode == Addressing_OptionalOk) { Ast *expr = unselector_expr(o->expr); if (expr->kind != Ast_TypeAssertion) { @@ -2306,6 +2333,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * Entity *e = entity_of_node(ue->expr); if (e != nullptr && (e->flags & EntityFlag_Param) != 0) { error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str); + } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) { + error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str); } else { switch (o->mode) { case Addressing_Constant: @@ -5067,6 +5096,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod operand->type = entity->type; operand->expr = node; + if (entity->flags & EntityFlag_BitFieldField) { + add_package_dependency(c, "runtime", "__write_bits"); + add_package_dependency(c, "runtime", "__read_bits"); + } + switch (entity->kind) { case Entity_Constant: operand->value = entity->Constant.value; @@ -5080,6 +5114,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } break; case Entity_Variable: + if (sel.is_bit_field) { + se->is_bit_field = true; + } if (sel.indirect) { operand->mode = Addressing_Variable; } else if (operand->mode == Addressing_Context) { @@ -11115,6 +11152,33 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; + case_ast_node(f, BitFieldField, node); + str = write_expr_to_string(str, f->name, shorthand); + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, f->type, shorthand); + str = gb_string_appendc(str, " | "); + str = write_expr_to_string(str, f->bit_size, shorthand); + case_end; + case_ast_node(bf, BitFieldType, node); + str = gb_string_appendc(str, "bit_field "); + if (!shorthand) { + str = write_expr_to_string(str, bf->backing_type, shorthand); + } + str = gb_string_appendc(str, " {"); + if (shorthand) { + str = gb_string_appendc(str, "..."); + } else { + for_array(i, bf->fields) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, bf->fields[i], false); + } + return str; + } + str = gb_string_appendc(str, "}"); + case_end; + case_ast_node(ia, InlineAsmExpr, node); str = gb_string_appendc(str, "asm("); for_array(i, ia->param_types) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6897701d6..a7dd9743b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -485,7 +485,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } + Entity *lhs_e = entity_of_node(lhs->expr); + u8 prev_bit_field_bit_size = ctx->bit_field_bit_size; + if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) { + // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case + ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size; + } + check_assignment(ctx, rhs, assignment_type, str_lit("assignment")); + + ctx->bit_field_bit_size = prev_bit_field_bit_size; + if (rhs->mode == Addressing_Invalid) { return nullptr; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 8afac2fc5..8c746a2f7 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1035,11 +1035,19 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64); bit_size_i64 = 64; } + i64 sz = 8*type_size_of(type); + if (bit_size_i64 > sz) { + error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz); + bit_size_i64 = sz; + } + bit_size_u8 = cast(u8)bit_size_i64; Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index); e->Variable.docs = docs; e->Variable.comment = comment; + e->Variable.bit_field_bit_size = bit_size_u8; + e->flags |= EntityFlag_BitFieldField; add_entity(ctx, ctx->scope, nullptr, e); array_add(&fields, e); @@ -1050,6 +1058,14 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, GB_ASSERT(fields.count <= bf->fields.count); + auto bit_offsets = slice_make(permanent_allocator(), fields.count); + i64 curr_offset = 0; + for_array(i, bit_sizes) { + bit_offsets[i] = curr_offset; + curr_offset += cast(i64)bit_sizes[i]; + } + + if (total_bit_size > maximum_bit_size) { gbString s = type_to_string(backing_type); error(node, "The numbers required %llu exceeds the backing type's (%s) bit size %llu", @@ -1059,8 +1075,9 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, gb_string_free(s); } - bit_field_type->BitField.fields = slice_from_array(fields); - bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.fields = slice_from_array(fields); + bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); + bit_field_type->BitField.bit_offsets = bit_offsets; } gb_internal bool is_type_valid_bit_set_range(Type *t) { diff --git a/src/checker.hpp b/src/checker.hpp index 9aee82257..066d6bb4a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -475,6 +475,7 @@ struct CheckerContext { bool hide_polymorphic_errors; bool in_polymorphic_specialization; bool allow_arrow_right_selector_expr; + u8 bit_field_bit_size; Scope * polymorphic_scope; Ast *assignment_lhs_hint; diff --git a/src/entity.cpp b/src/entity.cpp index e6c46d37e..916c2b2bd 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -43,6 +43,7 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, + EntityFlag_BitFieldField = 1ull<<12, @@ -212,6 +213,7 @@ struct Entity { Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; i32 field_group_index; + u8 bit_field_bit_size; ParameterValue param_value; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 026454c81..00d1b7a21 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -84,6 +84,8 @@ enum lbAddrKind { lbAddr_Swizzle, lbAddr_SwizzleLarge, + + lbAddr_BitField, }; struct lbAddr { @@ -118,6 +120,12 @@ struct lbAddr { Type *type; Slice indices; } swizzle_large; + struct { + Type *type; + i64 index; + i64 bit_offset; + i64 bit_size; + } bitfield; }; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0c06c8c1b..6bef21822 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4627,6 +4627,22 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); + if (sel.is_bit_field) { + lbAddr addr = lb_build_addr(p, se->expr); + Type *bf_type = base_type(type_deref(lb_addr_type(addr))); + GB_ASSERT(bf_type->kind == Type_BitField); + + lbValue a = lb_addr_get_ptr(p, addr); + Selection sub_sel = sel; + sub_sel.index.count -= 1; + i32 index = sel.index[sel.index.count-1]; + + Entity *f = bf_type->BitField.fields[index]; + u8 bit_size = bf_type->BitField.bit_sizes[index]; + i64 bit_offset = bf_type->BitField.bit_offsets[index]; + + return lb_addr_bit_field(a, f->type, index, bit_offset, bit_size); + } if (sel.pseudo_field) { GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup); Entity *e = entity_of_node(sel_node); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 2102420f8..4ff8482a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -451,6 +451,20 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice(temporary_allocator(), 4); + args[0] = dst; + args[1] = lb_address_from_load_or_generate_local(p, value); + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__write_bits", args); + return; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); GB_ASSERT(rel_ptr->kind == Type_RelativePointer || rel_ptr->kind == Type_RelativeMultiPointer); @@ -1074,8 +1098,31 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { GB_ASSERT(addr.addr.value != nullptr); + if (addr.kind == lbAddr_BitField) { + lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true); + lbValue src = addr.addr; - if (addr.kind == lbAddr_RelativePointer) { + auto args = array_make(temporary_allocator(), 4); + args[0] = dst.addr; + args[1] = src; + args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset); + args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size); + lb_emit_runtime_call(p, "__read_bits", args); + + lbValue r = lb_addr_load(p, dst); + + if (!is_type_unsigned(core_type(addr.bitfield.type))) { + // Sign extension + // m := 1<<(bit_size-1) + // r = (r XOR m) - m + Type *t = addr.bitfield.type; + lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1)); + r = lb_emit_arith(p, Token_Xor, r, m, t); + r = lb_emit_arith(p, Token_Sub, r, m, t); + } + + return r; + } else if (addr.kind == lbAddr_RelativePointer) { Type *rel_ptr = base_type(lb_addr_type(addr)); Type *base_integer = nullptr; Type *pointer_type = nullptr; diff --git a/src/parser.hpp b/src/parser.hpp index ff77c88c7..1f4ec8726 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -429,6 +429,7 @@ AST_KIND(_ExprBegin, "", bool) \ Ast *expr, *selector; \ u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \ u8 swizzle_indices; /*2 bits per component*/ \ + bool is_bit_field; \ }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { \ diff --git a/src/types.cpp b/src/types.cpp index 1c28e6583..be4b8944b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -287,6 +287,7 @@ struct TypeProc { Type * backing_type; \ Slice fields; \ Slice bit_sizes; \ + Slice bit_offsets; \ Ast * node; \ }) \ TYPE_KIND(SoaPointer, struct { Type *elem; }) @@ -408,6 +409,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool is_bit_field; bool pseudo_field; }; gb_global Selection const empty_selection = {0}; @@ -3187,6 +3189,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name else if (field_name == "a") mapped_field_name = str_lit("w"); return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident); } + } else if (type->kind == Type_BitField) { + for_array(i, type->BitField.fields) { + Entity *f = type->BitField.fields[i]; + if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { + continue; + } + String str = f->token.string; + if (field_name == str) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + sel.is_bit_field = true; + return sel; + } + } + } else if (type->kind == Type_Basic) { switch (type->Basic.kind) { case Basic_any: { @@ -4551,6 +4568,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count)); str = write_type_to_string(str, type->Matrix.elem); break; + + case Type_BitField: + str = gb_string_appendc(str, "bit_field "); + str = write_type_to_string(str, type->BitField.backing_type); + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->BitField.fields.count; i++) { + Entity *f = type->BitField.fields[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]); + } + str = gb_string_appendc(str, " }"); + break; } return str; -- cgit v1.2.3 From c14b9d461a5c58d4b80957682f00205714063435 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 19:14:16 +0000 Subject: Support `using` of a `bit_field` within a `struct` --- src/check_type.cpp | 2 ++ src/llvm_backend_expr.cpp | 14 +++++++-- src/llvm_backend_utility.cpp | 2 +- src/types.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 6 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 41eae2178..74828f97f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -89,6 +89,8 @@ gb_internal bool does_field_type_allow_using(Type *t) { return true; } else if (is_type_array(t)) { return t->Array.count <= 4; + } else if (is_type_bit_field(t)) { + return true; } return false; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7e000c9e8..5bf2642e6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4679,12 +4679,20 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(sel.entity != nullptr); if (sel.is_bit_field) { lbAddr addr = lb_build_addr(p, se->expr); - Type *bf_type = base_type(type_deref(lb_addr_type(addr))); - GB_ASSERT(bf_type->kind == Type_BitField); - lbValue a = lb_addr_get_ptr(p, addr); + Selection sub_sel = sel; sub_sel.index.count -= 1; + + Type *bf_type = type_from_selection(type, sub_sel); + bf_type = base_type(type_deref(bf_type)); + GB_ASSERT(bf_type->kind == Type_BitField); + + lbValue a = lb_addr_get_ptr(p, addr); + if (sub_sel.index.count > 0) { + a = lb_emit_deep_field_gep(p, a, sub_sel); + } + i32 index = sel.index[sel.index.count-1]; Entity *f = bf_type->BitField.fields[index]; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index bc5106601..5bd3cd8e2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1332,7 +1332,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection if (index == 0) { type = t_rawptr; } else if (index == 1) { - type = t_type_info_ptr; + type = t_typeid; } e = lb_emit_struct_ep(p, e, index); break; diff --git a/src/types.cpp b/src/types.cpp index be4b8944b..3945c7111 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -385,6 +385,9 @@ enum : int { gb_internal bool is_type_comparable(Type *t); gb_internal bool is_type_simple_compare(Type *t); +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false); +gb_internal Type *base_type(Type *t); +gb_internal Type *alloc_type_multi_pointer(Type *elem); gb_internal u32 type_info_flags_of_type(Type *type) { if (type == nullptr) { @@ -762,7 +765,6 @@ gb_internal bool is_type_proc(Type *t); gb_internal bool is_type_slice(Type *t); gb_internal bool is_type_integer(Type *t); gb_internal bool type_set_offsets(Type *t); -gb_internal Type *base_type(Type *t); gb_internal i64 type_size_of_internal(Type *t, TypePath *path); gb_internal i64 type_align_of_internal(Type *t, TypePath *path); @@ -1157,7 +1159,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co //////////////////////////////////////////////////////////////// -gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) { +gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) { if (t != nullptr) { Type *bt = base_type(t); if (bt == nullptr) { @@ -4261,6 +4263,71 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ } +gb_internal Type *type_from_selection(Type *type, Selection const &sel) { + for (i32 index : sel.index) { + Type *bt = base_type(type_deref(type)); + switch (bt->kind) { + case Type_Struct: + type = bt->Struct.fields[index]->type; + break; + case Type_Tuple: + type = bt->Tuple.variables[index]->type; + break; + case Type_BitField: + type = bt->BitField.fields[index]->type; + break; + case Type_Array: + type = bt->Array.elem; + break; + case Type_EnumeratedArray: + type = bt->Array.elem; + break; + case Type_Slice: + switch (index) { + case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break; + case 1: type = t_int; break; + } + break; + case Type_DynamicArray: + switch (index) { + case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break; + case 1: type = t_int; break; + case 2: type = t_int; break; + case 3: type = t_allocator; break; + } + break; + case Type_Map: + switch (index) { + case 0: type = t_uintptr; break; + case 1: type = t_int; break; + case 2: type = t_allocator; break; + } + break; + case Type_Basic: + if (is_type_complex_or_quaternion(bt)) { + type = base_complex_elem_type(bt); + } else { + switch (type->Basic.kind) { + case Basic_any: + switch (index) { + case 0: type = t_rawptr; break; + case 1: type = t_typeid; break; + } + break; + case Basic_string: + switch (index) { + case 0: type = t_u8_multi_ptr; break; + case 1: type = t_int; break; + } + break; + } + } + break; + } + } + return type; +} + gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { -- cgit v1.2.3 From 3060225f460cb5d1ad124fcf449b27b3e2e981f8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 19:24:16 +0000 Subject: Simplify usage code --- src/llvm_backend_expr.cpp | 8 +-- src/types.cpp | 130 +++++++++++++++++++++++----------------------- 2 files changed, 69 insertions(+), 69 deletions(-) (limited to 'src/types.cpp') diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 5bf2642e6..442121f83 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4684,15 +4684,17 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { Selection sub_sel = sel; sub_sel.index.count -= 1; - Type *bf_type = type_from_selection(type, sub_sel); - bf_type = base_type(type_deref(bf_type)); - GB_ASSERT(bf_type->kind == Type_BitField); lbValue a = lb_addr_get_ptr(p, addr); if (sub_sel.index.count > 0) { a = lb_emit_deep_field_gep(p, a, sub_sel); } + + Type *bf_type = type_deref(a.type); + bf_type = base_type(type_deref(bf_type)); + GB_ASSERT(bf_type->kind == Type_BitField); + i32 index = sel.index[sel.index.count-1]; Entity *f = bf_type->BitField.fields[index]; diff --git a/src/types.cpp b/src/types.cpp index 3945c7111..eac834f25 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4262,72 +4262,70 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_ return t; } - -gb_internal Type *type_from_selection(Type *type, Selection const &sel) { - for (i32 index : sel.index) { - Type *bt = base_type(type_deref(type)); - switch (bt->kind) { - case Type_Struct: - type = bt->Struct.fields[index]->type; - break; - case Type_Tuple: - type = bt->Tuple.variables[index]->type; - break; - case Type_BitField: - type = bt->BitField.fields[index]->type; - break; - case Type_Array: - type = bt->Array.elem; - break; - case Type_EnumeratedArray: - type = bt->Array.elem; - break; - case Type_Slice: - switch (index) { - case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break; - case 1: type = t_int; break; - } - break; - case Type_DynamicArray: - switch (index) { - case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break; - case 1: type = t_int; break; - case 2: type = t_int; break; - case 3: type = t_allocator; break; - } - break; - case Type_Map: - switch (index) { - case 0: type = t_uintptr; break; - case 1: type = t_int; break; - case 2: type = t_allocator; break; - } - break; - case Type_Basic: - if (is_type_complex_or_quaternion(bt)) { - type = base_complex_elem_type(bt); - } else { - switch (type->Basic.kind) { - case Basic_any: - switch (index) { - case 0: type = t_rawptr; break; - case 1: type = t_typeid; break; - } - break; - case Basic_string: - switch (index) { - case 0: type = t_u8_multi_ptr; break; - case 1: type = t_int; break; - } - break; - } - } - break; - } - } - return type; -} - +// gb_internal Type *type_from_selection(Type *type, Selection const &sel) { +// for (i32 index : sel.index) { +// Type *bt = base_type(type_deref(type)); +// switch (bt->kind) { +// case Type_Struct: +// type = bt->Struct.fields[index]->type; +// break; +// case Type_Tuple: +// type = bt->Tuple.variables[index]->type; +// break; +// case Type_BitField: +// type = bt->BitField.fields[index]->type; +// break; +// case Type_Array: +// type = bt->Array.elem; +// break; +// case Type_EnumeratedArray: +// type = bt->Array.elem; +// break; +// case Type_Slice: +// switch (index) { +// case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break; +// case 1: type = t_int; break; +// } +// break; +// case Type_DynamicArray: +// switch (index) { +// case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break; +// case 1: type = t_int; break; +// case 2: type = t_int; break; +// case 3: type = t_allocator; break; +// } +// break; +// case Type_Map: +// switch (index) { +// case 0: type = t_uintptr; break; +// case 1: type = t_int; break; +// case 2: type = t_allocator; break; +// } +// break; +// case Type_Basic: +// if (is_type_complex_or_quaternion(bt)) { +// type = base_complex_elem_type(bt); +// } else { +// switch (type->Basic.kind) { +// case Basic_any: +// switch (index) { +// case 0: type = t_rawptr; break; +// case 1: type = t_typeid; break; +// } +// break; +// case Basic_string: +// switch (index) { +// case 0: type = t_u8_multi_ptr; break; +// case 1: type = t_int; break; +// } +// break; +// } +// } +// break; +// } +// } +// return type; +// } gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { -- cgit v1.2.3 From 54515af8ccff67cae71982d1bbf5bd1c31628af3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 19:41:48 +0000 Subject: Add field tags to `bit_field` --- base/runtime/core.odin | 1 + core/fmt/fmt.odin | 25 +++++++++++++++++++++++-- src/check_type.cpp | 10 ++++++++++ src/llvm_backend_type.cpp | 34 +++++++++++++++++++--------------- src/parser.cpp | 10 ++++++++-- src/parser.hpp | 1 + src/types.cpp | 1 + 7 files changed, 63 insertions(+), 19 deletions(-) (limited to 'src/types.cpp') diff --git a/base/runtime/core.odin b/base/runtime/core.odin index dcc1e7476..2f63a7ac2 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -187,6 +187,7 @@ Type_Info_Bit_Field :: struct { types: []^Type_Info, bit_sizes: []uintptr, bit_offsets: []uintptr, + tags: []string, } Type_Info_Flag :: enum u8 { diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 51f158cd8..38e125c30 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2297,6 +2297,23 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit return } + handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) { + tag := info.tags[idx] + if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok { + value := strings.trim_space(string(vt)) + switch value { + case "": return false + case "-": return true + } + r, w := utf8.decode_rune_in_string(value) + value = value[w:] + if value == "" || value[0] == ',' { + verb^ = r + } + } + return false + } + io.write_string(fi.writer, "bit_field{", &fi.n) hash := fi.hash; defer fi.hash = hash @@ -2318,7 +2335,11 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit field_count := -1 for name, i in info.names { - _ = i + field_verb := verb + if handle_bit_field_tag(v.data, info, i, &field_verb) { + continue + } + field_count += 1 if !do_trailing_comma && field_count > 0 { @@ -2343,7 +2364,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit value = (value ~ m) - m } - fmt_value(fi, any{&value, type.id}, verb) + fmt_value(fi, any{&value, type.id}, field_verb) if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 74828f97f..1bcae140f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -959,6 +959,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, auto fields = array_make(permanent_allocator(), 0, bf->fields.count); auto bit_sizes = array_make (permanent_allocator(), 0, bf->fields.count); + auto tags = array_make (permanent_allocator(), 0, bf->fields.count); u64 maximum_bit_size = 8 * type_size_of(backing_type); u64 total_bit_size = 0; @@ -1054,6 +1055,14 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, add_entity(ctx, ctx->scope, nullptr, e); array_add(&fields, e); array_add(&bit_sizes, bit_size_u8); + + String tag = f->tag.string; + if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) { + error(f->tag, "Invalid string literal"); + tag = {}; + } + array_add(&tags, tag); + add_entity_use(ctx, field, e); } } @@ -1080,6 +1089,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, bit_field_type->BitField.fields = slice_from_array(fields); bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes); bit_field_type->BitField.bit_offsets = bit_offsets; + bit_field_type->BitField.tags = tags.data; } gb_internal bool is_type_valid_bit_set_range(Type *t) { diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 3567a550b..4952d75de 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -1792,19 +1792,21 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup case Type_BitField: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_field_ptr); - LLVMValueRef vals[5] = {}; + LLVMValueRef vals[6] = {}; vals[0] = lb_type_info(m, t->BitField.backing_type).value; isize count = t->BitField.fields.count; if (count > 0) { - i64 names_offset = 0; - i64 types_offset = 0; - i64 bit_sizes_offset = 0; + i64 names_offset = 0; + i64 types_offset = 0; + i64 bit_sizes_offset = 0; i64 bit_offsets_offset = 0; + i64 tags_offset = 0; lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset); lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset); lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset); lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset); + lbValue memory_tags = lb_type_info_member_tags_offset (m, count, &tags_offset); u64 bit_offset = 0; for (isize source_index = 0; source_index < count; source_index++) { @@ -1813,8 +1815,8 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lbValue index = lb_const_int(m, t_int, source_index); if (f->token.string.len > 0) { - lbValue name = lb_emit_ptr_offset(p, memory_names, index); - lb_emit_store(p, name, lb_const_string(m, f->token.string)); + lbValue name_ptr = lb_emit_ptr_offset(p, memory_names, index); + lb_emit_store(p, name_ptr, lb_const_string(m, f->token.string)); } lbValue type_ptr = lb_emit_ptr_offset(p, memory_types, index); lbValue bit_size_ptr = lb_emit_ptr_offset(p, memory_bit_sizes, index); @@ -1824,21 +1826,23 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup lb_emit_store(p, bit_size_ptr, lb_const_int(m, t_uintptr, bit_size)); lb_emit_store(p, bit_offset_ptr, lb_const_int(m, t_uintptr, bit_offset)); - // lb_global_type_info_member_types_values [types_offset +source_index] = get_type_info_ptr(m, f->type); - // lb_global_type_info_member_offsets_values[bit_sizes_offset +source_index] = lb_const_int(m, t_uintptr, bit_size).value; - // lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value; - // if (f->token.string.len > 0) { - // lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value; - // } + if (t->BitField.tags) { + String tag = t->BitField.tags[source_index]; + if (tag.len > 0) { + lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index); + lb_emit_store(p, tag_ptr, lb_const_string(m, tag)); + } + } bit_offset += bit_size; } lbValue cv = lb_const_int(m, t_int, count); - vals[1] = llvm_const_slice(m, memory_names, cv); - vals[2] = llvm_const_slice(m, memory_types, cv); - vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); + vals[1] = llvm_const_slice(m, memory_names, cv); + vals[2] = llvm_const_slice(m, memory_types, cv); + vals[3] = llvm_const_slice(m, memory_bit_sizes, cv); vals[4] = llvm_const_slice(m, memory_bit_offsets, cv); + vals[5] = llvm_const_slice(m, memory_tags, cv); } for (isize i = 0; i < gb_count_of(vals); i++) { diff --git a/src/parser.cpp b/src/parser.cpp index 6a9481693..03d1e7aeb 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1054,12 +1054,13 @@ gb_internal Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast return result; } -gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, +gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_BitFieldField); result->BitFieldField.name = name; result->BitFieldField.type = type; result->BitFieldField.bit_size = bit_size; + result->BitFieldField.tag = tag; result->BitFieldField.docs = docs; result->BitFieldField.comment = comment; return result; @@ -2611,7 +2612,12 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { expect_token(f, Token_Or); Ast *bit_size = parse_expr(f, true); - Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, docs, comment); + Token tag = {}; + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + } + + Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment); array_add(&fields, bf_field); if (!allow_field_separator(f)) { diff --git a/src/parser.hpp b/src/parser.hpp index 1f4ec8726..f410419d4 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -655,6 +655,7 @@ AST_KIND(_DeclEnd, "", bool) \ Ast * name; \ Ast * type; \ Ast * bit_size; \ + Token tag; \ CommentGroup *docs; \ CommentGroup *comment; \ }) \ diff --git a/src/types.cpp b/src/types.cpp index eac834f25..90cb130b6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -286,6 +286,7 @@ struct TypeProc { Scope * scope; \ Type * backing_type; \ Slice fields; \ + String * tags; /*count == fields.count*/ \ Slice bit_sizes; \ Slice bit_offsets; \ Ast * node; \ -- cgit v1.2.3