Updates to bitstruct

This commit is contained in:
Christoffer Lerno
2021-11-08 22:01:21 +01:00
committed by Christoffer Lerno
parent 15f902579b
commit 4662133893
12 changed files with 301 additions and 34 deletions

View File

@@ -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";

View File

@@ -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:

View File

@@ -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

View File

@@ -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))

View File

@@ -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:
{

View File

@@ -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))

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "A239"
#define COMPILER_VERSION "A240"

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;
}