From: Brendan Hansen Date: Mon, 20 Jul 2020 18:49:18 +0000 (-0500) Subject: Added initial implementation of structs X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=325085c3325796fa89733ded847b1b983944457b;p=onyx.git Added initial implementation of structs --- diff --git a/.vimspector.json b/.vimspector.json index 404230c7..f64d90eb 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -6,7 +6,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/onyx", - "args": ["progs/arrays.onyx"], + "args": ["progs/structs.onyx"], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 14281d03..f33ef189 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -19,6 +19,7 @@ typedef struct AstArgument AstArgument; typedef struct AstAddressOf AstAddressOf; typedef struct AstDereference AstDereference; typedef struct AstArrayAccess AstArrayAccess; +typedef struct AstFieldAccess AstFieldAccess; typedef struct AstAssign AstAssign; typedef struct AstReturn AstReturn; @@ -32,6 +33,8 @@ typedef struct AstType AstType; typedef struct AstBasicType AstBasicType; typedef struct AstPointerType AstPointerType; typedef struct AstFunctionType AstFunctionType; +typedef struct AstStructType AstStructType; +typedef struct AstStructMember AstStructMember; typedef struct AstBinding AstBinding; typedef struct AstUse AstUse; @@ -71,8 +74,11 @@ typedef enum AstKind { Ast_Kind_Basic_Type, Ast_Kind_Pointer_Type, Ast_Kind_Function_Type, + Ast_Kind_Struct_Type, Ast_Kind_Type_End, + Ast_Kind_Struct_Member, + Ast_Kind_NumLit, Ast_Kind_StrLit, Ast_Kind_Param, @@ -83,6 +89,7 @@ typedef enum AstKind { Ast_Kind_Address_Of, Ast_Kind_Dereference, Ast_Kind_Array_Access, + Ast_Kind_Field_Access, Ast_Kind_If, Ast_Kind_For, @@ -97,17 +104,20 @@ typedef enum AstKind { // only 32-bits of flags to play with typedef enum AstFlags { // Top-level flags - Ast_Flag_Exported = BH_BIT(0), - Ast_Flag_Foreign = BH_BIT(1), - Ast_Flag_Const = BH_BIT(2), - Ast_Flag_Comptime = BH_BIT(3), + Ast_Flag_Exported = BH_BIT(0), + Ast_Flag_Foreign = BH_BIT(1), + Ast_Flag_Const = BH_BIT(2), + Ast_Flag_Comptime = BH_BIT(3), // Function flags - Ast_Flag_Inline = BH_BIT(8), - Ast_Flag_Intrinsic = BH_BIT(9), + Ast_Flag_Inline = BH_BIT(8), + Ast_Flag_Intrinsic = BH_BIT(9), // Expression flags - Ast_Flag_Expr_Ignored = BH_BIT(8), + Ast_Flag_Expr_Ignored = BH_BIT(8), + + // Type flags + Ast_Flag_Type_Is_Resolved = BH_BIT(8), } AstFlags; typedef enum UnaryOp { @@ -210,6 +220,7 @@ struct AstArgument { AstTyped_base; AstTyped *value; }; struct AstAddressOf { AstTyped_base; AstTyped *expr; }; struct AstDereference { AstTyped_base; AstTyped *expr; }; struct AstArrayAccess { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; }; +struct AstFieldAccess { AstTyped_base; AstTyped *expr; u64 offset; }; // Intruction Node struct AstReturn { AstNode_base; AstTyped* expr; }; @@ -243,13 +254,24 @@ struct AstIf { // without the 'next' member. This is because types // can't be in expressions so a 'next' thing // doesn't make sense. -#define AstType_members { AstKind kind; u32 flags; char* name; } +#define AstType_members { AstKind kind; u32 flags; OnyxToken* token; char* name; } #define AstType_base struct AstType_members; struct AstType AstType_members; struct AstBasicType { AstType_base; Type* type; }; struct AstPointerType { AstType_base; AstType* elem; }; struct AstFunctionType { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; }; +struct AstStructType { + AstType_base; + + bh_arr(AstStructMember *) members; + + // NOTE: Used to cache the actual type, since building + // a struct type is kind of complicated and should + // only happen once. + Type *stcache; +}; +struct AstStructMember { AstTyped_base; u64 offset; }; // Top level nodes struct AstBinding { AstTyped_base; AstNode* node; }; @@ -300,11 +322,14 @@ struct AstOverloadedFunction { // processed later down the pipeline. typedef enum EntityType { Entity_Type_Unknown, - Entity_Type_Function, - Entity_Type_Overloaded_Function, - Entity_Type_Global, + Entity_Type_Function_Header, + Entity_Type_Global_Header, + Entity_Type_Expression, Entity_Type_String_Literal, - Entity_Type_Expression + Entity_Type_Struct, + Entity_Type_Global, + Entity_Type_Overloaded_Function, + Entity_Type_Function, } EntityType; typedef struct Entity { @@ -316,6 +341,7 @@ typedef struct Entity { AstGlobal *global; AstTyped *expr; AstStrLit *strlit; + AstStructType *struct_type; }; } Entity; @@ -362,7 +388,8 @@ static inline b32 is_lval(AstNode* node) { return (node->kind == Ast_Kind_Local) || (node->kind == Ast_Kind_Global) || (node->kind == Ast_Kind_Dereference) - || (node->kind == Ast_Kind_Array_Access); + || (node->kind == Ast_Kind_Array_Access) + || (node->kind == Ast_Kind_Field_Access); } static inline b32 binop_is_assignment(AstBinaryOp* binop) { diff --git a/include/onyxmsgs.h b/include/onyxmsgs.h index 9c98c2d0..2af7b721 100644 --- a/include/onyxmsgs.h +++ b/include/onyxmsgs.h @@ -28,6 +28,8 @@ typedef enum MsgType { Msg_Type_Function_Return_Mismatch, Msg_Type_Function_Param_Mismatch, + Msg_Type_Duplicate_Member, + Msg_Type_Unresolved_Type, Msg_Type_Unresolved_Symbol, diff --git a/include/onyxtypes.h b/include/onyxtypes.h index 485a0d48..f7feda57 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -46,10 +46,16 @@ typedef struct TypeBasic { // NOTE: Forward declaration for some of the types below typedef struct Type Type; +typedef struct StructMember { + u64 offset; + Type *type; +} StructMember; + #define TYPE_KINDS \ TYPE_KIND(Basic, TypeBasic) \ TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; }) \ - TYPE_KIND(Function, struct { Type *return_type; u64 param_count; Type* params[]; }) + TYPE_KIND(Function, struct { Type *return_type; u64 param_count; Type* params[]; }) \ + TYPE_KIND(Struct, struct { char* name; u32 size; u32 mem_count; bh_table(StructMember) members; }) typedef enum TypeKind { Type_Kind_Invalid, @@ -85,13 +91,18 @@ extern Type basic_types[]; struct AstType; b32 types_are_compatible(Type* t1, Type* t2); +u32 type_size_of(Type* type); Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node); Type* type_make_pointer(bh_allocator alloc, Type* to); const char* type_get_name(Type* type); +u32 type_get_alignment_log2(Type* type); + +StructMember type_struct_lookup_member(Type* type, char* member); b32 type_is_pointer(Type* type); +b32 type_is_struct(Type* type); b32 type_is_bool(Type* type); #endif // #ifndef ONYX_TYPES diff --git a/onyx b/onyx index c3e5c62f..31c4540f 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/structs.onyx b/progs/structs.onyx new file mode 100644 index 00000000..55c676e0 --- /dev/null +++ b/progs/structs.onyx @@ -0,0 +1,129 @@ +use "progs/print_funcs" +use "progs/intrinsics" + +Vec3 :: struct { + x : f32; + y : f32; + z : f32; +} + +Foo :: struct { + v : Vec3; + i : i32; + l : i64; + f : f32; + d : f64; + foo : ^Foo; +} + +alloc :: proc (size: u32) -> rawptr { + heap_u32 :: __heap_start as ^u32; + + curr_offset := *heap_u32; + if curr_offset == 0 curr_offset = 8; + + *heap_u32 = curr_offset + size; + + return ((__heap_start as u32) + curr_offset) as rawptr; +} + +foo_make :: proc -> ^Foo { + return alloc(40) as ^Foo; +} + +foo_get :: proc (fooarr: ^Foo, i: i32) -> ^Foo { + return ^fooarr[i]; +} + +proc #export "main" { + // foo := foo_make(); + // other_foo := foo_make(); + // other_foo.i = 1234; + + // foo.foo = other_foo; + // print(foo.foo.i); + + foo1 := foo_make(); + foo1.v.y = 123.0f; + print(foo1.v.y); + + foo := alloc(40* 5) as ^Foo; + + for i: 1, 6 { + foo[i - 1].v.x = (i + 3) as f32; + foo[i - 1].v.y = (i + 4) as f32; + + foo[i - 1].i = i; + foo[i - 1].l = (i * 10) as i64; + foo[i - 1].f = (i * 100) as f32; + foo[i - 1].d = (i * 1000) as f64; + + foo[i - 1].foo = foo; + } + + print(foo[3].v.x); // 7 + print(foo[4].i); // 5 + print(foo[2].d); // 3000.0 + print(foo[3].f); // 400.0 + print(foo[0].l); // 10 + + print(foo as i32); + print((^(foo[3].l)) as i32); // 84 + + print(foo[2].foo[4].foo[3].f); // 400.0 + + foo_get(foo, 2).f = 1234.5f; + print(foo[2].f); + + print(1000000000000); + link_test(); +} + +Mut1 :: struct { + bar : i32; + other : Mut2; +} + +Mut2 :: struct { + foo : i32; + other : ^Foo; +} + +mut_func :: proc #export (mut: ^Mut1) -> ^Foo { + return mut.other.other; +} + + +Link :: struct { + data : i32; + other_data : i32; + next : ^Link; +} + +link_create :: proc (data: i32, parent: ^Link) -> ^Link { + link := alloc(12) as ^Link; + link.data = data; + link.other_data = 1234; + link.next = parent; + return link; +} + +link_print :: proc (start: ^Link) { + walker := start; + while (walker as i32) != 0 { + print(walker.next.data); + walker = walker.next; + } +} + +link_test :: proc #export "main2" { + dummy :: "H"; + + node1 := link_create(1, 0 as ^Link); + node2 := link_create(2, node1); + node3 := link_create(3, node2); + node4 := link_create(4, node3); + node5 := link_create(5, node4); + + link_print(node5); +} \ No newline at end of file diff --git a/src/onyx.c b/src/onyx.c index a2939441..a19f67aa 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -166,6 +166,10 @@ static ParseResults parse_source_file(CompilerState* compiler_state, bh_file_con return onyx_parse(&parser); } +static i32 sort_entities(const void* e1, const void* e2) { + return ((Entity *)e1)->type - ((Entity *)e2)->type; +} + static void merge_parse_results(CompilerState* compiler_state, ParseResults* results) { bh_arr_each(AstUse *, use_node, results->uses) { char* formatted_name = bh_aprintf( @@ -179,39 +183,66 @@ static void merge_parse_results(CompilerState* compiler_state, ParseResults* res bh_arr_each(AstBinding *, binding_node, results->bindings) bh_arr_push(compiler_state->prog_info.bindings, *binding_node); + Entity ent; bh_arr_each(AstNode *, node, results->nodes_to_process) { - Entity ent = { Entity_Type_Unknown }; - AstKind nkind = (*node)->kind; switch (nkind) { - case Ast_Kind_Function: + case Ast_Kind_Function: { + ent.type = Entity_Type_Function_Header; + ent.function = (AstFunction *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); + ent.type = Entity_Type_Function; ent.function = (AstFunction *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); break; + } - case Ast_Kind_Overloaded_Function: + case Ast_Kind_Overloaded_Function: { ent.type = Entity_Type_Overloaded_Function; ent.overloaded_function = (AstOverloadedFunction *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); break; + } + + case Ast_Kind_Global: { + ent.type = Entity_Type_Global_Header; + ent.global = (AstGlobal *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); - case Ast_Kind_Global: ent.type = Entity_Type_Global; ent.global = (AstGlobal *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); break; + } - case Ast_Kind_StrLit: + case Ast_Kind_StrLit: { ent.type = Entity_Type_String_Literal; ent.strlit = (AstStrLit *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); + break; + } + + case Ast_Kind_Struct_Type: { + ent.type = Entity_Type_Struct; + ent.struct_type = (AstStructType *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); break; + } - default: + default: { ent.type = Entity_Type_Expression; ent.expr = (AstTyped *) *node; + bh_arr_push(compiler_state->prog_info.entities, ent); break; + } } - - bh_arr_push(compiler_state->prog_info.entities, ent); } + + qsort(compiler_state->prog_info.entities, + bh_arr_length(compiler_state->prog_info.entities), + sizeof(Entity), + sort_entities); } static CompilerProgress process_source_file(CompilerState* compiler_state, char* filename) { diff --git a/src/onyxchecker.c b/src/onyxchecker.c index d5626dcb..0ff7d4fb 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -1,6 +1,7 @@ #define BH_DEBUG #include "onyxsempass.h" #include "onyxparser.h" +#include "onyxutils.h" #define CHECK(kind, ...) static b32 check_ ## kind (__VA_ARGS__) @@ -17,9 +18,11 @@ CHECK(expression, AstTyped* expr); CHECK(address_of, AstAddressOf* aof); CHECK(dereference, AstDereference* deref); CHECK(array_access, AstArrayAccess* expr); +CHECK(field_access, AstFieldAccess* field); CHECK(global, AstGlobal* global); CHECK(function, AstFunction* func); CHECK(overloaded_function, AstOverloadedFunction* func); +CHECK(struct, AstStructType* s_node); static inline void fill_in_type(AstTyped* node) { if (node->type == NULL) @@ -380,7 +383,8 @@ CHECK(address_of, AstAddressOf* aof) { if (check_expression(aof->expr)) return 1; if (aof->expr->kind != Ast_Kind_Array_Access - && aof->expr->kind != Ast_Kind_Dereference) { + && aof->expr->kind != Ast_Kind_Dereference + && aof->expr->kind != Ast_Kind_Field_Access) { onyx_message_add(Msg_Type_Literal, aof->token->pos, "cannot take the address of this"); @@ -434,7 +438,26 @@ CHECK(array_access, AstArrayAccess* aa) { } aa->type = aa->addr->type->Pointer.elem; - aa->elem_size = aa->type->Basic.size; + aa->elem_size = type_size_of(aa->type); + + return 0; +} + +CHECK(field_access, AstFieldAccess* field) { + if (check_expression(field->expr)) return 1; + + if (!type_is_struct(field->expr->type)) { + onyx_message_add(Msg_Type_Literal, + field->token->pos, + "expected expression of kind struct or pointer to struct"); + return 1; + } + + token_toggle_end(field->token); + StructMember smem = type_struct_lookup_member(field->expr->type, field->token->text); + field->offset = smem.offset; + field->type = smem.type; + token_toggle_end(field->token); return 0; } @@ -485,6 +508,7 @@ CHECK(expression, AstTyped* expr) { case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf *) expr); break; case Ast_Kind_Dereference: retval = check_dereference((AstDereference *) expr); break; case Ast_Kind_Array_Access: retval = check_array_access((AstArrayAccess *) expr); break; + case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess *) expr); break; case Ast_Kind_Global: if (expr->type == NULL) { @@ -657,6 +681,34 @@ CHECK(overloaded_function, AstOverloadedFunction* func) { return 0; } +CHECK(struct, AstStructType* s_node) { + bh_table(i32) mem_set; + bh_table_init(global_heap_allocator, mem_set, bh_arr_length(s_node->members)); + + bh_arr_each(AstStructMember *, member, s_node->members) { + token_toggle_end((*member)->token); + + if (bh_table_has(i32, mem_set, (*member)->token->text)) { + onyx_message_add(Msg_Type_Duplicate_Member, + (*member)->token->pos, + (*member)->token->text); + + token_toggle_end((*member)->token); + return 1; + } + + bh_table_put(i32, mem_set, (*member)->token->text, 1); + token_toggle_end((*member)->token); + } + + bh_table_free(mem_set); + + // NOTE: fills in the stcache + type_build_from_ast(semstate.allocator, (AstType *) s_node); + + return 0; +} + CHECK(node, AstNode* node) { switch (node->kind) { case Ast_Kind_Function: return check_function((AstFunction *) node); @@ -694,8 +746,15 @@ void onyx_type_check(ProgramInfo* program) { if (check_expression(entity->expr)) return; break; + case Entity_Type_Struct: + if (check_struct(entity->struct_type)) return; + break; + case Entity_Type_String_Literal: break; + case Entity_Type_Function_Header: break; + case Entity_Type_Global_Header: break; + default: DEBUG_HERE; break; } } diff --git a/src/onyxmsgs.c b/src/onyxmsgs.c index 2a979dff..2df639bf 100644 --- a/src/onyxmsgs.c +++ b/src/onyxmsgs.c @@ -26,6 +26,8 @@ static const char* msg_formats[] = { "returning '%s' from function that returns '%s'", "function '%b' expected type '%s' in position '%d', got '%s'", + "duplicate declaration of struct member '%s'", + "unable to resolve type for symbol '%b'", "unable to resolve symbol '%b'", }; diff --git a/src/onyxparser.c b/src/onyxparser.c index 03de4daa..fe9ec61d 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -14,22 +14,23 @@ static void unconsume_token(OnyxParser* parser); static b32 is_terminating_token(TokenType token_type); static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type); -static AstNumLit* parse_numeric_literal(OnyxParser* parser); -static AstTyped* parse_factor(OnyxParser* parser); -static AstTyped* parse_expression(OnyxParser* parser); -static AstIf* parse_if_stmt(OnyxParser* parser); -static AstWhile* parse_while_stmt(OnyxParser* parser); -static AstFor* parse_for_stmt(OnyxParser* parser); -static b32 parse_symbol_declaration(OnyxParser* parser, AstNode** ret); -static AstReturn* parse_return_statement(OnyxParser* parser); -static AstBlock* parse_block(OnyxParser* parser); -static AstNode* parse_statement(OnyxParser* parser); -static AstType* parse_type(OnyxParser* parser); -static AstLocal* parse_function_params(OnyxParser* parser); -static AstFunction* parse_function_definition(OnyxParser* parser); -static AstTyped* parse_global_declaration(OnyxParser* parser); -static AstTyped* parse_top_level_expression(OnyxParser* parser); -static AstNode* parse_top_level_statement(OnyxParser* parser); +static AstNumLit* parse_numeric_literal(OnyxParser* parser); +static AstTyped* parse_factor(OnyxParser* parser); +static AstTyped* parse_expression(OnyxParser* parser); +static AstIf* parse_if_stmt(OnyxParser* parser); +static AstWhile* parse_while_stmt(OnyxParser* parser); +static AstFor* parse_for_stmt(OnyxParser* parser); +static b32 parse_symbol_declaration(OnyxParser* parser, AstNode** ret); +static AstReturn* parse_return_statement(OnyxParser* parser); +static AstBlock* parse_block(OnyxParser* parser); +static AstNode* parse_statement(OnyxParser* parser); +static AstType* parse_type(OnyxParser* parser); +static AstStructType* parse_struct(OnyxParser* parser); +static AstLocal* parse_function_params(OnyxParser* parser); +static AstFunction* parse_function_definition(OnyxParser* parser); +static AstTyped* parse_global_declaration(OnyxParser* parser); +static AstTyped* parse_top_level_expression(OnyxParser* parser); +static AstNode* parse_top_level_statement(OnyxParser* parser); static void consume_token(OnyxParser* parser) { parser->prev = parser->curr; @@ -270,27 +271,44 @@ static AstTyped* parse_factor(OnyxParser* parser) { return NULL; } - while (parser->curr->type == '[') { - AstArrayAccess* aa_node = make_node(AstArrayAccess, Ast_Kind_Array_Access); - aa_node->token = expect_token(parser, '['); - aa_node->addr = retval; - aa_node->expr = parse_expression(parser); + while (parser->curr->type == '[' || parser->curr->type == Token_Type_Keyword_Cast + || parser->curr->type == '.') { - expect_token(parser, ']'); + switch ((u16) parser->curr->type) { + case '[': { + AstArrayAccess* aa_node = make_node(AstArrayAccess, Ast_Kind_Array_Access); + aa_node->token = expect_token(parser, '['); + aa_node->addr = retval; + aa_node->expr = parse_expression(parser); - retval = (AstTyped *) aa_node; - } + expect_token(parser, ']'); - while (parser->curr->type == Token_Type_Keyword_Cast) { + retval = (AstTyped *) aa_node; + break; + } - AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op); - cast_node->token = expect_token(parser, Token_Type_Keyword_Cast); - cast_node->type_node = parse_type(parser); - cast_node->operation = Unary_Op_Cast; - cast_node->expr = retval; - retval = (AstTyped *) cast_node; - } + case '.': { + consume_token(parser); + AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access); + field->token = expect_token(parser, Token_Type_Symbol); + field->expr = retval; + retval = (AstTyped *) field; + break; + } + + case Token_Type_Keyword_Cast: { + AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op); + cast_node->token = expect_token(parser, Token_Type_Keyword_Cast); + cast_node->type_node = parse_type(parser); + cast_node->operation = Unary_Op_Cast; + cast_node->expr = retval; + + retval = (AstTyped *) cast_node; + break; + } + } + } return retval; } @@ -691,9 +709,9 @@ static AstType* parse_type(OnyxParser* parser) { while (1) { if (parser->curr->type == '^') { - consume_token(parser); AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type); new->flags |= Basic_Flag_Pointer; + new->token = expect_token(parser, '^'); *next_insertion = (AstType *) new; next_insertion = &new->elem; } @@ -722,6 +740,28 @@ static AstType* parse_type(OnyxParser* parser) { return root; } +static AstStructType* parse_struct(OnyxParser* parser) { + AstStructType* s_node = make_node(AstStructType, Ast_Kind_Struct_Type); + s_node->token = expect_token(parser, Token_Type_Keyword_Struct); + + bh_arr_new(global_heap_allocator, s_node->members, 4); + + expect_token(parser, '{'); + while (parser->curr->type != '}') { + AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member); + mem->offset = 0; + + mem->token = expect_token(parser, Token_Type_Symbol); + expect_token(parser, ':'); + mem->type_node = parse_type(parser); + expect_token(parser, ';'); + + bh_arr_push(s_node->members, mem); + } + + return s_node; +} + // e // '(' (: ,?)* ')' static AstLocal* parse_function_params(OnyxParser* parser) { @@ -935,6 +975,9 @@ static AstTyped* parse_top_level_expression(OnyxParser* parser) { else if (parser->curr->type == Token_Type_Keyword_Global) { return parse_global_declaration(parser); } + else if (parser->curr->type == Token_Type_Keyword_Struct) { + return (AstTyped *) parse_struct(parser); + } else { return parse_expression(parser); } @@ -944,54 +987,58 @@ static AstTyped* parse_top_level_expression(OnyxParser* parser) { // :: static AstNode* parse_top_level_statement(OnyxParser* parser) { switch (parser->curr->type) { - case Token_Type_Keyword_Use: - { - AstUse* use_node = make_node(AstUse, Ast_Kind_Use); - use_node->token = expect_token(parser, Token_Type_Keyword_Use); - use_node->filename = expect_token(parser, Token_Type_Literal_String); + case Token_Type_Keyword_Use: { + AstUse* use_node = make_node(AstUse, Ast_Kind_Use); + use_node->token = expect_token(parser, Token_Type_Keyword_Use); + use_node->filename = expect_token(parser, Token_Type_Literal_String); - return (AstNode *) use_node; - } + return (AstNode *) use_node; + } case Token_Type_Keyword_Proc: parse_top_level_expression(parser); return NULL; - case Token_Type_Symbol: - { - OnyxToken* symbol = parser->curr; - consume_token(parser); + case Token_Type_Symbol: { + OnyxToken* symbol = parser->curr; + consume_token(parser); - expect_token(parser, ':'); - expect_token(parser, ':'); + expect_token(parser, ':'); + expect_token(parser, ':'); - AstTyped* node = parse_top_level_expression(parser); + AstTyped* node = parse_top_level_expression(parser); - if (node->kind == Ast_Kind_Function) { - AstFunction* func = (AstFunction *) node; + if (node->kind == Ast_Kind_Function) { + AstFunction* func = (AstFunction *) node; - if (func->exported_name == NULL) - func->exported_name = symbol; + if (func->exported_name == NULL) + func->exported_name = symbol; - } else if (node->kind == Ast_Kind_Global) { - AstGlobal* global = (AstGlobal *) node; + } else if (node->kind == Ast_Kind_Global) { + AstGlobal* global = (AstGlobal *) node; - if (global->exported_name == NULL) - global->exported_name = symbol; + if (global->exported_name == NULL) + global->exported_name = symbol; - } else if (node->kind != Ast_Kind_Overloaded_Function - && node->kind != Ast_Kind_StrLit) { - // HACK - bh_arr_push(parser->results.nodes_to_process, (AstNode *) node); - } + } else if (node->kind != Ast_Kind_Overloaded_Function + && node->kind != Ast_Kind_StrLit) { - AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding); - binding->token = symbol; - binding->node = (AstNode *) node; + if (node->kind == Ast_Kind_Struct_Type) { + ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator, + "%b", symbol->text, symbol->length); + } - return (AstNode *) binding; + // HACK + bh_arr_push(parser->results.nodes_to_process, (AstNode *) node); } + AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding); + binding->token = symbol; + binding->node = (AstNode *) node; + + return (AstNode *) binding; + } + default: break; } diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 02cff8b6..28a62d69 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -1,19 +1,19 @@ #define BH_DEBUG #include "onyxsempass.h" -AstBasicType basic_type_void = { { Ast_Kind_Basic_Type, 0, "void" }, &basic_types[Basic_Kind_Void] }; -AstBasicType basic_type_bool = { { Ast_Kind_Basic_Type, 0, "bool" }, &basic_types[Basic_Kind_Bool] }; -AstBasicType basic_type_i8 = { { Ast_Kind_Basic_Type, 0, "i8" }, &basic_types[Basic_Kind_I8] }; -AstBasicType basic_type_u8 = { { Ast_Kind_Basic_Type, 0, "u8" }, &basic_types[Basic_Kind_U8] }; -AstBasicType basic_type_i16 = { { Ast_Kind_Basic_Type, 0, "i16" }, &basic_types[Basic_Kind_I16] }; -AstBasicType basic_type_u16 = { { Ast_Kind_Basic_Type, 0, "u16" }, &basic_types[Basic_Kind_U16] }; -AstBasicType basic_type_i32 = { { Ast_Kind_Basic_Type, 0, "i32" }, &basic_types[Basic_Kind_I32] }; -AstBasicType basic_type_u32 = { { Ast_Kind_Basic_Type, 0, "u32" }, &basic_types[Basic_Kind_U32] }; -AstBasicType basic_type_i64 = { { Ast_Kind_Basic_Type, 0, "i64" }, &basic_types[Basic_Kind_I64] }; -AstBasicType basic_type_u64 = { { Ast_Kind_Basic_Type, 0, "u64" }, &basic_types[Basic_Kind_U64] }; -AstBasicType basic_type_f32 = { { Ast_Kind_Basic_Type, 0, "f32" }, &basic_types[Basic_Kind_F32] }; -AstBasicType basic_type_f64 = { { Ast_Kind_Basic_Type, 0, "f64" }, &basic_types[Basic_Kind_F64] }; -AstBasicType basic_type_rawptr = { { Ast_Kind_Basic_Type, 0, "rawptr" }, &basic_types[Basic_Kind_Rawptr] }; +AstBasicType basic_type_void = { { Ast_Kind_Basic_Type, 0, NULL, "void" }, &basic_types[Basic_Kind_Void] }; +AstBasicType basic_type_bool = { { Ast_Kind_Basic_Type, 0, NULL, "bool" }, &basic_types[Basic_Kind_Bool] }; +AstBasicType basic_type_i8 = { { Ast_Kind_Basic_Type, 0, NULL, "i8" }, &basic_types[Basic_Kind_I8] }; +AstBasicType basic_type_u8 = { { Ast_Kind_Basic_Type, 0, NULL, "u8" }, &basic_types[Basic_Kind_U8] }; +AstBasicType basic_type_i16 = { { Ast_Kind_Basic_Type, 0, NULL, "i16" }, &basic_types[Basic_Kind_I16] }; +AstBasicType basic_type_u16 = { { Ast_Kind_Basic_Type, 0, NULL, "u16" }, &basic_types[Basic_Kind_U16] }; +AstBasicType basic_type_i32 = { { Ast_Kind_Basic_Type, 0, NULL, "i32" }, &basic_types[Basic_Kind_I32] }; +AstBasicType basic_type_u32 = { { Ast_Kind_Basic_Type, 0, NULL, "u32" }, &basic_types[Basic_Kind_U32] }; +AstBasicType basic_type_i64 = { { Ast_Kind_Basic_Type, 0, NULL, "i64" }, &basic_types[Basic_Kind_I64] }; +AstBasicType basic_type_u64 = { { Ast_Kind_Basic_Type, 0, NULL, "u64" }, &basic_types[Basic_Kind_U64] }; +AstBasicType basic_type_f32 = { { Ast_Kind_Basic_Type, 0, NULL, "f32" }, &basic_types[Basic_Kind_F32] }; +AstBasicType basic_type_f64 = { { Ast_Kind_Basic_Type, 0, NULL, "f64" }, &basic_types[Basic_Kind_F64] }; +AstBasicType basic_type_rawptr = { { Ast_Kind_Basic_Type, 0, NULL, "rawptr" }, &basic_types[Basic_Kind_Rawptr] }; AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 }; @@ -151,6 +151,19 @@ static AstType* symres_type(AstType* type) { return type; } + if (type->kind == Ast_Kind_Struct_Type) { + AstStructType* s_node = (AstStructType *) type; + if (s_node->flags & Ast_Flag_Type_Is_Resolved) return type; + + s_node->flags |= Ast_Flag_Type_Is_Resolved; + + bh_arr_each(AstStructMember *, member, s_node->members) { + (*member)->type_node = symres_type((*member)->type_node); + } + + return type; + } + assert(("Bad type node", 0)); return NULL; } @@ -222,6 +235,10 @@ static void symres_expression(AstTyped** expr) { symres_expression(&((AstArrayAccess *)(*expr))->expr); break; + case Ast_Kind_Field_Access: + symres_expression(&((AstFieldAccess *)(*expr))->expr); + break; + default: DEBUG_HERE; break; @@ -367,6 +384,7 @@ void onyx_resolve_symbols(ProgramInfo* program) { case Entity_Type_Overloaded_Function: symres_overloaded_function(entity->overloaded_function); break; case Entity_Type_Global: symres_global(entity->global); break; case Entity_Type_Expression: symres_expression(&entity->expr); break; + case Entity_Type_Struct: symres_type((AstType *) entity->struct_type); break; default: break; } diff --git a/src/onyxtypes.c b/src/onyxtypes.c index f8e9cd30..f23b8802 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -23,6 +23,56 @@ Type basic_types[] = { { Type_Kind_Basic, 0, { Basic_Kind_Rawptr, Basic_Flag_Pointer, 4, "rawptr" } }, }; +b32 types_are_surface_compatible(Type* t1, Type* t2) { + // NOTE: If they are pointing to the same thing, + // it is safe to assume they are the same type + if (t1 == t2) return 1; + if (t1 == NULL || t2 == NULL) return 0; + + switch (t1->kind) { + case Type_Kind_Basic: + if (t2->kind == Type_Kind_Basic) { + // HACK: Not sure if this is right way to check this? + if (t1 == t2) return 1; + + if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) { + return t1->Basic.size == t2->Basic.size; + } + } + break; + + case Type_Kind_Pointer: + if (t2->kind == Type_Kind_Pointer) return 1; + break; + + case Type_Kind_Struct: { + if (t2->kind != Type_Kind_Struct) return 0; + if (t1->Struct.mem_count != t2->Struct.mem_count) return 0; + if (strcmp(t1->Struct.name, t2->Struct.name) == 0) return 1; + + b32 works = 1; + bh_table_each_start(StructMember, t1->Struct.members); + if (!bh_table_has(StructMember, t2->Struct.members, (char *) key)) return 0; + StructMember other = bh_table_get(StructMember, t2->Struct.members, (char *) key); + if (other.offset != value.offset) return 0; + + if (!types_are_compatible(value.type, other.type)) { + works = 0; + break; + } + bh_table_each_end; + + return works; + } + + default: + assert(("Invalid type", 0)); + break; + } + + return 0; +} + b32 types_are_compatible(Type* t1, Type* t2) { // NOTE: If they are pointing to the same thing, // it is safe to assume they are the same type @@ -47,6 +97,26 @@ b32 types_are_compatible(Type* t1, Type* t2) { } break; + case Type_Kind_Struct: { + if (t2->kind != Type_Kind_Struct) return 0; + if (t1->Struct.mem_count != t2->Struct.mem_count) return 0; + if (strcmp(t1->Struct.name, t2->Struct.name) == 0) return 1; + + b32 works = 1; + bh_table_each_start(StructMember, t1->Struct.members); + if (!bh_table_has(StructMember, t2->Struct.members, (char *) key)) return 0; + StructMember other = bh_table_get(StructMember, t2->Struct.members, (char *) key); + if (other.offset != value.offset) return 0; + + if (!types_are_surface_compatible(value.type, other.type)) { + works = 0; + break; + } + bh_table_each_end; + + return works; + } + default: assert(("Invalid type", 0)); break; @@ -55,6 +125,18 @@ b32 types_are_compatible(Type* t1, Type* t2) { return 0; } +u32 type_size_of(Type* type) { + if (type == NULL) return 0; + + switch (type->kind) { + case Type_Kind_Basic: return type->Basic.size; + case Type_Kind_Pointer: return 4; + case Type_Kind_Function: return 0; + case Type_Kind_Struct: return type->Struct.size; + default: return 0; + } +} + Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { if (type_node == NULL) return NULL; @@ -81,9 +163,50 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { return func_type; } + case Ast_Kind_Struct_Type: { + AstStructType* s_node = (AstStructType *) type_node; + if (s_node->stcache != NULL) return s_node->stcache; + + Type* s_type = bh_alloc(alloc, sizeof(Type)); + s_node->stcache = s_type; + s_type->kind = Type_Kind_Struct; + + s_type->Struct.name = s_node->name; + s_type->Struct.mem_count = bh_arr_length(s_node->members); + bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count); + + u32 offset = 0; + u32 min_alignment = 1; + bh_arr_each(AstStructMember *, member, s_node->members) { + (*member)->type = type_build_from_ast(alloc, (*member)->type_node); + + // TODO: Add alignment checking here + + StructMember smem = { + .offset = offset, + .type = (*member)->type + }; + + token_toggle_end((*member)->token); + bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem); + token_toggle_end((*member)->token); + + offset += type_size_of((*member)->type); + } + + // TODO: Again, add alignment + s_type->Struct.size = offset; + + return s_type; + } + case Ast_Kind_Basic_Type: return ((AstBasicType *) type_node)->type; + case Ast_Kind_Symbol: + assert(("symbol node in type expression", 0)); + return NULL; + default: assert(("Node is not a type node", 0)); return NULL; @@ -107,14 +230,50 @@ const char* type_get_name(Type* type) { switch (type->kind) { case Type_Kind_Basic: return type->Basic.name; case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem)); + case Type_Kind_Struct: return type->Struct.name; default: return "unknown"; } } +u32 type_get_alignment_log2(Type* type) { + i32 store_size = type_size_of(type); + i32 is_integer = (type->Basic.flags & Basic_Flag_Integer) + || (type->Basic.flags & Basic_Flag_Pointer); + + if (is_integer) { + if (store_size == 1) return 0; + else if (store_size == 2) return 1; + else if (store_size == 4) return 2; + else if (store_size == 8) return 3; + } else { + if (store_size == 4) return 2; + else if (store_size == 8) return 3; + } + + return 2; +} + +StructMember type_struct_lookup_member(Type* type, char* member) { + if (!type_is_struct(type)) return (StructMember) { 0 }; + if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem; + + TypeStruct* stype = &type->Struct; + + if (!bh_table_has(StructMember, stype->members, member)) return (StructMember) { 0 }; + StructMember smem = bh_table_get(StructMember, stype->members, member); + return smem; +} + b32 type_is_pointer(Type* type) { return type->kind == Type_Kind_Pointer || (type->Basic.flags & Basic_Flag_Pointer) != 0; } +b32 type_is_struct(Type* type) { + if (type->kind == Type_Kind_Struct) return 1; + if (type->kind == Type_Kind_Pointer && type->Pointer.elem->kind == Type_Kind_Struct) return 1; + return 0; +} + b32 type_is_bool(Type* type) { return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool; } diff --git a/src/onyxutils.c b/src/onyxutils.c index 541accf5..edc56e13 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -26,11 +26,14 @@ static const char* ast_node_names[] = { "UN_OP", "BIN_OP", - "TYPE_START (BAD)" + "TYPE_START (BAD)", "TYPE", "POINTER_TYPE", "FUNCTION_TYPE", - "TYPE_END (BAD)" + "STRUCT TYPE", + "TYPE_END (BAD)", + + "STRUCT MEMBER", "NUMERIC LITERAL", "STRING LITERAL", @@ -41,6 +44,7 @@ static const char* ast_node_names[] = { "ADDRESS OF", "DEREFERENCE", "ARRAY_ACCESS", + "FIELD_ACCESS", "IF", "FOR", diff --git a/src/onyxwasm.c b/src/onyxwasm.c index b5a361d2..cc3e89e5 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -227,20 +227,24 @@ static WasmType onyx_type_to_wasm_type(Type* type) { #define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data })) #define COMPILE_FUNC(kind, ...) static void compile_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__) -COMPILE_FUNC(function_body, AstFunction* fd); -COMPILE_FUNC(block, AstBlock* block); -COMPILE_FUNC(statement, AstNode* stmt); -COMPILE_FUNC(assignment, AstBinaryOp* assign); -COMPILE_FUNC(if, AstIf* if_node); -COMPILE_FUNC(while, AstWhile* while_node); -COMPILE_FUNC(for, AstFor* for_node); -COMPILE_FUNC(binop, AstBinaryOp* binop); -COMPILE_FUNC(unaryop, AstUnaryOp* unop); -COMPILE_FUNC(call, AstCall* call); -COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call); -COMPILE_FUNC(expression, AstTyped* expr); -COMPILE_FUNC(cast, AstUnaryOp* cast); -COMPILE_FUNC(return, AstReturn* ret); +COMPILE_FUNC(function_body, AstFunction* fd); +COMPILE_FUNC(block, AstBlock* block); +COMPILE_FUNC(statement, AstNode* stmt); +COMPILE_FUNC(assignment, AstBinaryOp* assign); +COMPILE_FUNC(store_instruction, Type* type, u32 alignment, u32 offset); +COMPILE_FUNC(load_instruction, Type* type, u32 offset); +COMPILE_FUNC(if, AstIf* if_node); +COMPILE_FUNC(while, AstWhile* while_node); +COMPILE_FUNC(for, AstFor* for_node); +COMPILE_FUNC(binop, AstBinaryOp* binop); +COMPILE_FUNC(unaryop, AstUnaryOp* unop); +COMPILE_FUNC(call, AstCall* call); +COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call); +COMPILE_FUNC(array_access_location, AstArrayAccess* aa); +COMPILE_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return); +COMPILE_FUNC(expression, AstTyped* expr); +COMPILE_FUNC(cast, AstUnaryOp* cast); +COMPILE_FUNC(return, AstReturn* ret); COMPILE_FUNC(function_body, AstFunction* fd) { if (fd->body == NULL) return; @@ -337,46 +341,32 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) { compile_expression(mod, &code, deref->expr); compile_expression(mod, &code, assign->right); - i32 store_size = deref->type->Basic.size; - i32 is_integer = (deref->type->Basic.flags & Basic_Flag_Integer) - || (deref->type->Basic.flags & Basic_Flag_Pointer); - - if (is_integer) { - if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 })); - else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { 1, 0 })); - else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { 2, 0 })); - else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { 3, 0 })); - } else { - if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { 2, 0 })); - else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { 3, 0 })); - } + compile_store_instruction(mod, &code, + deref->type, + type_get_alignment_log2(deref->type), + 0); } else if (lval->kind == Ast_Kind_Array_Access) { AstArrayAccess* aa = (AstArrayAccess *) lval; - compile_expression(mod, &code, aa->expr); - if (aa->elem_size != 1) { - WID(WI_I32_CONST, aa->elem_size); - WI(WI_I32_MUL); - } - compile_expression(mod, &code, aa->addr); - WI(WI_I32_ADD); - + compile_array_access_location(mod, &code, aa); compile_expression(mod, &code, assign->right); - i32 store_size = aa->type->Basic.size; - i32 is_integer = (aa->type->Basic.flags & Basic_Flag_Integer) - || (aa->type->Basic.flags & Basic_Flag_Pointer); + compile_store_instruction(mod, &code, + aa->type, + type_get_alignment_log2(aa->type), + 0); - if (is_integer) { - if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 })); - else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { 1, 0 })); - else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { 2, 0 })); - else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { 3, 0 })); - } else { - if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { 2, 0 })); - else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { 3, 0 })); - } + } else if (lval->kind == Ast_Kind_Field_Access) { + AstFieldAccess* field = (AstFieldAccess *) lval; + u64 offset = 0; + compile_field_access_location(mod, &code, field, &offset); + compile_expression(mod, &code, assign->right); + + compile_store_instruction(mod, &code, + field->type, + type_get_alignment_log2(field->type), + offset); } else { assert(("Invalid lval", 0)); } @@ -384,6 +374,60 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) { *pcode = code; } +COMPILE_FUNC(store_instruction, Type* type, u32 alignment, u32 offset) { + bh_arr(WasmInstruction) code = *pcode; + + i32 store_size = type_size_of(type); + i32 is_integer = (type->Basic.flags & Basic_Flag_Integer) + || (type->Basic.flags & Basic_Flag_Pointer); + + if (is_integer) { + if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { alignment, offset })); + else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset })); + else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { alignment, offset })); + else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { alignment, offset })); + } else { + if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset })); + else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset })); + } + + *pcode = code; +} + +COMPILE_FUNC(load_instruction, Type* type, u32 offset) { + bh_arr(WasmInstruction) code = *pcode; + + i32 load_size = type_size_of(type); + i32 is_integer = (type->Basic.flags & Basic_Flag_Integer) + || (type->Basic.flags & Basic_Flag_Pointer); + i32 is_unsigned = type->Basic.flags & Basic_Flag_Unsigned; + + WasmInstructionType instr = WI_NOP; + i32 alignment = type_get_alignment_log2(type); + + if (is_integer) { + if (load_size == 1) instr = WI_I32_LOAD_8_S; + else if (load_size == 2) instr = WI_I32_LOAD_16_S; + else if (load_size == 4) instr = WI_I32_LOAD; + else if (load_size == 8) instr = WI_I64_LOAD; + + if (load_size < 4 && is_unsigned) instr += 1; + } else { + if (load_size == 4) instr = WI_F32_LOAD; + else if (load_size == 8) instr = WI_F64_LOAD; + } + + WID(instr, ((WasmInstructionData) { alignment, offset })); + + if (instr == WI_NOP) { + onyx_message_add(Msg_Type_Literal, + (OnyxFilePos) { 0 }, + "failed to generate load instruction"); + } + + *pcode = code; +} + COMPILE_FUNC(if, AstIf* if_node) { bh_arr(WasmInstruction) code = *pcode; @@ -703,6 +747,42 @@ COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call) { *pcode = code; } +COMPILE_FUNC(array_access_location, AstArrayAccess* aa) { + bh_arr(WasmInstruction) code = *pcode; + + compile_expression(mod, &code, aa->expr); + if (aa->elem_size != 1) { + WID(WI_I32_CONST, aa->elem_size); + WI(WI_I32_MUL); + } + compile_expression(mod, &code, aa->addr); + WI(WI_I32_ADD); + + *pcode = code; +} + +COMPILE_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) { + bh_arr(WasmInstruction) code = *pcode; + + u64 offset = field->offset; + AstTyped* source_expr = field->expr; + while (source_expr->kind == Ast_Kind_Field_Access + && (source_expr->type->kind == Type_Kind_Struct)) { + offset += ((AstFieldAccess *) source_expr)->offset; + source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr; + } + + if (source_expr->kind == Ast_Kind_Array_Access) { + compile_array_access_location(mod, &code, (AstArrayAccess *) source_expr); + } else { + compile_expression(mod, &code, source_expr); + } + + *offset_return = offset; + + *pcode = code; +} + COMPILE_FUNC(expression, AstTyped* expr) { bh_arr(WasmInstruction) code = *pcode; @@ -767,13 +847,15 @@ COMPILE_FUNC(expression, AstTyped* expr) { case Ast_Kind_Array_Access: { AstArrayAccess* aa = (AstArrayAccess *) aof->expr; + compile_array_access_location(mod, &code, aa); + break; + } - compile_expression(mod, &code, aa->expr); - if (aa->elem_size != 1) { - WID(WI_I32_CONST, aa->elem_size); - WI(WI_I32_MUL); - } - compile_expression(mod, &code, aa->addr); + case Ast_Kind_Field_Access: { + AstFieldAccess* field = (AstFieldAccess *) aof->expr; + u64 offset = 0; + compile_field_access_location(mod, &code, field, &offset); + WID(WI_I32_CONST, offset); WI(WI_I32_ADD); break; } @@ -790,72 +872,23 @@ COMPILE_FUNC(expression, AstTyped* expr) { case Ast_Kind_Dereference: { AstDereference* deref = (AstDereference *) expr; compile_expression(mod, &code, deref->expr); - - i32 load_size = deref->type->Basic.size; - i32 is_integer = (deref->type->Basic.flags & Basic_Flag_Integer) - || (deref->type->Basic.flags & Basic_Flag_Pointer); - i32 is_unsigned = deref->type->Basic.flags & Basic_Flag_Unsigned; - - WasmInstructionType instr = WI_NOP; - i32 alignment = log2_dumb(load_size); - - if (is_integer) { - if (load_size == 1) instr = WI_I32_LOAD_8_S; - else if (load_size == 2) instr = WI_I32_LOAD_16_S; - else if (load_size == 4) instr = WI_I32_LOAD; - else if (load_size == 8) instr = WI_I64_LOAD; - - if (load_size < 4 && is_unsigned) instr += 1; - } else { - if (load_size == 4) instr = WI_F32_LOAD; - else if (load_size == 8) instr = WI_F64_LOAD; - } - - if (instr != WI_NOP) { - WID(instr, ((WasmInstructionData) { alignment, 0 })); - } else { - DEBUG_HERE; - } - + compile_load_instruction(mod, &code, deref->type, 0); break; } case Ast_Kind_Array_Access: { AstArrayAccess* aa = (AstArrayAccess *) expr; - compile_expression(mod, &code, aa->expr); - if (aa->elem_size != 1) { - WID(WI_I32_CONST, aa->elem_size); - WI(WI_I32_MUL); - } - compile_expression(mod, &code, aa->addr); - WI(WI_I32_ADD); - - i32 load_size = aa->type->Basic.size; - i32 is_integer = (aa->type->Basic.flags & Basic_Flag_Integer) - || (aa->type->Basic.flags & Basic_Flag_Pointer); - i32 is_unsigned = aa->type->Basic.flags & Basic_Flag_Unsigned; - - WasmInstructionType instr = WI_NOP; - i32 alignment = log2_dumb(load_size); - - if (is_integer) { - if (load_size == 1) instr = WI_I32_LOAD_8_S; - else if (load_size == 2) instr = WI_I32_LOAD_16_S; - else if (load_size == 4) instr = WI_I32_LOAD; - else if (load_size == 8) instr = WI_I64_LOAD; - - if (load_size < 4 && is_unsigned) instr += 1; - } else { - if (load_size == 4) instr = WI_F32_LOAD; - else if (load_size == 8) instr = WI_F64_LOAD; - } + compile_array_access_location(mod, &code, aa); + compile_load_instruction(mod, &code, aa->type, 0); + break; + } - if (instr != WI_NOP) { - WID(instr, ((WasmInstructionData) { alignment, 0 })); - } else { - DEBUG_HERE; - } + case Ast_Kind_Field_Access: { + AstFieldAccess* field = (AstFieldAccess* ) expr; + u64 offset = 0; + compile_field_access_location(mod, &code, field, &offset); + compile_load_instruction(mod, &code, field->type, offset); break; } @@ -1225,10 +1258,9 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) { module->next_func_idx = program->foreign_func_count; module->next_global_idx = program->foreign_global_count; - // NOTE: First, assign indicies to all functions / globals bh_arr_each(Entity, entity, program->entities) { switch (entity->type) { - case Entity_Type_Function: { + case Entity_Type_Function_Header: { if (entity->function->flags & Ast_Flag_Intrinsic) break; u64 func_idx; @@ -1241,7 +1273,7 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) { break; } - case Entity_Type_Global: { + case Entity_Type_Global_Header: { u64 global_idx; if ((entity->global->flags & Ast_Flag_Foreign) != 0) global_idx = module->next_foreign_global_idx++; @@ -1254,23 +1286,19 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) { case Entity_Type_String_Literal: { compile_string_literal(module, (AstStrLit *) entity->strlit); - } - default: break; - } - } - - // NOTE: Round up to the nearest multiple of 16 - builtin_heap_start.value.i = - (module->next_datum_offset & 15) - ? ((module->next_datum_offset >> 4) + 1) << 4 - : module->next_datum_offset; + // HACK: To put this here + // NOTE: Round up to the nearest multiple of 16 + builtin_heap_start.value.i = + (module->next_datum_offset & 15) + ? ((module->next_datum_offset >> 4) + 1) << 4 + : module->next_datum_offset; + break; + } - // NOTE: Then, compile everything - bh_arr_each(Entity, entity, program->entities) { - switch (entity->type) { case Entity_Type_Function: compile_function(module, entity->function); break; case Entity_Type_Global: compile_global(module, entity->global); break; + default: break; } }