Work on unions and anonymous structs/unions.

This commit is contained in:
Christoffer Lerno
2020-04-06 23:48:51 +02:00
parent 400c38b95b
commit 96c8c77e89
9 changed files with 265 additions and 143 deletions

View File

@@ -1,14 +1,15 @@
Things missing:
* Attributes
- All types: @noreflect
- All types: @noreflect, @deprecated
- Struct: @packed, @aligned, @opaque
- Enums: @distinct, @noreflect
- Unions: @packed, @aligned, @opaque
- Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention()
- Calls: @noinline
- Calls: @noinline, @inline
- Variables, parameters: @unused
- Constants, globals: @unused, @used, @section
- Labels: @unused
* Designated initializer
- Array initializer
@@ -17,6 +18,7 @@ Things missing:
* Initializers
- Array initializers
- Union initializers
- Initializers with anonymous members
* Asserts
- assert, $assert
@@ -36,7 +38,10 @@ Things missing:
- Bitstruct
- Enumset
- Typeid
- Union usage
* Struct / union
- Cast to union?
- Structural typed anonymous structs and casts to them.
* Expressions
- Disallow x >= 0 and x < 0 on unsigned types unless in a macro.

View File

@@ -106,24 +106,67 @@ struct TestStruct2
}
union TestUnion
{
int a;
double f;
TestStruct2 e;
}
union SimpleUnion
{
int a;
double f;
}
func TestStruct structTest(int i)
struct AnonStruct
{
int a;
struct sune
{
int b;
int c;
}
struct
{
int b1;
int c1;
}
union
{
int b2;
int c2;
}
int x;
}
TestUnion tu;
/* TestStruct foo = { i };
TestStruct foo2 = { a = i };*/
func void testAnonStruct()
{
AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 };
s.sune.b = 1;
s.b1 = 2;
s.b2 = 3;
s.c2 = 4;
}
func void testUnion()
{
SimpleUnion s;
s.a = 1;
s.f = 1.0;
//s = { f = 1.0 };
TestUnion tu = { e = TestStruct2 { c = 1 } };
tu.e = TestStruct2 { c = 1 };
}
func TestStruct2 structTest(int i)
{
TestStruct foo = { i };
TestStruct foo2 = { a = i };
TestStruct foo3 = TestStruct { i };
return foo3;
/* TestStruct2 bar = { c = 2 };
TestStruct2 bar = { c = 2 };
int x = 3 * i;
TestStruct2 bar2 = { b.a = x, a.a = x + 1 };
return bar2;*/
return bar2;
}
func void enumInferenceTest()
{

View File

@@ -8,7 +8,6 @@ static void fprint_asts_recursive(FILE *file, Ast **asts, int indent);
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility)
{
assert(name.string);
Decl *decl = CALLOCS(Decl);
decl->decl_kind = decl_kind;
decl->name_span = name.span;

View File

@@ -227,6 +227,7 @@ typedef struct
typedef struct
{
uint32_t abi_alignment;
uint32_t id;
uint64_t size;
Decl **members;
} StructDecl;
@@ -237,7 +238,11 @@ typedef struct _VarDecl
unsigned id : 16;
VarDeclKind kind : 3;
TypeInfo *type_info;
Expr *init_expr;
union
{
Expr *init_expr;
Decl *parent;
};
void *backend_ref;
void *backend_debug_ref;
} VarDecl;
@@ -364,7 +369,11 @@ typedef struct _Decl
{
struct
{
Decl** method_functions;
union
{
Decl* parent_struct;
Decl** method_functions;
};
union
{
ErrorDecl error;
@@ -460,8 +469,6 @@ typedef struct
Token sub_element;
Decl *ref;
};
// TODO cleanup
int index;
} ExprAccess;
typedef struct
@@ -997,7 +1004,6 @@ bool cast_to_runtime(Expr *expr);
void cast_to_smallest_runtime(Expr *expr);
void llvm_codegen(Context *context);
void llvm_set_struct_size_alignment(Decl *decl);
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr);
@@ -1182,6 +1188,20 @@ static inline Type *type_reduced(Type *type)
return canonical;
}
static inline bool type_is_structlike(Type *type)
{
assert(type->canonical = type);
switch (type->type_kind)
{
case TYPE_UNION:
case TYPE_STRUCT:
return true;
default:
return false;
}
}
static inline Type *type_reduced_from_expr(Expr *expr)
{
return type_reduced(expr->type);

View File

@@ -54,10 +54,38 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
}
}
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
{
unsigned index;
Decl *current_parent;
if (decl_is_struct_type(member))
{
index = member->strukt.id;
current_parent = member->parent_struct;
}
else
{
index = member->var.id;
current_parent = member->var.parent;
}
assert(current_parent);
if (parent != current_parent)
{
value = gencontext_emit_member_addr(context, value, parent, current_parent);
}
if (current_parent->decl_kind == DECL_UNION)
{
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), "");
}
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, "");
}
static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr)
{
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
return LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
Expr *parent = expr->access_expr.parent;
LLVMValueRef value = gencontext_emit_address(context, parent);
Decl *member = expr->access_expr.ref;
return gencontext_emit_member_addr(context, value, parent->type->canonical->decl, member);
}
LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr)
@@ -177,6 +205,70 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *
return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical);
}
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
{
LLVMTypeRef type = llvm_type(expr->type);
LLVMValueRef ref = gencontext_emit_alloca(context, type, "literal");
if (expr->expr_initializer.init_type == INITIALIZER_ZERO)
{
LLVMBuildMemSet(context->builder,
ref,
LLVMConstInt(llvm_type(type_byte), 0, false),
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false),
expr->type->decl->strukt.abi_alignment);
return ref;
}
Expr **elements = expr->expr_initializer.initializer_expr;
bool is_union = expr->type->canonical->type_kind == TYPE_UNION;
if (expr->expr_initializer.init_type == INITIALIZER_NORMAL)
{
assert(!is_union);
VECEACH(elements, i)
{
Expr *element = elements[i];
LLVMValueRef init_value = gencontext_emit_expr(context, element);
LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, "");
LLVMBuildStore(context->builder, init_value, subref);
}
return ref;
}
// Clear the temp.
LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment);
VECEACH(elements, i)
{
Expr *element = elements[i];
LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value);
Decl *parent = expr->type->decl;
DesignatedInitPath *path = &element->designated_init_expr.path;
LLVMValueRef subref = ref;
assert(element->expr_kind == EXPR_DESIGNATED_INIT);
// If this is a union, we down a step to begin with.
if (is_union)
{
subref = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(path->decl->type), 0), "unioncast");
parent = path->decl;
path = path->sub_path;
}
while (path)
{
subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access");
parent = path->decl;
path = path->sub_path;
}
LLVMBuildStore(context->builder, sub_value, subref);
}
return ref;
}
static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff)
{
Type *type = type_reduced_from_expr(expr);
@@ -580,10 +672,6 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
if (binary_op == BINARYOP_ASSIGN)
{
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
if (expr->binary_expr.right->expr_kind == EXPR_INITIALIZER_LIST)
{
return gencontext_emit_initialization_from_expr(context, addr, expr->binary_expr.right);
}
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
LLVMBuildStore(context->builder, value, addr);
return value;
@@ -725,7 +813,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr
{
// Improve, add string description to the access?
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, expr->access_expr.ref->var.id, "");
return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, "");
}
@@ -741,54 +829,6 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
{
LLVMTypeRef type = llvm_type(expr->type);
LLVMValueRef value = LLVMGetUndef(type);
if (expr->expr_initializer.init_type == INITIALIZER_ZERO)
{
LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp");
value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment);
return value;
}
Expr **elements = expr->expr_initializer.initializer_expr;
if (expr->expr_initializer.init_type == INITIALIZER_NORMAL)
{
VECEACH(elements, i)
{
LLVMValueRef init_value = gencontext_emit_expr(context, elements[i]);
value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal");
}
return value;
}
LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp");
value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment);
VECEACH(elements, i)
{
Expr *element = elements[i];
LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value);
Decl *parent = expr->type->decl;
DesignatedInitPath *path = &element->designated_init_expr.path;
LLVMValueRef subref = ref;
assert(element->expr_kind == EXPR_DESIGNATED_INIT);
while (path)
{
subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access");
parent = path->decl;
path = path->sub_path;
}
LLVMBuildStore(context->builder, sub_value, subref);
}
return ref;
}
static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr)
{

View File

@@ -52,20 +52,28 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
}
case DECL_UNION:
{
LLVMTypeRef max_type = NULL;
Decl *max_type = NULL;
unsigned long long max_size = 0;
VECEACH(decl->strukt.members, i)
{
LLVMTypeRef type = llvm_get_type(context, decl->strukt.members[i]->type);
unsigned long long size = LLVMStoreSizeOfType(target_data_layout(), type);
Decl *member = decl->strukt.members[i];
unsigned size = type_size(member->type);
if (size > max_size || !max_type)
{
max_size = size;
max_type = type;
max_type = member;
}
}
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name);
LLVMStructSetBody(type, &max_type, 1, false);
if (max_type)
{
LLVMTypeRef type_ref = llvm_get_type(context, max_type->type);
LLVMStructSetBody(type, &type_ref, 1, false);
}
else
{
LLVMStructSetBody(type, NULL, 0, true);
}
return type;
}
case DECL_ENUM:
@@ -222,9 +230,3 @@ LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
return llvm_get_type(context->context, type);
}
void llvm_set_struct_size_alignment(Decl *decl)
{
LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type);
decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type);
decl->strukt.abi_alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type);
}

