From: Brendan Hansen Date: Fri, 17 Jul 2020 18:55:23 +0000 (-0500) Subject: if and while can now take any statement X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=2f24fce5f1d2df7bf5b3793d83c5e7d6030ddb3c;p=onyx.git if and while can now take any statement --- diff --git a/docs/thoughts b/docs/thoughts index 8bedc24a..13703fa4 100644 --- a/docs/thoughts +++ b/docs/thoughts @@ -100,4 +100,6 @@ Explicit overriden functions: min(2, 5); min(4.5, 10.4); - +Some WASM intruction optimizations: + - local.set followed by local.get -> local.tee + - Any code after unconditional break before 'end' can be removed diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 967f43ab..1d2293b1 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -110,12 +110,6 @@ typedef enum BinaryOp { // Base Nodes - -// NOTE: AstNode and AstTyped need to be EXACTLY the same for all -// arguments existing in AstNode. I do this to avoid a nested -// "inheiritance" where you would have to say node.base.base.next -// for example - #define AstNode_members { \ AstKind kind; \ u32 flags; \ @@ -137,10 +131,7 @@ struct AstNode AstNode_members; // 'type' is filled out afterwards. If it is NULL, the Type* is built // using the type_node. This can then be used to typecheck this node. #define AstTyped_members { \ - AstKind kind; \ - u32 flags; \ - OnyxToken *token; \ - AstNode *next; \ + AstNode_base; \ AstType *type_node; \ Type *type; \ } @@ -160,15 +151,13 @@ struct AstArgument { AstTyped_base; AstTyped *value; }; // Structure Nodes struct AstLocalGroup { AstNode_base; AstLocalGroup *prev_group; AstLocal *last_local; }; struct AstBlock { AstNode_base; AstNode *body; AstLocalGroup *locals; }; -struct AstWhile { AstNode_base; AstTyped *cond; AstBlock *body; }; +struct AstWhile { AstNode_base; AstTyped *cond; AstNode *stmt; }; struct AstIf { AstNode_base; AstTyped *cond; - union { - AstIf *as_if; - AstBlock* as_block; - } true_block, false_block; + AstNode* true_stmt; + AstNode* false_stmt; }; // Type Nodes @@ -207,6 +196,7 @@ struct AstFunction { AstBlock *body; AstLocal *params; + bh_arr(AstLocal *) locals; union { // NOTE: Used when a function is exported with a specific name diff --git a/include/onyxsempass.h b/include/onyxsempass.h index c5780fa6..97114bcf 100644 --- a/include/onyxsempass.h +++ b/include/onyxsempass.h @@ -20,6 +20,7 @@ typedef struct SemState { // NOTE: Used in symbol resolution phase AstLocalGroup* curr_local_group; + AstFunction* curr_function; // NOTE: Used in type checking phase Type* expected_return_type; diff --git a/include/onyxwasm.h b/include/onyxwasm.h index 24b951c1..55be4e15 100644 --- a/include/onyxwasm.h +++ b/include/onyxwasm.h @@ -282,23 +282,20 @@ typedef struct OnyxWasmModule { // NOTE: Mapping from local ast node ptrs to indicies bh_imap local_map; bh_imap global_map; - - bh_arr(u8) structured_jump_target; + bh_imap func_map; // NOTE: Maps from ast node pointers to the function index // NOTE: Used internally as a map from strings that represent function types, // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 ) // to the function type index if it has been created. bh_table(i32) type_map; - bh_arr(WasmFuncType*) functypes; // NOTE: This have to be pointers because the type is variadic in size - - bh_arr(WasmFunc) funcs; - bh_imap func_map; // NOTE: Maps from ast node pointers to the function index - bh_table(WasmExport) exports; + bh_arr(u8) structured_jump_target; + bh_arr(WasmFuncType*) types; // NOTE: This have to be pointers because the type is variadic in size bh_arr(WasmImport) imports; - + bh_table(WasmExport) exports; bh_arr(WasmGlobal) globals; + bh_arr(WasmFunc) funcs; u16 next_type_idx; u16 next_func_idx; diff --git a/onyx b/onyx index efc7bf7d..8457bd02 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/basic.onyx b/progs/basic.onyx index 1d10f18e..caed8770 100644 --- a/progs/basic.onyx +++ b/progs/basic.onyx @@ -6,6 +6,10 @@ print :: proc #foreign "host" "print" (val: i32) --- +printf :: proc + #foreign "host" "print" + (val: f32) --- + test :: proc (a: bool) -> bool { return !a; } @@ -36,11 +40,25 @@ main :: proc #export { print(4 + global_value); b := 1 + foo(2); + if b == 13 b = 10; print(b); - if b == 13 {} - else { - print(1024); + if b == 13 print(5678); + else print(1024); + + if b == 13 --- + + while true { + while true { + while true { + foo := 20; + foo = global_value + 10; + print(foo); + break; + } + break; + } + break; } cond :: true; diff --git a/src/onyxchecker.c b/src/onyxchecker.c index a41720b7..ec81e35f 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -97,8 +97,8 @@ static b32 check_if(SemState* state, AstIf* ifnode) { return 1; } - if (ifnode->true_block.as_if) if (check_statement(state, (AstNode *) ifnode->true_block.as_block)) return 1; - if (ifnode->false_block.as_if) if (check_statement(state, (AstNode *) ifnode->false_block.as_block)) return 1; + if (ifnode->true_stmt) if (check_statement(state, ifnode->true_stmt)) return 1; + if (ifnode->false_stmt) if (check_statement(state, ifnode->false_stmt)) return 1; return 0; } @@ -114,7 +114,7 @@ static b32 check_while(SemState* state, AstWhile* whilenode) { return 1; } - return check_block(state, whilenode->body); + return check_statement(state, whilenode->stmt); } static b32 check_call(SemState* state, AstCall* call) { diff --git a/src/onyxparser.c b/src/onyxparser.c index 6e860082..d98a8c6e 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -368,41 +368,41 @@ expression_done: return root; } -// 'if' ('elseif' )* ('else' )? +// 'if' ('elseif' )* ('else' )? static AstIf* parse_if_stmt(OnyxParser* parser) { expect_token(parser, Token_Type_Keyword_If); AstTyped* cond = parse_expression(parser); - AstBlock* true_block = parse_block(parser); + AstNode* true_stmt = parse_statement(parser); AstIf* if_node = make_node(AstIf, Ast_Kind_If); AstIf* root_if = if_node; if_node->cond = cond; - if (true_block != NULL) - if_node->true_block.as_block = true_block; + if (true_stmt != NULL) + if_node->true_stmt = true_stmt; while (parser->curr->type == Token_Type_Keyword_Elseif) { consume_token(parser); AstIf* elseif_node = make_node(AstIf, Ast_Kind_If); cond = parse_expression(parser); - true_block = parse_block(parser); + true_stmt = parse_statement(parser); elseif_node->cond = cond; - if (true_block != NULL) - elseif_node->true_block.as_block = true_block; + if (true_stmt != NULL) + elseif_node->true_stmt = true_stmt; - if_node->false_block.as_if = elseif_node; + if_node->false_stmt = (AstNode *) elseif_node; if_node = elseif_node; } if (parser->curr->type == Token_Type_Keyword_Else) { consume_token(parser); - AstBlock* false_block = parse_block(parser); - if (false_block != NULL) - if_node->false_block.as_block = false_block; + AstNode* false_stmt = parse_statement(parser); + if (false_stmt != NULL) + if_node->false_stmt = false_stmt; } return root_if; @@ -413,12 +413,12 @@ static AstWhile* parse_while_stmt(OnyxParser* parser) { OnyxToken* while_token = expect_token(parser, Token_Type_Keyword_While); AstTyped* cond = parse_expression(parser); - AstBlock* body = parse_block(parser); + AstNode* stmt = parse_statement(parser); AstWhile* while_node = make_node(AstWhile, Ast_Kind_While); while_node->token = while_token; while_node->cond = cond; - while_node->body = body; + while_node->stmt = stmt; return while_node; } @@ -588,6 +588,7 @@ static AstNode* parse_statement(OnyxParser* parser) { break; case '{': + case Token_Type_Empty_Block: needs_semicolon = 0; retval = (AstNode *) parse_block(parser); break; @@ -779,6 +780,7 @@ static b32 parse_possible_directive(OnyxParser* parser, const char* dir) { static AstFunction* parse_function_definition(OnyxParser* parser) { AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function); + bh_arr_new(global_heap_allocator, func_def->locals, 4); func_def->token = expect_token(parser, Token_Type_Keyword_Proc); while (parser->curr->type == '#') { diff --git a/src/onyxsempass.c b/src/onyxsempass.c index bc1d8524..0b502c6e 100644 --- a/src/onyxsempass.c +++ b/src/onyxsempass.c @@ -18,75 +18,10 @@ SemState onyx_sempass_create(bh_allocator alloc, bh_allocator node_alloc, OnyxMe return state; } - -// NOTE: If the compiler is expanded to support more targets than just -// WASM, this function may not be needed. It brings all of the locals -// defined in sub-scopes up to the function-block level. This is a -// requirement of WASM, but not of other targets. -static void hoist_locals(ParserOutput* program) { - bh_arr(AstBlock*) traversal_queue = NULL; - bh_arr_new(global_scratch_allocator, traversal_queue, 4); - bh_arr_set_length(traversal_queue, 0); - - bh_arr_each(AstFunction *, func, program->functions) { - if ((*func)->flags & Ast_Flag_Intrinsic) continue; - - AstLocalGroup* top_locals = (*func)->body->locals; - - bh_arr_push(traversal_queue, (*func)->body); - while (!bh_arr_is_empty(traversal_queue)) { - AstBlock* block = traversal_queue[0]; - - if (block->kind == Ast_Kind_If) { - AstIf* if_node = (AstIf *) block; - if (if_node->true_block.as_block != NULL) - bh_arr_push(traversal_queue, if_node->true_block.as_block); - - if (if_node->false_block.as_block != NULL) - bh_arr_push(traversal_queue, if_node->false_block.as_block); - - } else { - - if (block->locals != top_locals && block->locals->last_local != NULL) { - AstLocal* last_local = block->locals->last_local; - while (last_local && last_local->prev_local != NULL) last_local = last_local->prev_local; - - last_local->prev_local = top_locals->last_local; - top_locals->last_local = block->locals->last_local; - block->locals->last_local = NULL; - } - - AstNode* walker = block->body; - while (walker) { - if (walker->kind == Ast_Kind_Block) { - bh_arr_push(traversal_queue, (AstBlock *) walker); - - } else if (walker->kind == Ast_Kind_While) { - bh_arr_push(traversal_queue, ((AstWhile *) walker)->body); - - } else if (walker->kind == Ast_Kind_If) { - if (((AstIf *) walker)->true_block.as_block != NULL) - bh_arr_push(traversal_queue, ((AstIf *) walker)->true_block.as_block); - - if (((AstIf *) walker)->false_block.as_block != NULL) - bh_arr_push(traversal_queue, ((AstIf *) walker)->false_block.as_block); - } - - walker = walker->next; - } - } - - bh_arr_deleten(traversal_queue, 0, 1); - } - } -} - void onyx_sempass(SemState* state, ParserOutput* program) { onyx_resolve_symbols(state, program); if (onyx_message_has_errors(state->msgs)) return; onyx_type_check(state, program); if (onyx_message_has_errors(state->msgs)) return; - - hoist_locals(program); } diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 0db1bb0b..a75c15cc 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -40,6 +40,8 @@ static void symbol_introduce(SemState* state, OnyxToken* tkn, AstNode* symbol) { AstLocal* local = (AstLocal *) symbol; local->prev_local = state->curr_local_group->last_local; state->curr_local_group->last_local = local; + + bh_arr_push(state->curr_function->locals, local); } token_toggle_end(tkn); @@ -164,6 +166,7 @@ static AstType* symres_type(SemState* state, AstType* type) { static void symres_local(SemState* state, AstLocal** local) { (*local)->type_node = symres_type(state, (*local)->type_node); + symbol_introduce(state, (*local)->token, (AstNode *) *local); } @@ -240,30 +243,20 @@ static void symres_return(SemState* state, AstReturn* ret) { static void symres_if(SemState* state, AstIf* ifnode) { symres_expression(state, &ifnode->cond); - if (ifnode->true_block.as_if != NULL) { - if (ifnode->true_block.as_if->kind == Ast_Kind_Block) - symres_block(state, ifnode->true_block.as_block); - - else if (ifnode->true_block.as_if->kind == Ast_Kind_If) - symres_if(state, ifnode->true_block.as_if); - - else DEBUG_HERE; - } - - if (ifnode->false_block.as_if != NULL) { - if (ifnode->false_block.as_if->kind == Ast_Kind_Block) - symres_block(state, ifnode->false_block.as_block); - else if (ifnode->false_block.as_if->kind == Ast_Kind_If) - symres_if(state, ifnode->false_block.as_if); - - else DEBUG_HERE; - } + // BUG: This will not work for the following case: + // if cond foo := 10 + // else foo := 20 + // + // The declaration will cause a problem but semantically the above + // doesn't make sense. + if (ifnode->true_stmt != NULL) symres_statement(state, ifnode->true_stmt); + if (ifnode->false_stmt != NULL) symres_statement(state, ifnode->false_stmt); } static void symres_while(SemState* state, AstWhile* whilenode) { symres_expression(state, &whilenode->cond); - symres_block(state, whilenode->body); + symres_statement(state, whilenode->stmt); } // NOTE: Returns 1 if the statment should be removed @@ -315,6 +308,7 @@ static void symres_function(SemState* state, AstFunction* func) { func->type_node = symres_type(state, func->type_node); } + state->curr_function = func; symres_block(state, func->body); for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->next) { diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 752e4aaf..721cbc0f 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -344,31 +344,25 @@ COMPILE_FUNC(if, AstIf* if_node) { bh_arr_push(mod->structured_jump_target, 0); - if (if_node->true_block.as_if) { - // NOTE: This is kind of gross, but making a function for this doesn't feel right - - if (if_node->true_block.as_if->kind == Ast_Kind_If) { - forll (AstNode, stmt, (AstNode *) if_node->true_block.as_if, next) { - compile_statement(mod, &code, stmt); - } - } else if (if_node->true_block.as_if->kind == Ast_Kind_Block) { - forll (AstNode, stmt, if_node->true_block.as_block->body, next) { + if (if_node->true_stmt) { + if (if_node->true_stmt->kind == Ast_Kind_Block) { + forll (AstNode, stmt, ((AstBlock *) if_node->true_stmt)->body, next) { compile_statement(mod, &code, stmt); } + } else { + compile_statement(mod, &code, if_node->true_stmt); } } - if (if_node->false_block.as_if) { + if (if_node->false_stmt) { WI(WI_ELSE); - if (if_node->false_block.as_if->kind == Ast_Kind_If) { - forll (AstNode, stmt, (AstNode *) if_node->false_block.as_if, next) { - compile_statement(mod, &code, stmt); - } - } else if (if_node->false_block.as_if->kind == Ast_Kind_Block) { - forll (AstNode, stmt, if_node->false_block.as_block->body, next) { + if (if_node->false_stmt->kind == Ast_Kind_Block) { + forll (AstNode, stmt, ((AstBlock *) if_node->false_stmt)->body, next) { compile_statement(mod, &code, stmt); } + } else { + compile_statement(mod, &code, if_node->false_stmt); } } @@ -392,8 +386,12 @@ COMPILE_FUNC(while, AstWhile* while_node) { bh_arr_push(mod->structured_jump_target, 1); bh_arr_push(mod->structured_jump_target, 2); - forll (AstNode, stmt, while_node->body->body, next) { - compile_statement(mod, &code, stmt); + if (while_node->stmt->kind == Ast_Kind_Block) { + forll (AstNode, stmt, ((AstBlock *) while_node->stmt)->body, next) { + compile_statement(mod, &code, stmt); + } + } else { + compile_statement(mod, &code, while_node->stmt); } bh_arr_pop(mod->structured_jump_target); @@ -784,7 +782,7 @@ static i32 generate_type_idx(OnyxWasmModule* mod, AstFunction* fd) { // HACK ish thing memcpy(type->param_types, type_repr_buf, type->param_count); - bh_arr_push(mod->functypes, type); + bh_arr_push(mod->types, type); bh_table_put(i32, mod->type_map, type_repr_buf, mod->next_type_idx); type_idx = mod->next_type_idx; @@ -854,9 +852,9 @@ static void compile_function(OnyxWasmModule* mod, AstFunction* fd) { // is the same as the order of the local_types above u8* count = &wasm_func.locals.i32_count; fori (ti, 0, 3) { - forll (AstLocal, local, fd->body->locals->last_local, prev_local) { - if (onyx_type_to_wasm_type(local->type) == local_types[ti]) { - bh_imap_put(&mod->local_map, (u64) local, localidx++); + bh_arr_each(AstLocal *, local, fd->locals) { + if (onyx_type_to_wasm_type((*local)->type) == local_types[ti]) { + bh_imap_put(&mod->local_map, (u64) *local, localidx++); (*count)++; } @@ -936,7 +934,7 @@ OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc, OnyxMessages* msgs) { .type_map = NULL, .next_type_idx = 0, - .functypes = NULL, + .types = NULL, .funcs = NULL, .next_func_idx = 0, @@ -952,7 +950,7 @@ OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc, OnyxMessages* msgs) { .structured_jump_target = NULL, }; - bh_arr_new(alloc, module.functypes, 4); + bh_arr_new(alloc, module.types, 4); bh_arr_new(alloc, module.funcs, 4); bh_arr_new(alloc, module.imports, 4); bh_arr_new(alloc, module.globals, 4); @@ -1009,7 +1007,7 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ParserOutput* program) { } void onyx_wasm_module_free(OnyxWasmModule* module) { - bh_arr_free(module->functypes); + bh_arr_free(module->types); bh_arr_free(module->funcs); bh_imap_free(&module->local_map); bh_imap_free(&module->func_map); @@ -1094,9 +1092,9 @@ static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) { bh_buffer_init(&vec_buff, buff->allocator, 128); i32 vec_len = output_vector( - (void**) module->functypes, + (void**) module->types, sizeof(WasmFuncType*), - bh_arr_length(module->functypes), + bh_arr_length(module->types), (vector_func *) output_functype, &vec_buff);