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
// 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; \
// '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; \
}
// 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
AstBlock *body;
AstLocal *params;
+ bh_arr(AstLocal *) locals;
union {
// NOTE: Used when a function is exported with a specific name
// NOTE: Used in symbol resolution phase
AstLocalGroup* curr_local_group;
+ AstFunction* curr_function;
// NOTE: Used in type checking phase
Type* expected_return_type;
// 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;
#foreign "host" "print"
(val: i32) ---
+printf :: proc
+ #foreign "host" "print"
+ (val: f32) ---
+
test :: proc (a: bool) -> bool {
return !a;
}
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;
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;
}
return 1;
}
- return check_block(state, whilenode->body);
+ return check_statement(state, whilenode->stmt);
}
static b32 check_call(SemState* state, AstCall* call) {
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;
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;
}
break;
case '{':
+ case Token_Type_Empty_Block:
needs_semicolon = 0;
retval = (AstNode *) parse_block(parser);
break;
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 == '#') {
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);
}
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);
static void symres_local(SemState* state, AstLocal** local) {
(*local)->type_node = symres_type(state, (*local)->type_node);
+
symbol_introduce(state, (*local)->token, (AstNode *) *local);
}
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
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) {
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);
}
}
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);
// 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;
// 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)++;
}
.type_map = NULL,
.next_type_idx = 0,
- .functypes = NULL,
+ .types = NULL,
.funcs = NULL,
.next_func_idx = 0,
.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);
}
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);
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);