if and while can now take any statement
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 17 Jul 2020 18:55:23 +0000 (13:55 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 17 Jul 2020 18:55:23 +0000 (13:55 -0500)
docs/thoughts
include/onyxastnodes.h
include/onyxsempass.h
include/onyxwasm.h
onyx
progs/basic.onyx
src/onyxchecker.c
src/onyxparser.c
src/onyxsempass.c
src/onyxsymres.c
src/onyxwasm.c

index 8bedc24af27686b99bbf0b52c3566963667732b4..13703fa4b10d7da6d9f3a0acd687fc2a4e3baa5c 100644 (file)
@@ -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
index 967f43abc2976d0e1a160a3d660d4cc0e5ff1d79..1d2293b1fff190a564fe01e1b3ef7ef665007b5a 100644 (file)
@@ -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
index c5780fa61b36b97404ec1210df4cd663aaaa4df7..97114bcfad39d0a86a2d404c4afe84ee52941e91 100644 (file)
@@ -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;
index 24b951c16906746e12698e92f6db9048d6dc8b09..55be4e15f5d8fc65713aefda828f5371d3f21ee0 100644 (file)
@@ -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 efc7bf7d7325729e88d39c864f41e9de0e659aab..8457bd029fef3350cbd6cc053d59c4ee343b972d 100755 (executable)
Binary files a/onyx and b/onyx differ
index 1d10f18e1ceca8aa10cff85fe6c076d991251fd7..caed8770263ebda4239b109b3996c4c2c4f7641b 100644 (file)
@@ -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;
index a41720b7d6872da96f4bd03109e04d7d4053516e..ec81e35f58d9691ff90dd3212a22a954c63a574e 100644 (file)
@@ -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) {
index 6e86008257b55a373edbdb811591f946e451fe63..d98a8c6eb5bf3151b168702ad88d9c40d07b8d74 100644 (file)
@@ -368,41 +368,41 @@ expression_done:
     return root;
 }
 
-// 'if' <expr> <block> ('elseif' <cond> <block>)* ('else' <block>)?
+// 'if' <expr> <stmt> ('elseif' <cond> <stmt>)* ('else' <block>)?
 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 == '#') {
index bc1d852431c4368b4e41d25bc184ad78127d48a6..0b502c6edcfbae67632625efb12b404aa49ab942 100644 (file)
@@ -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);
 }
index 0db1bb0bf16d1c1923bc9a57fa28cda09f0641e1..a75c15ccd662b5ab5f99d6fa083008821de86df9 100644 (file)
@@ -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) {
index 752e4aaf4815881ad7c5283e2d2d6afbb013eedb..721cbc0f2a7025e2f148ea6a8e0155f93d3239c8 100644 (file)
@@ -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);