From 4662133893bc152ee7a13619c91b9efcaec0028d Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 8 Nov 2021 22:01:21 +0100 Subject: [PATCH] Updates to bitstruct --- src/compiler/ast.c | 1 + src/compiler/compiler_internal.h | 1 + src/compiler/enums.h | 27 ++-- src/compiler/lexer.c | 41 ++++++ src/compiler/llvm_codegen_expr.c | 33 +++++ src/compiler/parse_global.c | 2 +- src/compiler/sema_decls.c | 3 + src/compiler/sema_expr.c | 125 +++++++++++++++--- src/version.h | 2 +- test/test_suite/bitstruct/bitstruct_init.c3 | 3 +- .../bitstruct/embedded_bitstruct.c3t | 87 ++++++++++++ test/test_suite/unicode/commenting-out.c3 | 10 ++ 12 files changed, 301 insertions(+), 34 deletions(-) create mode 100644 test/test_suite/bitstruct/embedded_bitstruct.c3t create mode 100644 test/test_suite/unicode/commenting-out.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 084056d34..87d1b2d5c 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -95,6 +95,7 @@ const char *decl_to_name(Decl *decl) case VARDECL_PARAM: return "parameter"; case VARDECL_MEMBER: + case VARDECL_BITMEMBER: return "member"; case VARDECL_PARAM_CT: return "compile time parameter"; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b8145ed63..bb4fbd1b0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1774,6 +1774,7 @@ static inline bool decl_var_is_assignable(Decl *decl) case VARDECL_LOCAL_CT_TYPE: case VARDECL_UNWRAPPED: return true; + case VARDECL_BITMEMBER: case VARDECL_CONST: case VARDECL_MEMBER: case VARDECL_PARAM_EXPR: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 2ec670885..1c0eaec77 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -582,19 +582,20 @@ typedef enum typedef enum { VARDECL_CONST = 0, - VARDECL_GLOBAL = 1, - VARDECL_LOCAL = 2, - VARDECL_PARAM = 3, - VARDECL_MEMBER = 4, - VARDECL_PARAM_CT = 5, - VARDECL_PARAM_CT_TYPE = 6, - VARDECL_PARAM_REF = 7, - VARDECL_PARAM_EXPR = 8, - VARDECL_LOCAL_CT = 9, - VARDECL_LOCAL_CT_TYPE = 10, - VARDECL_UNWRAPPED = 11, - VARDECL_ERASE = 12, - VARDECL_REWRAPPED = 13, + VARDECL_GLOBAL, + VARDECL_LOCAL, + VARDECL_PARAM, + VARDECL_MEMBER, + VARDECL_BITMEMBER, + VARDECL_PARAM_CT, + VARDECL_PARAM_CT_TYPE, + VARDECL_PARAM_REF, + VARDECL_PARAM_EXPR, + VARDECL_LOCAL_CT, + VARDECL_LOCAL_CT_TYPE, + VARDECL_UNWRAPPED, + VARDECL_ERASE, + VARDECL_REWRAPPED, } VarDeclKind; typedef enum diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 8ea04c938..d84152447 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -1681,6 +1681,47 @@ void lexer_init_with_file(Lexer *lexer, File *file) lexer->current_line = 1; lexer->line_start = lexer->current; lexer->lexer_index = file->token_start_id; + const unsigned char *check = (const unsigned char *)lexer->current; + unsigned c; + int balance = 0; + while ((c = *(check++)) != '\0') + { + if (c != 0xE2) continue; + unsigned char type = check[1]; + switch (check[0]) + { + case 0x80: + if (type == 0xAC) + { + balance--; + if (balance < 0) goto DONE; + } + if (type >= 0xAA && type <= 0xAE) + { + balance++; + } + break; + case 0x81: + if (type >= 0xA6 && type <= 0xA8) + { + balance++; + } + else if (type == 0xA9) + { + balance--; + if (balance < 0) goto DONE; + } + break; + default: + break; + } + } +DONE: + if (balance != 0) + { + add_error_token(lexer, "Invalid encoding - Unbalanced bidirectional markers."); + return; + } while(1) { if (!lexer_scan_token_inner(lexer, LEX_NORMAL)) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 678fd78e0..cd5a4ad12 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -601,6 +601,36 @@ static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *par } while (found != member); } +static void llvm_emit_bitstruct_member(GenContext *c, BEValue *value, Decl *parent, Decl *member) +{ + assert(member->resolve_status == RESOLVE_DONE); + Decl *found = NULL; + do + { + int index = find_member_index(parent, member); + assert(index > -1); + found = parent->strukt.members[index]; + switch (parent->type->canonical->type_kind) + { + case TYPE_UNION: + llvm_value_addr(c, value); + llvm_value_set_address_align(value, + llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), + found->type, + value->alignment); + break; + case TYPE_STRUCT: + llvm_value_struct_gep(c, value, value, index); + break; + case TYPE_BITSTRUCT: + break; + default: + UNREACHABLE + } + parent = found; + } while (found != member); +} + static LLVMValueRef llvm_emit_bswap(GenContext *c, LLVMValueRef value) { if (LLVMIsConstant(value)) @@ -937,8 +967,10 @@ static inline void llvm_emit_bitaccess(GenContext *c, BEValue *be_value, Expr *e Expr *parent = expr->access_expr.parent; llvm_emit_expr(c, be_value, parent); + Decl *member = expr->access_expr.ref; assert(be_value && be_value->type); + llvm_emit_bitstruct_member(c, be_value, type_flatten(parent->type)->decl, member); llvm_extract_bitvalue(c, be_value, parent, expr->access_expr.ref); } @@ -4502,6 +4534,7 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, case VARDECL_UNWRAPPED: case VARDECL_REWRAPPED: case VARDECL_ERASE: + case VARDECL_BITMEMBER: UNREACHABLE case VARDECL_PARAM_REF: { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 9bf8889ef..77099de35 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1406,7 +1406,7 @@ static inline bool parse_bitstruct_body(Context *context, Decl *decl) SEMA_TOKEN_ERROR(context->tok, "Expected a field name at this position."); return false; } - Decl *member_decl = decl_new_var(context->prev_tok, type, VARDECL_MEMBER, VISIBLE_LOCAL); + Decl *member_decl = decl_new_var(context->prev_tok, type, VARDECL_BITMEMBER, VISIBLE_LOCAL); CONSUME_OR(TOKEN_COLON, false); ASSIGN_EXPR_ELSE(member_decl->var.start, parse_constant_expr(context), false); if (try_consume(context, TOKEN_DOTDOT)) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index fe38e155d..b28c848e4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -484,6 +484,7 @@ static inline bool sema_analyse_bitstruct_member(Context *context, Decl *decl, u return false; } } + member->resolve_status = RESOLVE_DONE; return true; } @@ -1276,6 +1277,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: case VARDECL_UNWRAPPED: @@ -1310,6 +1312,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: case VARDECL_UNWRAPPED: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 684c4dbda..a6e3c586c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -129,6 +129,7 @@ static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind return decl->var.is_static; case VARDECL_PARAM: case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_PARAM_CT: case VARDECL_PARAM_CT_TYPE: case VARDECL_PARAM_REF: @@ -468,6 +469,7 @@ bool expr_is_ltype(Expr *expr) return true; case VARDECL_CONST: case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_PARAM_CT: case VARDECL_PARAM_CT_TYPE: case VARDECL_PARAM_EXPR: @@ -582,6 +584,7 @@ static inline bool sema_cast_ident_rvalue(Context *context, Expr *expr) case VARDECL_LOCAL: case VARDECL_UNWRAPPED: return true; + case VARDECL_BITMEMBER: case VARDECL_MEMBER: SEMA_ERROR(expr, "Expected '%s' followed by a method call or property.", decl->name); return expr_poison(expr); @@ -1406,6 +1409,7 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: case VARDECL_UNWRAPPED: @@ -2750,7 +2754,7 @@ CHECK_DEEPER: } // Transform bitstruct access to expr_bitaccess. - if (decl->decl_kind == DECL_BITSTRUCT) + if (member->var.kind == VARDECL_BITMEMBER) { expr->expr_kind = EXPR_BITACCESS; } @@ -2759,6 +2763,7 @@ CHECK_DEEPER: expr->access_expr.parent = current_parent; expr->type = type_get_opt_fail(member->type, failable); expr->access_expr.ref = member; + return true; } @@ -2838,12 +2843,13 @@ static int64_t sema_analyse_designator_index(Context *context, Expr *index) return index_val; } -static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error, ArrayIndex *max_index) +static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error, ArrayIndex *max_index, Decl **member_ptr) { Type *type_flattened = type_flatten(type); DesignatorElement *element = elements[*curr_index]; if (element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE) { + *member_ptr = NULL; ByteSize len; Type *base; switch (type_flattened->type_kind) @@ -2909,26 +2915,30 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE return NULL; } Decl *member = sema_resolve_element_for_name(type_flattened->decl->strukt.members, elements, curr_index); + *member_ptr = member; if (!member) return NULL; return member->type; } -static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr *expr, ArrayIndex *max_index) +static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr *expr, ArrayIndex *max_index, Decl **member_ptr) { DesignatorElement **path = expr->designator_expr.path; // Walk down into this path bool is_constant = true; bool did_report_error = false; + *member_ptr = NULL; for (unsigned i = 0; i < vec_size(path); i++) { - Type *new_current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error, i == 0 ? max_index : NULL); + Decl *member_found; + Type *new_current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error, i == 0 ? max_index : NULL, &member_found); if (!new_current) { if (!did_report_error) SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current)); return NULL; } current = new_current; + *member_ptr = member_found; } return current; } @@ -3231,17 +3241,23 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass { Expr **init_expressions = initializer->designated_init_list; Type *original = assigned->canonical; - bool is_structlike = type_is_structlike(assigned->canonical); - + bool is_bitstruct = original->type_kind == TYPE_BITSTRUCT; + bool is_structlike = type_is_structlike(original) || is_bitstruct; ArrayIndex max_index = -1; bool failable = false; VECEACH(init_expressions, i) { Expr *expr = init_expressions[i]; - Type *result = sema_expr_analyse_designator(context, original, expr, &max_index); + Decl *member; + Type *result = sema_expr_analyse_designator(context, original, expr, &max_index, &member); if (!result) return false; Expr *value = expr->designator_expr.value; if (!sema_analyse_expr_rhs(context, result, value, true)) return false; + DesignatorElement *element = VECLAST(expr->designator_expr.path); + if (member && member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) + { + if (!sema_bit_assignment_check(value, member)) return false; + } failable = failable || IS_FAILABLE(value); expr->resolve_status = RESOLVE_DONE; } @@ -3261,6 +3277,32 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass return true; } +static int decl_count_elements(Decl *structlike) +{ + int elements = 0; + Decl **members = structlike->strukt.members; + VECEACH(members, i) + { + Decl *member = members[i]; + if (member->decl_kind != DECL_VAR && !member->name) + { + elements += decl_count_elements(member); + continue; + } + elements++; + } + return elements; +} + +static inline void not_enough_elements(Expr *initializer, int element) +{ + if (element == 0) + { + SEMA_ERROR(initializer, "The initializer is missing elements."); + return; + } + SEMA_ERROR(initializer->initializer_list[element - 1], "Too few elements in initializer, there should be elements after this one."); +} /** * Perform analysis for a plain initializer, that is one initializing all fields. * @return true if analysis succeeds. @@ -3298,31 +3340,76 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, } } // 3. Loop through all elements. - VECEACH(elements, i) + int max_loop = MAX(size, expected_members); + for (unsigned i = 0; i < max_loop; i++) { // 4. Check if we exceeded the list of elements in the struct/union. // This way we can check the other elements which might help the // user pinpoint where they put the double elements. - Expr *element = elements[i]; if (i >= expected_members) { - SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members); + assert(i < size); + SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); return false; } - // 5. We know the required type, so resolve the expression. + // 5. We might have anonymous members + Decl *member = members[i]; + if (member->decl_kind != DECL_VAR && !member->name) + { + int sub_element_count = decl_count_elements(members[i]); + if (!sub_element_count) + { + vec_add(initializer->initializer_list, NULL); + for (int j = size - 1; j > i; j--) + { + initializer->initializer_list[j] = initializer->initializer_list[j - 1]; + } + Expr *new_initializer = expr_new(EXPR_INITIALIZER_LIST, initializer->span); + ConstInitializer *empty = CALLOCS(ConstInitializer); + empty->kind = CONST_INIT_ZERO; + empty->type = member->type; + expr_set_as_const_list(new_initializer, empty); + initializer->initializer_list[i] = new_initializer; + size += 1; + continue; + } + if (i >= size) + { + not_enough_elements(initializer, i); + return false; + } + Expr *new_initializer = expr_new(EXPR_INITIALIZER_LIST, elements[i]->span); + int max_index_to_copy = MIN(i + sub_element_count, size); + for (int j = i; j < max_index_to_copy; j++) + { + vec_add(new_initializer->initializer_list, elements[j]); + } + int reduce_by = max_index_to_copy - i - 1; + size -= reduce_by; + max_loop = MAX(size, expected_members); + assert(size <= vec_size(initializer->initializer_list)); + vec_resize(initializer->initializer_list, size); + elements = initializer->initializer_list; + elements[i] = new_initializer; + } + if (i >= size) + { + not_enough_elements(initializer, i); + } + Expr *element = elements[i]; + // 6. We know the required type, so resolve the expression. if (!sema_analyse_expr_rhs(context, members[i]->type, element, true)) return false; - if (is_bitstruct && !sema_bit_assignment_check(element, members[i])) return false; + if (member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) + { + if (!sema_bit_assignment_check(element, members[i])) return false; + } failable = failable || IS_FAILABLE(element); } assert(initializer->type); if (failable) initializer->type = type_get_failable(initializer->type); // 6. There's the case of too few values as well. Mark the last field as wrong. - if (expected_members > size) - { - SEMA_ERROR(elements[size - 1], "Too few elements in initializer, there should be elements after this one."); - return false; - } + assert(expected_members <= size); if (expr_is_constant_eval(initializer, CONSTANT_EVAL_ANY)) { @@ -3348,6 +3435,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, // 7. Done! return true; + + } @@ -4798,6 +4887,7 @@ static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl) // May not be reached due to EXPR_CT_IDENT being handled elsewhere. UNREACHABLE; case VARDECL_MEMBER: + case VARDECL_BITMEMBER: case VARDECL_UNWRAPPED: case VARDECL_REWRAPPED: case VARDECL_ERASE: @@ -5792,6 +5882,7 @@ static inline bool decl_is_local(Decl *decl) || kind == VARDECL_LOCAL_CT || kind == VARDECL_PARAM_REF || kind == VARDECL_PARAM_EXPR + || kind == VARDECL_BITMEMBER || kind == VARDECL_MEMBER; } diff --git a/src/version.h b/src/version.h index 4ebc7f619..43a3c13c8 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A239" \ No newline at end of file +#define COMPILER_VERSION "A240" \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_init.c3 b/test/test_suite/bitstruct/bitstruct_init.c3 index a8c6ae9fc..ebb316d4c 100644 --- a/test/test_suite/bitstruct/bitstruct_init.c3 +++ b/test/test_suite/bitstruct/bitstruct_init.c3 @@ -1,4 +1,3 @@ -// #skip module foo; bitstruct Foo : uint @@ -33,7 +32,7 @@ fn void testNested() Bar2 b3 = { 1, 3 }; Bar2 b4 = { .x = 123, .z = 3 }; Bar2 b5 = { .x = 123, .z = 4 }; // #error: would be truncated - Bar2 b6 = { 1, 3 }; // #error: would be truncated + Bar2 b6 = { 1, 4 }; // #error: would be truncated Bar b7 = { 3, { 4 } }; // #error: would be truncated Bar b8 = { .x = 3, .baz.x = 4 }; // #error: would be truncated diff --git a/test/test_suite/bitstruct/embedded_bitstruct.c3t b/test/test_suite/bitstruct/embedded_bitstruct.c3t new file mode 100644 index 000000000..7cb21407b --- /dev/null +++ b/test/test_suite/bitstruct/embedded_bitstruct.c3t @@ -0,0 +1,87 @@ +// #target: x64-darwin + +module foo; + +struct Bar +{ + int x; + struct + { + struct + { + int y; + } + struct + {} + union + { + struct { } + } + } + bitstruct : uint + { + int ww : 2..10; + } +} + +struct Foo +{ + struct + { + struct + { + int x; + } + struct + { + int y; + } + int z; + } + int w; +} +extern fn void printf(char*, ...); + +fn void main() +{ + Bar b = { 1, 2, -5 }; + printf("%d %d\n", b.y, b.ww); + Foo f = { 5, 6, 7, 8 }; + printf("%d %d %d %d\n", f.x, f.y, f.z, f.w); +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %b = alloca %Bar, align 4 + %f = alloca %Foo, align 4 + %0 = bitcast %Bar* %b to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Bar* @.__const to i8*), i32 12, i1 false) + %1 = getelementptr inbounds %Bar, %Bar* %b, i32 0, i32 1 + %2 = getelementptr inbounds %anon, %anon* %1, i32 0, i32 0 + %3 = getelementptr inbounds %anon.0, %anon.0* %2, i32 0, i32 0 + %4 = load i32, i32* %3, align 4 + %5 = getelementptr inbounds %Bar, %Bar* %b, i32 0, i32 2 + %6 = load i32, i32* %5, align 4 + %7 = shl i32 %6, 21 + %8 = ashr i32 %7, 23 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0), i32 %4, i32 %8) + %9 = bitcast %Foo* %f to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %9, i8* align 4 bitcast (%Foo* @.__const.8 to i8*), i32 16, i1 false) + %10 = getelementptr inbounds %Foo, %Foo* %f, i32 0, i32 0 + %11 = getelementptr inbounds %anon.4, %anon.4* %10, i32 0, i32 0 + %12 = getelementptr inbounds %anon.5, %anon.5* %11, i32 0, i32 0 + %13 = load i32, i32* %12, align 4 + %14 = getelementptr inbounds %Foo, %Foo* %f, i32 0, i32 0 + %15 = getelementptr inbounds %anon.4, %anon.4* %14, i32 0, i32 1 + %16 = getelementptr inbounds %anon.6, %anon.6* %15, i32 0, i32 0 + %17 = load i32, i32* %16, align 4 + %18 = getelementptr inbounds %Foo, %Foo* %f, i32 0, i32 0 + %19 = getelementptr inbounds %anon.4, %anon.4* %18, i32 0, i32 2 + %20 = load i32, i32* %19, align 4 + %21 = getelementptr inbounds %Foo, %Foo* %f, i32 0, i32 1 + %22 = load i32, i32* %21, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.9, i32 0, i32 0), i32 %13, i32 %17, i32 %20, i32 %22) + ret void +} diff --git a/test/test_suite/unicode/commenting-out.c3 b/test/test_suite/unicode/commenting-out.c3 new file mode 100644 index 000000000..2c9a908db --- /dev/null +++ b/test/test_suite/unicode/commenting-out.c3 @@ -0,0 +1,10 @@ +// #error: Invalid encoding - Unbalanced bidirectional markers. +int main() { + bool isAdmin = false; + /*‮ } ⁦if (isAdmin)⁩ ⁦ begin admins only */ + printf("You are an admin.\n"); + /* end admins only ‮ { ⁦*/ + return 0; +} + +