View File

@@ -989,11 +989,11 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
Decl *member;
if (context->next_tok.type != TOKEN_IDENT)
{
Token name_replacement = context->tok;
name_replacement.string = NULL;
member = decl_new_with_type(name_replacement, decl_kind, parent->visibility);
advance(context);
}
Token name_replacement = context->tok;
name_replacement.string = NULL;
member = decl_new_with_type(name_replacement, decl_kind, parent->visibility);
advance(context);
}
else
{
advance(context);
@@ -1010,6 +1010,8 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
advance_and_verify(context, TOKEN_IDENT);
}
if (!parse_attributes(context, member)) return false;
member->parent_struct = parent;
member->strukt.id = vec_size(parent->strukt.members);
parent->strukt.members = VECADD(parent->strukt.members, member);
if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent))
{
@@ -1036,6 +1038,7 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
unsigned index = vec_size(parent->strukt.members);
parent->strukt.members = VECADD(parent->strukt.members, member);
member->var.id = index;
member->var.parent = parent;
advance(context);
if (context->tok.type != TOKEN_COMMA) break;
}

View File

@@ -38,6 +38,53 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
}
static inline void sema_set_struct_size(Decl *decl)
{
// TODO packed
uint64_t size = 0;
uint64_t alignment = 0;
VECEACH(decl->strukt.members, i)
{
Decl *member = decl->strukt.members[i];
Type *canonical = member->type->canonical;
uint64_t member_size = type_size(canonical);
uint64_t member_alignment = type_abi_alignment(canonical);
assert(member_size > 0);
// Add padding.
if (member_alignment && (size % member_alignment))
{
size += member_alignment - size % member_alignment;
}
// Add size.
size += member_size;
if (member_alignment > alignment) alignment = member_alignment;
}
decl->strukt.abi_alignment = alignment;
if (alignment && size % alignment)
{
size += alignment - size % alignment;
}
decl->strukt.size = size;
}
static inline void sema_set_union_size(Decl *decl)
{
uint64_t size = 0;
uint64_t alignment = 0;
VECEACH(decl->strukt.members, i)
{
Decl *member = decl->strukt.members[i];
Type *canonical = member->type->canonical;
uint64_t member_size = type_size(canonical);
uint64_t member_alignment = type_abi_alignment(canonical);
if (member_size > size) size = member_size;
if (member_alignment > alignment) alignment = member_alignment;
}
decl->strukt.abi_alignment = alignment;
decl->strukt.size = size;
}
static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
{
assert(decl->resolve_status == RESOLVE_NOT_DONE);
@@ -63,12 +110,19 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
decl_poison(decl);
}
}
if (decl->decl_kind == DECL_UNION)
{
sema_set_union_size(decl);
}
else
{
sema_set_struct_size(decl);
}
DEBUG_LOG("Analysis complete.");
return decl_ok(decl);
}
assert(decl->decl_kind == DECL_VAR);
assert(decl->var.kind == VARDECL_MEMBER);
assert(!decl->var.init_expr);
if (!sema_resolve_type_info(context, decl->var.type_info))
{
decl_poison(decl);
@@ -92,7 +146,7 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl)
decl_poison(decl);
continue;
}
if (!sema_analyse_struct_member(context, decl->strukt.members[i]))
if (!sema_analyse_struct_member(context, member))
{
if (decl_ok(decl))
{
@@ -433,51 +487,6 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl)
return true;
}
static inline void sema_set_struct_size(Decl *decl)
{
// TODO packed
uint64_t size = 0;
uint64_t alignment = 0;
VECEACH(decl->strukt.members, i)
{
Decl *member = decl->strukt.members[i];
Type *canonical = member->type->canonical;
uint64_t member_size = type_size(canonical);
uint64_t member_alignment = type_abi_alignment(canonical);
assert(member_size > 0);
// Add padding.
if (member_alignment && (size % member_alignment))
{
size += member_alignment - size % member_alignment;
}
// Add size.
size += member_size;
if (member_alignment > alignment) alignment = member_alignment;
}
decl->strukt.abi_alignment = alignment;
if (alignment && size % alignment)
{
size += alignment - size % alignment;
}
decl->strukt.size = size;
}
static inline void sema_set_union_size(Decl *decl)
{
uint64_t size = 0;
uint64_t alignment = 0;
VECEACH(decl->strukt.members, i)
{
Decl *member = decl->strukt.members[i];
Type *canonical = member->type->canonical;
uint64_t member_size = type_size(canonical);
uint64_t member_alignment = type_abi_alignment(canonical);
if (member_size > size) size = member_size;
if (member_alignment > alignment) alignment = member_alignment;
}
decl->strukt.abi_alignment = alignment;
decl->strukt.size = size;
}
bool sema_analyse_decl(Context *context, Decl *decl)
{

View File

@@ -335,17 +335,19 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp
}
static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int *index)
static Decl *strukt_recursive_search_member(Decl *strukt, const char *name)
{
VECEACH(strukt->strukt.members, i)
{
(*index)++;
Decl *member = strukt->strukt.members[i];
if (member->name == name) return member;
if (!member->name && decl_is_struct_type(member))
if (!member->name && type_is_structlike(member->type->canonical))
{
Decl *result = strukt_recursive_search_member(member, name, index);
if (result) return result;
Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name);
if (result)
{
return result;
}
}
}
return NULL;
@@ -380,8 +382,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
default:
UNREACHABLE
}
int index = -1;
Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string, &index);
Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string);
if (!member)
{
SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string);
@@ -397,7 +398,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
expr->access_expr.parent = deref;
}
expr->type = member->type;
expr->access_expr.index = index;
expr->access_expr.ref = member;
return true;
}