|
@@ -0,0 +1,287 @@
|
|
|
+gb_internal cgBranchBlocks cg_lookup_branch_blocks(cgProcedure *p, Ast *ident) {
|
|
|
+ GB_ASSERT(ident->kind == Ast_Ident);
|
|
|
+ Entity *e = entity_of_node(ident);
|
|
|
+ GB_ASSERT(e->kind == Entity_Label);
|
|
|
+ for (cgBranchBlocks const &b : p->branch_blocks) {
|
|
|
+ if (b.label == e->Label.node) {
|
|
|
+ return b;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ GB_PANIC("Unreachable");
|
|
|
+ cgBranchBlocks empty = {};
|
|
|
+ return empty;
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal cgTargetList *cg_push_target_list(cgProcedure *p, Ast *label, TB_Node *break_, TB_Node *continue_, TB_Node *fallthrough_) {
|
|
|
+ cgTargetList *tl = gb_alloc_item(permanent_allocator(), cgTargetList);
|
|
|
+ tl->prev = p->target_list;
|
|
|
+ tl->break_ = break_;
|
|
|
+ tl->continue_ = continue_;
|
|
|
+ tl->fallthrough_ = fallthrough_;
|
|
|
+ p->target_list = tl;
|
|
|
+
|
|
|
+ if (label != nullptr) { // Set label blocks
|
|
|
+ GB_ASSERT(label->kind == Ast_Label);
|
|
|
+
|
|
|
+ for (cgBranchBlocks &b : p->branch_blocks) {
|
|
|
+ GB_ASSERT(b.label != nullptr && label != nullptr);
|
|
|
+ GB_ASSERT(b.label->kind == Ast_Label);
|
|
|
+ if (b.label == label) {
|
|
|
+ b.break_ = break_;
|
|
|
+ b.continue_ = continue_;
|
|
|
+ return tl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ GB_PANIC("Unreachable");
|
|
|
+ }
|
|
|
+
|
|
|
+ return tl;
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal void cg_pop_target_list(cgProcedure *p) {
|
|
|
+ p->target_list = p->target_list->prev;
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal cgAddr cg_add_local(cgProcedure *p, Type *type, Entity *e, bool zero_init) {
|
|
|
+ char const *name = "";
|
|
|
+ if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
|
|
|
+ // NOTE(bill): for debugging purposes only
|
|
|
+ name = alloc_cstring(permanent_allocator(), e->token.string);
|
|
|
+ }
|
|
|
+
|
|
|
+ isize size = type_size_of(type);
|
|
|
+ TB_CharUnits alignment = cast(TB_CharUnits)type_align_of(type);
|
|
|
+ if (is_type_matrix(type)) {
|
|
|
+ alignment *= 2; // NOTE(bill): Just in case
|
|
|
+ }
|
|
|
+
|
|
|
+ TB_Node *local = tb_inst_local(p->func, cast(u32)size, alignment);
|
|
|
+
|
|
|
+ if (zero_init) {
|
|
|
+ bool is_volatile = false;
|
|
|
+ TB_Node *zero = tb_inst_uint(p->func, TB_TYPE_I8, 0);
|
|
|
+ TB_Node *count = tb_inst_uint(p->func, TB_TYPE_I32, cast(u64)size);
|
|
|
+ tb_inst_memset(p->func, local, zero, count, alignment, is_volatile);
|
|
|
+ }
|
|
|
+
|
|
|
+ return cg_addr(cg_value(local, alloc_type_pointer(type)));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+gb_internal void cg_scope_open(cgProcedure *p, Scope *scope) {
|
|
|
+ // TODO(bill): cg_scope_open
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal void cg_scope_close(cgProcedure *p, cgDeferExitKind kind, TB_Node *control_region, bool pop_stack=true) {
|
|
|
+ // TODO(bill): cg_scope_close
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal void cg_emit_defer_stmts(cgProcedure *p, cgDeferExitKind kind, TB_Node *control_region) {
|
|
|
+ // TODO(bill): cg_emit_defer_stmts
|
|
|
+}
|
|
|
+
|
|
|
+gb_internal void cg_build_stmt(cgProcedure *p, Ast *node) {
|
|
|
+ Ast *prev_stmt = p->curr_stmt;
|
|
|
+ defer (p->curr_stmt = prev_stmt);
|
|
|
+ p->curr_stmt = node;
|
|
|
+
|
|
|
+ // TODO(bill): check if last instruction was a terminating one or not
|
|
|
+
|
|
|
+ {
|
|
|
+ TokenPos pos = ast_token(node).pos;
|
|
|
+ TB_FileID *file_id = map_get(&p->module->file_id_map, cast(uintptr)pos.file_id);
|
|
|
+ if (file_id) {
|
|
|
+ tb_inst_set_location(p->func, *file_id, pos.line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ u16 prev_state_flags = p->state_flags;
|
|
|
+ defer (p->state_flags = prev_state_flags);
|
|
|
+
|
|
|
+ if (node->state_flags != 0) {
|
|
|
+ u16 in = node->state_flags;
|
|
|
+ u16 out = p->state_flags;
|
|
|
+
|
|
|
+ if (in & StateFlag_bounds_check) {
|
|
|
+ out |= StateFlag_bounds_check;
|
|
|
+ out &= ~StateFlag_no_bounds_check;
|
|
|
+ } else if (in & StateFlag_no_bounds_check) {
|
|
|
+ out |= StateFlag_no_bounds_check;
|
|
|
+ out &= ~StateFlag_bounds_check;
|
|
|
+ }
|
|
|
+ if (in & StateFlag_no_type_assert) {
|
|
|
+ out |= StateFlag_no_type_assert;
|
|
|
+ out &= ~StateFlag_type_assert;
|
|
|
+ } else if (in & StateFlag_type_assert) {
|
|
|
+ out |= StateFlag_type_assert;
|
|
|
+ out &= ~StateFlag_no_type_assert;
|
|
|
+ }
|
|
|
+
|
|
|
+ p->state_flags = out;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (node->kind) {
|
|
|
+ case_ast_node(bs, EmptyStmt, node);
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ case_ast_node(us, UsingStmt, node);
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ case_ast_node(ws, WhenStmt, node);
|
|
|
+ cg_build_when_stmt(p, ws);
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ case_ast_node(bs, BlockStmt, node);
|
|
|
+ TB_Node *done = nullptr;
|
|
|
+ if (bs->label != nullptr) {
|
|
|
+ done = tb_inst_region(p->func);
|
|
|
+ tb_inst_set_region_name(done, -1, "block.done");
|
|
|
+ cgTargetList *tl = cg_push_target_list(p, bs->label, done, nullptr, nullptr);
|
|
|
+ tl->is_block = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ cg_scope_open(p, bs->scope);
|
|
|
+ cg_build_stmt_list(p, bs->stmts);
|
|
|
+ cg_scope_close(p, cgDeferExit_Default, nullptr);
|
|
|
+
|
|
|
+ if (done != nullptr) {
|
|
|
+ tb_inst_goto(p->func, done);
|
|
|
+ tb_inst_set_control(p->func, done);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bs->label != nullptr) {
|
|
|
+ cg_pop_target_list(p);
|
|
|
+ }
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ case_ast_node(vd, ValueDecl, node);
|
|
|
+ if (!vd->is_mutable) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool is_static = false;
|
|
|
+ if (vd->names.count > 0) {
|
|
|
+ for (Ast *name : vd->names) {
|
|
|
+ if (!is_blank_ident(name)) {
|
|
|
+ GB_ASSERT(name->kind == Ast_Ident);
|
|
|
+ Entity *e = entity_of_node(name);
|
|
|
+ TokenPos pos = ast_token(name).pos;
|
|
|
+ GB_ASSERT_MSG(e != nullptr, "\n%s missing entity for %.*s", token_pos_to_string(pos), LIT(name->Ident.token.string));
|
|
|
+ if (e->flags & EntityFlag_Static) {
|
|
|
+ // NOTE(bill): If one of the entities is static, they all are
|
|
|
+ is_static = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (is_static) {
|
|
|
+ GB_PANIC("TODO(bill): build static variables");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ TEMPORARY_ALLOCATOR_GUARD();
|
|
|
+
|
|
|
+ auto const &values = vd->values;
|
|
|
+ if (values.count == 0) {
|
|
|
+ for (Ast *name : vd->names) {
|
|
|
+ if (!is_blank_ident(name)) {
|
|
|
+ Entity *e = entity_of_node(name);
|
|
|
+ bool zero_init = true;
|
|
|
+ cgAddr addr = cg_add_local(p, e->type, e, zero_init);
|
|
|
+ gb_unused(addr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ GB_PANIC("TODO multiple variables");
|
|
|
+ }
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ case_ast_node(bs, BranchStmt, node);
|
|
|
+ TB_Node *prev_block = tb_inst_get_control(p->func);
|
|
|
+
|
|
|
+ TB_Node *block = nullptr;
|
|
|
+
|
|
|
+ if (bs->label != nullptr) {
|
|
|
+ cgBranchBlocks bb = cg_lookup_branch_blocks(p, bs->label);
|
|
|
+ switch (bs->token.kind) {
|
|
|
+ case Token_break: block = bb.break_; break;
|
|
|
+ case Token_continue: block = bb.continue_; break;
|
|
|
+ case Token_fallthrough:
|
|
|
+ GB_PANIC("fallthrough cannot have a label");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (cgTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
|
|
|
+ if (t->is_block) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (bs->token.kind) {
|
|
|
+ case Token_break: block = t->break_; break;
|
|
|
+ case Token_continue: block = t->continue_; break;
|
|
|
+ case Token_fallthrough: block = t->fallthrough_; break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (block != nullptr) {
|
|
|
+ cg_emit_defer_stmts(p, cgDeferExit_Branch, block);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ tb_inst_goto(p->func, block);
|
|
|
+ tb_inst_set_control(p->func, block);
|
|
|
+ tb_inst_unreachable(p->func);
|
|
|
+
|
|
|
+ tb_inst_set_control(p->func, prev_block);
|
|
|
+ case_end;
|
|
|
+
|
|
|
+ default:
|
|
|
+ GB_PANIC("TODO cg_build_stmt %.*s", LIT(ast_strings[node->kind]));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+gb_internal void cg_build_stmt_list(cgProcedure *p, Slice<Ast *> const &stmts) {
|
|
|
+ for (Ast *stmt : stmts) {
|
|
|
+ switch (stmt->kind) {
|
|
|
+ case_ast_node(vd, ValueDecl, stmt);
|
|
|
+ // TODO(bill)
|
|
|
+ // cg_build_constant_value_decl(p, vd);
|
|
|
+ case_end;
|
|
|
+ case_ast_node(fb, ForeignBlockDecl, stmt);
|
|
|
+ ast_node(block, BlockStmt, fb->body);
|
|
|
+ cg_build_stmt_list(p, block->stmts);
|
|
|
+ case_end;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (Ast *stmt : stmts) {
|
|
|
+ cg_build_stmt(p, stmt);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+gb_internal void cg_build_when_stmt(cgProcedure *p, AstWhenStmt *ws) {
|
|
|
+ TypeAndValue tv = type_and_value_of_expr(ws->cond);
|
|
|
+ GB_ASSERT(is_type_boolean(tv.type));
|
|
|
+ GB_ASSERT(tv.value.kind == ExactValue_Bool);
|
|
|
+ if (tv.value.value_bool) {
|
|
|
+ cg_build_stmt_list(p, ws->body->BlockStmt.stmts);
|
|
|
+ } else if (ws->else_stmt) {
|
|
|
+ switch (ws->else_stmt->kind) {
|
|
|
+ case Ast_BlockStmt:
|
|
|
+ cg_build_stmt_list(p, ws->else_stmt->BlockStmt.stmts);
|
|
|
+ break;
|
|
|
+ case Ast_WhenStmt:
|
|
|
+ cg_build_when_stmt(p, &ws->else_stmt->WhenStmt);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ GB_PANIC("Invalid 'else' statement in 'when' statement");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|