diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_decl.c | 53 | ||||
| -rw-r--r-- | src/check_expr.c | 46 | ||||
| -rw-r--r-- | src/checker.c | 11 | ||||
| -rw-r--r-- | src/entity.c | 11 | ||||
| -rw-r--r-- | src/gb/gb.h | 4 | ||||
| -rw-r--r-- | src/ir.c | 52 | ||||
| -rw-r--r-- | src/parser.c | 14 | ||||
| -rw-r--r-- | src/ssa.c | 1573 |
8 files changed, 1415 insertions, 349 deletions
diff --git a/src/check_decl.c b/src/check_decl.c index bc49e36c1..a89460123 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -408,6 +408,56 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } +void check_alias_decl(Checker *c, Entity *e, AstNode *expr) { + GB_ASSERT(e->type == NULL); + GB_ASSERT(e->kind == Entity_Alias); + + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + return; + } + e->flags |= EntityFlag_Visited; + e->type = t_invalid; + + expr = unparen_expr(expr); + + if (expr->kind == AstNode_Alias) { + error_node(expr, "#alias of an #alias is not allowed"); + return; + } + + if (expr->kind == AstNode_Ident) { + Operand o = {0}; + Entity *f = check_ident(c, &o, expr, NULL, NULL, true); + if (f != NULL) { + e->Alias.original = f; + e->type = f->type; + } + return; + } else if (expr->kind == AstNode_SelectorExpr) { + Operand o = {0}; + Entity *f = check_selector(c, &o, expr, NULL); + if (f != NULL) { + e->Alias.original = f; + e->type = f->type; + } + return; + } + + Operand o = {0}; + check_expr_or_type(c, &o, expr); + if (o.mode == Addressing_Invalid) { + return; + } + switch (o.mode) { + case Addressing_Type: + e->type = o.type; + break; + default: + error_node(expr, "#alias declarations only allow types"); + } +} + void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { if (e->type != NULL) { return; @@ -443,6 +493,9 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { case Entity_Procedure: check_proc_lit(c, e, d); break; + case Entity_Alias: + check_alias_decl(c, e, d->init_expr); + break; } c->context = prev; diff --git a/src/check_expr.c b/src/check_expr.c index 3f6e591c0..75105462b 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1028,7 +1028,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { } -void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint) { +Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint, bool allow_import_name) { GB_ASSERT(n->kind == AstNode_Ident); o->mode = Addressing_Invalid; o->expr = n; @@ -1046,7 +1046,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ if (named_type != NULL) { set_base_type(named_type, t_invalid); } - return; + return NULL; } bool is_overloaded = false; @@ -1095,7 +1095,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ o->type = t_invalid; o->overload_count = overload_count; o->overload_entities = procs; - return; + return NULL; } gb_free(heap_allocator(), procs); } @@ -1106,20 +1106,26 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ if (e->type == NULL) { compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(name)); - return; + return NULL; } - Type *type = e->type; + e->flags |= EntityFlag_Used; + + Entity *original_e = e; + while (e->kind == Entity_Alias && e->Alias.original != NULL) { + e = e->Alias.original; + } + Type *type = e->type; switch (e->kind) { case Entity_Constant: if (type == t_invalid) { o->type = t_invalid; - return; + return e; } o->value = e->Constant.value; if (o->value.kind == ExactValue_Invalid) { - return; + return e; } o->mode = Addressing_Constant; break; @@ -1128,7 +1134,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ e->flags |= EntityFlag_Used; if (type == t_invalid) { o->type = t_invalid; - return; + return e; } o->mode = Addressing_Variable; if (e->Variable.is_immutable) { @@ -1151,22 +1157,25 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ break; case Entity_ImportName: - error_node(n, "Use of import `%.*s` not in selector", LIT(e->ImportName.name)); - return; + if (!allow_import_name) { + error_node(n, "Use of import `%.*s` not in selector", LIT(name)); + } + return e; case Entity_LibraryName: - error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(e->LibraryName.name)); - return; + error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name)); + return e; case Entity_Nil: o->mode = Addressing_Value; break; default: - compiler_error("Compiler error: Unknown EntityKind"); + compiler_error("Unknown EntityKind"); break; } o->type = type; + return e; } i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { @@ -1342,7 +1351,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { switch (e->kind) { case_ast_node(i, Ident, e); Operand o = {0}; - check_ident(c, &o, e, named_type, NULL); + check_ident(c, &o, e, named_type, NULL, false); switch (o.mode) { case Addressing_Invalid: @@ -2679,6 +2688,11 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h add_entity_use(c, op_expr, e); expr_entity = e; + Entity *original_e = e; + while (e->kind == Entity_Alias && e->Alias.original != NULL) { + e = e->Alias.original; + } + if (e != NULL && e->kind == Entity_ImportName && selector->kind == AstNode_Ident) { // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile // It pretty much needs to be in this order and this way @@ -4413,7 +4427,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_end; case_ast_node(i, Ident, node); - check_ident(c, o, node, NULL, type_hint); + check_ident(c, o, node, NULL, type_hint, false); case_end; case_ast_node(bl, BasicLit, node); @@ -5626,7 +5640,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) { case_end; case_ast_node(ht, HelperType, node); - str = gb_string_appendc(str, "type "); + str = gb_string_appendc(str, "#type "); str = write_expr_to_string(str, ht->type); case_end; } diff --git a/src/checker.c b/src/checker.c index 2e2eceefc..881e86c8b 100644 --- a/src/checker.c +++ b/src/checker.c @@ -816,7 +816,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { return false; } error(entity->token, - "Redeclararation of `%.*s` in this scope through `using`\n" + "Redeclaration of `%.*s` in this scope through `using`\n" "\tat %.*s(%td:%td)", LIT(name), LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); @@ -827,7 +827,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { return false; } error(entity->token, - "Redeclararation of `%.*s` in this scope\n" + "Redeclaration of `%.*s` in this scope\n" "\tat %.*s(%td:%td)", LIT(name), LIT(pos.file), pos.line, pos.column); @@ -1467,7 +1467,12 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope) // TODO(bill): What if vd->type != NULL??? How to handle this case? d->type_expr = init; d->init_expr = init; - } else if (init != NULL && up_init->kind == AstNode_ProcLit) { + } else if (up_init != NULL && up_init->kind == AstNode_Alias) { + error_node(up_init, "#alias declarations are not yet supported"); + continue; + // e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, NULL); + // d->init_expr = init->Alias.expr; + }else if (init != NULL && up_init->kind == AstNode_ProcLit) { e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags); d->proc_lit = up_init; d->type_expr = vd->type; diff --git a/src/entity.c b/src/entity.c index 32d070953..446edfa54 100644 --- a/src/entity.c +++ b/src/entity.c @@ -13,6 +13,7 @@ typedef struct Type Type; ENTITY_KIND(Builtin) \ ENTITY_KIND(ImportName) \ ENTITY_KIND(LibraryName) \ + ENTITY_KIND(Alias) \ ENTITY_KIND(Nil) \ ENTITY_KIND(Count) @@ -95,6 +96,9 @@ struct Entity { String name; bool used; } LibraryName; + struct { + Entity *original; + } Alias; i32 Nil; }; }; @@ -218,6 +222,13 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type return entity; } +Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type, + Entity *original) { + Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type); + entity->Alias.original = original; + return entity; +} + Entity *make_entity_nil(gbAllocator a, String name, Type *type) { Token token = make_token_ident(name); Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type); diff --git a/src/gb/gb.h b/src/gb/gb.h index d30e5d129..9a665e050 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -806,6 +806,10 @@ GB_DEF void const *gb_memchr (void const *data, u8 byte_value, isize size); GB_DEF void const *gb_memrchr (void const *data, u8 byte_value, isize size); +#ifndef gb_memcopy_array +#define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count)) +#endif + // NOTE(bill): Very similar to doing `*cast(T *)(&u)` #ifndef GB_BIT_CAST #define GB_BIT_CAST(dest, source) do { \ @@ -2916,8 +2916,21 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) { void ir_build_stmt_list(irProcedure *proc, AstNodeArray stmts); -irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv) { + +irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { expr = unparen_expr(expr); + + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT_NOT_NULL(tv); + + if (tv->value.kind != ExactValue_Invalid) { + return ir_add_module_constant(proc->module, tv->type, tv->value); + } + + if (tv->mode == Addressing_Variable) { + return ir_addr_load(proc, ir_build_addr(proc, expr)); + } + switch (expr->kind) { case_ast_node(bl, BasicLit, expr); TokenPos pos = bl->pos; @@ -3782,27 +3795,6 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv return NULL; } - -irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { - expr = unparen_expr(expr); - - TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); - GB_ASSERT_NOT_NULL(tv); - - if (tv->value.kind != ExactValue_Invalid) { - return ir_add_module_constant(proc->module, tv->type, tv->value); - } - - irValue *value = NULL; - if (tv->mode == Addressing_Variable) { - value = ir_addr_load(proc, ir_build_addr(proc, expr)); - } else { - value = ir_build_single_expr(proc, expr, tv); - } - - return value; -} - irValue *ir_get_using_variable(irProcedure *proc, Entity *e) { GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); String name = e->token.string; @@ -5192,9 +5184,9 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { if (fs->cond != NULL) { loop = ir_new_block(proc, node, "for.loop"); } - irBlock *cont = loop; + irBlock *post = loop; if (fs->post != NULL) { - cont = ir_new_block(proc, node, "for.post"); + post = ir_new_block(proc, node, "for.post"); } ir_emit_jump(proc, loop); ir_start_block(proc, loop); @@ -5204,7 +5196,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_start_block(proc, body); } - ir_push_target_list(proc, done, cont, NULL); + ir_push_target_list(proc, done, post, NULL); ir_open_scope(proc); ir_build_stmt(proc, fs->body); @@ -5212,10 +5204,10 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_pop_target_list(proc); - ir_emit_jump(proc, cont); + ir_emit_jump(proc, post); if (fs->post != NULL) { - ir_start_block(proc, cont); + ir_start_block(proc, post); ir_build_stmt(proc, fs->post); ir_emit_jump(proc, loop); } @@ -5646,7 +5638,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { case_end; - case_ast_node(pa, PushContext, node); + case_ast_node(pc, PushContext, node); ir_emit_comment(proc, str_lit("PushContext")); ir_open_scope(proc); @@ -5656,9 +5648,9 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context))); - ir_emit_store(proc, context_ptr, ir_build_expr(proc, pa->expr)); + ir_emit_store(proc, context_ptr, ir_build_expr(proc, pc->expr)); - ir_build_stmt(proc, pa->body); + ir_build_stmt(proc, pc->body); ir_close_scope(proc, irDeferExit_Default, NULL); case_end; diff --git a/src/parser.c b/src/parser.c index 160b72ef3..2905489d6 100644 --- a/src/parser.c +++ b/src/parser.c @@ -139,6 +139,10 @@ AstNodeArray make_ast_node_array(AstFile *f) { AstNodeArray elems; \ Token open, close; \ }) \ + AST_NODE_KIND(Alias, "alias", struct { \ + Token token; \ + AstNode *expr; \ + }) \ AST_NODE_KIND(_ExprBegin, "", i32) \ AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \ @@ -445,6 +449,8 @@ Token ast_node_token(AstNode *node) { return ast_node_token(node->CompoundLit.type); } return node->CompoundLit.open; + case AstNode_Alias: return node->Alias.token; + case AstNode_TagExpr: return node->TagExpr.token; case AstNode_RunExpr: return node->RunExpr.token; case AstNode_BadExpr: return node->BadExpr.begin; @@ -771,6 +777,13 @@ AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token o result->CompoundLit.close = close; return result; } +AstNode *ast_alias(AstFile *f, Token token, AstNode *expr) { + AstNode *result = make_ast_node(f, AstNode_Alias); + result->Alias.token = token; + result->Alias.expr = expr; + return result; +} + AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) { AstNode *result = make_ast_node(f, AstNode_TernaryExpr); @@ -1762,6 +1775,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { } else if (str_eq(name.string, str_lit("line"))) { return ast_basic_directive(f, token, name.string); } else if (str_eq(name.string, str_lit("procedure"))) { return ast_basic_directive(f, token, name.string); } else if (str_eq(name.string, str_lit("type"))) { return ast_helper_type(f, token, parse_type(f)); + } else if (!lhs && str_eq(name.string, str_lit("alias"))) { return ast_alias(f, token, parse_expr(f, false)); } else { operand = ast_tag_expr(f, token, name, parse_expr(f, false)); } @@ -1,12 +1,13 @@ -typedef enum ssaOp ssaOp; -typedef struct ssaModule ssaModule; -typedef struct ssaValue ssaValue; -typedef struct ssaBlock ssaBlock; -typedef struct ssaProc ssaProc; -typedef struct ssaEdge ssaEdge; -typedef struct ssaRegister ssaRegister; -typedef enum ssaBlockKind ssaBlockKind; -typedef enum ssaBranchPrediction ssaBranchPrediction; +typedef enum ssaOp ssaOp; +typedef struct ssaModule ssaModule; +typedef struct ssaValue ssaValue; +typedef struct ssaBlock ssaBlock; +typedef struct ssaProc ssaProc; +typedef struct ssaEdge ssaEdge; +typedef struct ssaRegister ssaRegister; +typedef struct ssaTargetList ssaTargetList; +typedef enum ssaBlockKind ssaBlockKind; +typedef enum ssaBranchPrediction ssaBranchPrediction; String ssa_mangle_name(ssaModule *m, String path, Entity *e); @@ -17,265 +18,280 @@ String ssa_mangle_name(ssaModule *m, String path, Entity *e); typedef Array(ssaValue *) ssaValueArray; + +#define SSA_OPS \ + SSA_OP(Invalid)\ +\ + SSA_OP(Unknown)\ +\ + SSA_OP(Comment) /* Does nothing */\ +\ + SSA_OP(SP) /* Stack Pointer */\ + SSA_OP(SB) /* Stack Base */\ + SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\ +\ + SSA_OP(Local)\ + SSA_OP(Global)\ + SSA_OP(Proc)\ +\ + SSA_OP(Load)\ + SSA_OP(Store)\ + SSA_OP(Move)\ + SSA_OP(Zero) /* Zero initialize */\ +\ + SSA_OP(ArrayIndex) /* Index for a fixed array */\ + SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\ + SSA_OP(OffsetPtr)\ + SSA_OP(ValueIndex) /* Extract for a value from a register */\ +\ + SSA_OP(Phi)\ + SSA_OP(Copy)\ +\ + /* TODO(bill): calling conventions */\ + SSA_OP(CallOdin)\ + SSA_OP(CallC)\ + SSA_OP(CallStd)\ + SSA_OP(CallFast)\ +\ + SSA_OP(BoundsCheck)\ + SSA_OP(SliceBoundsCheck)\ +\ + /* Built in operations/procedures */\ + SSA_OP(Bswap16)\ + SSA_OP(Bswap32)\ + SSA_OP(Bswap64)\ +\ + SSA_OP(Assume)\ + SSA_OP(DebugTrap)\ + SSA_OP(Trap)\ + SSA_OP(ReadCycleCounter)\ +\ +\ + SSA_OP(ConstBool)\ + SSA_OP(ConstString)\ + SSA_OP(ConstSlice)\ + SSA_OP(ConstNil)\ + SSA_OP(Const8)\ + SSA_OP(Const16)\ + SSA_OP(Const32)\ + SSA_OP(Const64)\ + SSA_OP(Const32F)\ + SSA_OP(Const64F)\ +\ + /* These should be all the operations I could possibly need for the mean time */\ + SSA_OP(Add8)\ + SSA_OP(Add16)\ + SSA_OP(Add32)\ + SSA_OP(Add64)\ + SSA_OP(AddPtr)\ + SSA_OP(Add32F)\ + SSA_OP(Add64F)\ + SSA_OP(Sub8)\ + SSA_OP(Sub16)\ + SSA_OP(Sub32)\ + SSA_OP(Sub64)\ + SSA_OP(SubPtr)\ + SSA_OP(Sub32F)\ + SSA_OP(Sub64F)\ + SSA_OP(Mul8)\ + SSA_OP(Mul16)\ + SSA_OP(Mul32)\ + SSA_OP(Mul64)\ + SSA_OP(Mul32F)\ + SSA_OP(Mul64F)\ + SSA_OP(Div8)\ + SSA_OP(Div8U)\ + SSA_OP(Div16)\ + SSA_OP(Div16U)\ + SSA_OP(Div32)\ + SSA_OP(Div32U)\ + SSA_OP(Div64)\ + SSA_OP(Div64U)\ + SSA_OP(Div32F)\ + SSA_OP(Div64F)\ + SSA_OP(Mod8)\ + SSA_OP(Mod8U)\ + SSA_OP(Mod16)\ + SSA_OP(Mod16U)\ + SSA_OP(Mod32)\ + SSA_OP(Mod32U)\ + SSA_OP(Mod64)\ + SSA_OP(Mod64U)\ +\ + SSA_OP(And8)\ + SSA_OP(And16)\ + SSA_OP(And32)\ + SSA_OP(And64)\ + SSA_OP(Or8)\ + SSA_OP(Or16)\ + SSA_OP(Or32)\ + SSA_OP(Or64)\ + SSA_OP(Xor8)\ + SSA_OP(Xor16)\ + SSA_OP(Xor32)\ + SSA_OP(Xor64)\ + SSA_OP(AndNot8)\ + SSA_OP(AndNot16)\ + SSA_OP(AndNot32)\ + SSA_OP(AndNot64)\ +\ + SSA_OP(Lsh8x8)\ + SSA_OP(Lsh8x16)\ + SSA_OP(Lsh8x32)\ + SSA_OP(Lsh8x64)\ + SSA_OP(Lsh16x8)\ + SSA_OP(Lsh16x16)\ + SSA_OP(Lsh16x32)\ + SSA_OP(Lsh16x64)\ + SSA_OP(Lsh32x8)\ + SSA_OP(Lsh32x16)\ + SSA_OP(Lsh32x32)\ + SSA_OP(Lsh32x64)\ + SSA_OP(Lsh64x8)\ + SSA_OP(Lsh64x16)\ + SSA_OP(Lsh64x32)\ + SSA_OP(Lsh64x64)\ + SSA_OP(Rsh8x8)\ + SSA_OP(Rsh8x16)\ + SSA_OP(Rsh8x32)\ + SSA_OP(Rsh8x64)\ + SSA_OP(Rsh16x8)\ + SSA_OP(Rsh16x16)\ + SSA_OP(Rsh16x32)\ + SSA_OP(Rsh16x64)\ + SSA_OP(Rsh32x8)\ + SSA_OP(Rsh32x16)\ + SSA_OP(Rsh32x32)\ + SSA_OP(Rsh32x64)\ + SSA_OP(Rsh64x8)\ + SSA_OP(Rsh64x16)\ + SSA_OP(Rsh64x32)\ + SSA_OP(Rsh64x64)\ + SSA_OP(Rsh8Ux8)\ + SSA_OP(Rsh8Ux16)\ + SSA_OP(Rsh8Ux32)\ + SSA_OP(Rsh8Ux64)\ + SSA_OP(Rsh16Ux8)\ + SSA_OP(Rsh16Ux16)\ + SSA_OP(Rsh16Ux32)\ + SSA_OP(Rsh16Ux64)\ + SSA_OP(Rsh32Ux8)\ + SSA_OP(Rsh32Ux16)\ + SSA_OP(Rsh32Ux32)\ + SSA_OP(Rsh32Ux64)\ + SSA_OP(Rsh64Ux8)\ + SSA_OP(Rsh64Ux16)\ + SSA_OP(Rsh64Ux32)\ + SSA_OP(Rsh64Ux64)\ +\ + SSA_OP(Eq8)\ + SSA_OP(Eq16)\ + SSA_OP(Eq32)\ + SSA_OP(Eq64)\ + SSA_OP(EqPtr)\ + SSA_OP(Eq32F)\ + SSA_OP(Eq64F)\ + SSA_OP(Ne8)\ + SSA_OP(Ne16)\ + SSA_OP(Ne32)\ + SSA_OP(Ne64)\ + SSA_OP(NePtr)\ + SSA_OP(Ne32F)\ + SSA_OP(Ne64F)\ + SSA_OP(Lt8)\ + SSA_OP(Lt16)\ + SSA_OP(Lt32)\ + SSA_OP(Lt64)\ + SSA_OP(LtPtr)\ + SSA_OP(Lt32F)\ + SSA_OP(Lt64F)\ + SSA_OP(Gt8)\ + SSA_OP(Gt16)\ + SSA_OP(Gt32)\ + SSA_OP(Gt64)\ + SSA_OP(GtPtr)\ + SSA_OP(Gt32F)\ + SSA_OP(Gt64F)\ + SSA_OP(Le8)\ + SSA_OP(Le16)\ + SSA_OP(Le32)\ + SSA_OP(Le64)\ + SSA_OP(LePtr)\ + SSA_OP(Le32F)\ + SSA_OP(Le64F)\ + SSA_OP(Ge8)\ + SSA_OP(Ge16)\ + SSA_OP(Ge32)\ + SSA_OP(Ge64)\ + SSA_OP(GePtr)\ + SSA_OP(Ge32F)\ + SSA_OP(Ge64F)\ +\ + SSA_OP(NotB)\ + SSA_OP(EqB)\ + SSA_OP(NeB)\ +\ + SSA_OP(Neg8)\ + SSA_OP(Neg16)\ + SSA_OP(Neg32)\ + SSA_OP(Neg64)\ + SSA_OP(Neg32F)\ + SSA_OP(Neg64F)\ +\ + SSA_OP(Not8)\ + SSA_OP(Not16)\ + SSA_OP(Not32)\ + SSA_OP(Not64)\ +\ + SSA_OP(SignExt8to16)\ + SSA_OP(SignExt8to32)\ + SSA_OP(SignExt8to64)\ + SSA_OP(SignExt16to32)\ + SSA_OP(SignExt16to64)\ + SSA_OP(SignExt32to64)\ + SSA_OP(ZeroExt8to16)\ + SSA_OP(ZeroExt8to32)\ + SSA_OP(ZeroExt8to64)\ + SSA_OP(ZeroExt16to32)\ + SSA_OP(ZeroExt16to64)\ + SSA_OP(ZeroExt32to64)\ + SSA_OP(Trunc16to8)\ + SSA_OP(Trunc32to8)\ + SSA_OP(Trunc32to16)\ + SSA_OP(Trunc64to8)\ + SSA_OP(Trunc64to16)\ + SSA_OP(Trunc64to32)\ +\ + SSA_OP(Cvt32to32F)\ + SSA_OP(Cvt32to64F)\ + SSA_OP(Cvt64to32F)\ + SSA_OP(Cvt64to64F)\ + SSA_OP(Cvt32Fto32)\ + SSA_OP(Cvt32Fto64)\ + SSA_OP(Cvt64Fto32)\ + SSA_OP(Cvt64Fto64)\ + SSA_OP(Cvt32Fto64F)\ + SSA_OP(Cvt64Fto32F)\ + SSA_OP(Cvt32Uto32F)\ + SSA_OP(Cvt32Uto64F)\ + SSA_OP(Cvt32Fto32U)\ + SSA_OP(Cvt64Fto32U)\ + SSA_OP(Cvt64Uto32F)\ + SSA_OP(Cvt64Uto64F)\ + SSA_OP(Cvt32Fto64U)\ + SSA_OP(Cvt64Fto64U)\ + + enum ssaOp { - ssaOp_Invalid, - - ssaOp_Unknown, - - ssaOp_Comment, // Does nothing - - ssaOp_SP, // Stack Pointer - ssaOp_SB, // Stack Base - ssaOp_Addr, // Address of something - special rules for certain types when loading and storing (e.g. Maps) - - ssaOp_Local, - ssaOp_Global, - ssaOp_Proc, - - ssaOp_Load, - ssaOp_Store, - ssaOp_Move, - ssaOp_Zero, // Zero initialize - - ssaOp_ArrayIndex, // Index for a fixed array - ssaOp_PtrIndex, // Index for a struct/tuple/etc - ssaOp_OffsetPtr, - ssaOp_ValueIndex, // Extract for a value from a register - - ssaOp_Phi, - ssaOp_Copy, - - // TODO(bill): calling conventions - ssaOp_CallOdin, - ssaOp_CallC, - ssaOp_CallStd, - ssaOp_CallFast, - - ssaOp_BoundsCheck, - ssaOp_SliceBoundsCheck, - - // Built in operations/procedures - ssaOp_Bswap16, - ssaOp_Bswap32, - ssaOp_Bswap64, - - ssaOp_Assume, - ssaOp_DebugTrap, - ssaOp_Trap, - ssaOp_ReadCycleCounter, - - - ssaOp_ConstBool, - ssaOp_ConstString, - ssaOp_ConstSlice, - ssaOp_ConstNil, - ssaOp_Const8, - ssaOp_Const16, - ssaOp_Const32, - ssaOp_Const64, - ssaOp_Const32F, - ssaOp_Const64F, - - // These should be all the operations I could possibly need for the mean time - ssaOp_Add8, - ssaOp_Add16, - ssaOp_Add32, - ssaOp_Add64, - ssaOp_AddPtr, - ssaOp_Add32F, - ssaOp_Add64F, - ssaOp_Sub8, - ssaOp_Sub16, - ssaOp_Sub32, - ssaOp_Sub64, - ssaOp_SubPtr, - ssaOp_Sub32F, - ssaOp_Sub64F, - ssaOp_Mul8, - ssaOp_Mul16, - ssaOp_Mul32, - ssaOp_Mul64, - ssaOp_Mul32F, - ssaOp_Mul64F, - ssaOp_Div8, - ssaOp_Div8U, - ssaOp_Div16, - ssaOp_Div16U, - ssaOp_Div32, - ssaOp_Div32U, - ssaOp_Div64, - ssaOp_Div64U, - ssaOp_Div32F, - ssaOp_Div64F, - ssaOp_Mod8, - ssaOp_Mod8U, - ssaOp_Mod16, - ssaOp_Mod16U, - ssaOp_Mod32, - ssaOp_Mod32U, - ssaOp_Mod64, - ssaOp_Mod64U, - - ssaOp_And8, - ssaOp_And16, - ssaOp_And32, - ssaOp_And64, - ssaOp_Or8, - ssaOp_Or16, - ssaOp_Or32, - ssaOp_Or64, - ssaOp_Xor8, - ssaOp_Xor16, - ssaOp_Xor32, - ssaOp_Xor64, - - ssaOp_Lsh8x8, - ssaOp_Lsh8x16, - ssaOp_Lsh8x32, - ssaOp_Lsh8x64, - ssaOp_Lsh16x8, - ssaOp_Lsh16x16, - ssaOp_Lsh16x32, - ssaOp_Lsh16x64, - ssaOp_Lsh32x8, - ssaOp_Lsh32x16, - ssaOp_Lsh32x32, - ssaOp_Lsh32x64, - ssaOp_Lsh64x8, - ssaOp_Lsh64x16, - ssaOp_Lsh64x32, - ssaOp_Lsh64x64, - ssaOp_Rsh8x8, - ssaOp_Rsh8x16, - ssaOp_Rsh8x32, - ssaOp_Rsh8x64, - ssaOp_Rsh16x8, - ssaOp_Rsh16x16, - ssaOp_Rsh16x32, - ssaOp_Rsh16x64, - ssaOp_Rsh32x8, - ssaOp_Rsh32x16, - ssaOp_Rsh32x32, - ssaOp_Rsh32x64, - ssaOp_Rsh64x8, - ssaOp_Rsh64x16, - ssaOp_Rsh64x32, - ssaOp_Rsh64x64, - ssaOp_Rsh8Ux8, - ssaOp_Rsh8Ux16, - ssaOp_Rsh8Ux32, - ssaOp_Rsh8Ux64, - ssaOp_Rsh16Ux8, - ssaOp_Rsh16Ux16, - ssaOp_Rsh16Ux32, - ssaOp_Rsh16Ux64, - ssaOp_Rsh32Ux8, - ssaOp_Rsh32Ux16, - ssaOp_Rsh32Ux32, - ssaOp_Rsh32Ux64, - ssaOp_Rsh64Ux8, - ssaOp_Rsh64Ux16, - ssaOp_Rsh64Ux32, - ssaOp_Rsh64Ux64, - - ssaOp_Eq8, - ssaOp_Eq16, - ssaOp_Eq32, - ssaOp_Eq64, - ssaOp_EqPtr, - ssaOp_Eq32F, - ssaOp_Eq64F, - ssaOp_Ne8, - ssaOp_Ne16, - ssaOp_Ne32, - ssaOp_Ne64, - ssaOp_NePtr, - ssaOp_Ne32F, - ssaOp_Ne64F, - ssaOp_Lt8, - ssaOp_Lt16, - ssaOp_Lt32, - ssaOp_Lt64, - ssaOp_LtPtr, - ssaOp_Lt32F, - ssaOp_Lt64F, - ssaOp_Gt8, - ssaOp_Gt16, - ssaOp_Gt32, - ssaOp_Gt64, - ssaOp_GtPtr, - ssaOp_Gt32F, - ssaOp_Gt64F, - ssaOp_Le8, - ssaOp_Le16, - ssaOp_Le32, - ssaOp_Le64, - ssaOp_LePtr, - ssaOp_Le32F, - ssaOp_Le64F, - ssaOp_Ge8, - ssaOp_Ge16, - ssaOp_Ge32, - ssaOp_Ge64, - ssaOp_GePtr, - ssaOp_Ge32F, - ssaOp_Ge64F, - - ssaOp_NotB, - ssaOp_EqB, - ssaOp_NeB, - - ssaOp_Neg8, - ssaOp_Neg16, - ssaOp_Neg32, - ssaOp_Neg64, - ssaOp_Neg32F, - ssaOp_Neg64F, - - ssaOp_Not8, - ssaOp_Not16, - ssaOp_Not32, - ssaOp_Not64, - - ssaOp_SignExt8to16, - ssaOp_SignExt8to32, - ssaOp_SignExt8to64, - ssaOp_SignExt16to32, - ssaOp_SignExt16to64, - ssaOp_SignExt32to64, - ssaOp_ZeroExt8to16, - ssaOp_ZeroExt8to32, - ssaOp_ZeroExt8to64, - ssaOp_ZeroExt16to32, - ssaOp_ZeroExt16to64, - ssaOp_ZeroExt32to64, - ssaOp_Trunc16to8, - ssaOp_Trunc32to8, - ssaOp_Trunc32to16, - ssaOp_Trunc64to8, - ssaOp_Trunc64to16, - ssaOp_Trunc64to32, - - ssaOp_Cvt32to32F, - ssaOp_Cvt32to64F, - ssaOp_Cvt64to32F, - ssaOp_Cvt64to64F, - ssaOp_Cvt32Fto32, - ssaOp_Cvt32Fto64, - ssaOp_Cvt64Fto32, - ssaOp_Cvt64Fto64, - ssaOp_Cvt32Fto64F, - ssaOp_Cvt64Fto32F, - ssaOp_Cvt32Uto32F, - ssaOp_Cvt32Uto64F, - ssaOp_Cvt32Fto32U, - ssaOp_Cvt64Fto32U, - ssaOp_Cvt64Uto32F, - ssaOp_Cvt64Uto64F, - ssaOp_Cvt32Fto64U, - ssaOp_Cvt64Fto64U, - - ssaOp_Count, +#define SSA_OP(k) GB_JOIN2(ssaOp_, k), + SSA_OPS +#undef SSA_OP +}; + +String const ssa_op_strings[] = { +#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1}, + SSA_OPS +#undef SSA_OP }; #define SSA_MAX_ARGS 4 @@ -295,6 +311,8 @@ struct ssaValue { ssaValueArray var_args; // Only used in procedure calls as the SSA_MAX_ARGS may be too small ExactValue exact_value; // Used for constants + + String comment_string; }; enum ssaBlockKind { @@ -333,15 +351,29 @@ struct ssaBlock { i32 id; // Unique identifier but the pointer could be used too ssaBlockKind kind; ssaProc * proc; // Containing procedure + String name; // Optional // Likely branch direction ssaBranchPrediction likeliness; + // Determines how a block exits + // It depends on the type of block: + // - BlockIf will be a boolean value + // - BlockExit will be a memory control value + ssaValue *control; + ssaValueArray values; ssaEdgeArray preds; ssaEdgeArray succs; }; +struct ssaTargetList { + ssaTargetList *prev; + ssaBlock * break_; + ssaBlock * continue_; + ssaBlock * fallthrough_; +}; + struct ssaProc { ssaModule * module; // Parent module String name; // Mangled name @@ -350,8 +382,11 @@ struct ssaProc { Array(ssaBlock *) blocks; ssaBlock * entry; // Entry block + ssaBlock * exit; // Exit block ssaBlock * curr_block; + ssaTargetList * target_list; + i32 block_id; i32 value_id; MapSsaValue values; // Key: Entity * @@ -385,14 +420,28 @@ struct ssaModule { }; +void ssa_push_target_list(ssaProc *p, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { + ssaTargetList *tl = gb_alloc_item(p->module->allocator, ssaTargetList); + tl->prev = p->target_list; + tl->break_ = break_; + tl->continue_ = continue_; + tl->fallthrough_ = fallthrough_; + p->target_list = tl; +} +void ssa_pop_target_list(ssaProc *p) { + p->target_list = p->target_list->prev; +} -ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind) { +ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind, char *name) { ssaBlock *b = gb_alloc_item(p->module->allocator, ssaBlock); b->id = p->block_id++; b->kind = kind; b->proc = p; + if (name != NULL || name[0] != 0) { + b->name = make_string_c(name); + } array_init(&b->values, heap_allocator()); array_init(&b->preds, heap_allocator()); @@ -425,7 +474,11 @@ ssaBlock *ssa_end_block(ssaProc *p) { return b; } -void ssa_add_to_edge(ssaBlock *b, ssaBlock *c) { +void ssa_add_edge_to(ssaBlock *b, ssaBlock *c) { + if (b == NULL) { + return; + } + GB_ASSERT(c != NULL); isize i = b->succs.count; isize j = b->preds.count; ssaEdge s = {c, j}; @@ -434,6 +487,37 @@ void ssa_add_to_edge(ssaBlock *b, ssaBlock *c) { array_add(&c->preds, p); } +void ssa_set_control(ssaBlock *b, ssaValue *v) { + if (b->control != NULL) { + b->control->uses--; + } + b->control = v; + if (v != NULL) { + v->uses++; + } +} + +void ssa_emit_jump(ssaProc *p, ssaBlock *edge) { + ssa_add_edge_to(ssa_end_block(p), edge); +} + + +bool ssa_op_uses_var_args(ssaOp op) { + switch (op) { + case ssaOp_CallOdin: + case ssaOp_CallC: + case ssaOp_CallStd: + case ssaOp_CallFast: + return true; + + case ssaOp_Phi: + return true; + } + return false; +} + + + ssaValue *ssa_new_value(ssaProc *p, ssaOp op, Type *t, ssaBlock *b) { ssaValue *v = gb_alloc_item(p->module->allocator, ssaValue); @@ -504,17 +588,44 @@ ssaValue *ssa_const_val(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) { return ssa_new_value0v(p->curr_block, op, t, exact_value); } -ssaValue *ssa_const_bool (ssaProc *p, Type *t, bool c) { return ssa_const_val(p, ssaOp_ConstBool, t, exact_value_bool(c)); } -ssaValue *ssa_const_i8 (ssaProc *p, Type *t, i8 c) { return ssa_const_val(p, ssaOp_Const8, t, exact_value_integer(cast(i64)c)); } -ssaValue *ssa_const_i16 (ssaProc *p, Type *t, i16 c) { return ssa_const_val(p, ssaOp_Const16, t, exact_value_integer(cast(i64)c)); } -ssaValue *ssa_const_i32 (ssaProc *p, Type *t, i32 c) { return ssa_const_val(p, ssaOp_Const32, t, exact_value_integer(cast(i64)c)); } -ssaValue *ssa_const_i64 (ssaProc *p, Type *t, i64 c) { return ssa_const_val(p, ssaOp_Const64, t, exact_value_integer(cast(i64)c)); } -ssaValue *ssa_const_f32 (ssaProc *p, Type *t, f32 c) { return ssa_const_val(p, ssaOp_Const32F, t, exact_value_float(c)); } -ssaValue *ssa_const_f64 (ssaProc *p, Type *t, f64 c) { return ssa_const_val(p, ssaOp_Const64F, t, exact_value_float(c)); } -ssaValue *ssa_const_string (ssaProc *p, Type *t, String c) { return ssa_const_val(p, ssaOp_ConstString, t, exact_value_string(c)); } -ssaValue *ssa_const_empty_string(ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstString, t, (ExactValue){0}); } -ssaValue *ssa_const_slice (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstSlice, t, (ExactValue){0}); } -ssaValue *ssa_const_nil (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstNil, t, (ExactValue){0}); } +ssaValue *ssa_const_bool (ssaProc *p, Type *t, bool c) { return ssa_const_val(p, ssaOp_ConstBool, t, exact_value_bool(c)); } +ssaValue *ssa_const_i8 (ssaProc *p, Type *t, i8 c) { return ssa_const_val(p, ssaOp_Const8, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i16 (ssaProc *p, Type *t, i16 c) { return ssa_const_val(p, ssaOp_Const16, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i32 (ssaProc *p, Type *t, i32 c) { return ssa_const_val(p, ssaOp_Const32, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i64 (ssaProc *p, Type *t, i64 c) { return ssa_const_val(p, ssaOp_Const64, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_f32 (ssaProc *p, Type *t, f32 c) { return ssa_const_val(p, ssaOp_Const32F, t, exact_value_float(c)); } +ssaValue *ssa_const_f64 (ssaProc *p, Type *t, f64 c) { return ssa_const_val(p, ssaOp_Const64F, t, exact_value_float(c)); } +ssaValue *ssa_const_string (ssaProc *p, Type *t, String c) { return ssa_const_val(p, ssaOp_ConstString, t, exact_value_string(c)); } +ssaValue *ssa_const_empty_string(ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstString, t, (ExactValue){0}); } +ssaValue *ssa_const_slice (ssaProc *p, Type *t, ExactValue v) { return ssa_const_val(p, ssaOp_ConstSlice, t, v); } +ssaValue *ssa_const_nil (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstNil, t, (ExactValue){0}); } + +ssaValue *ssa_const_int(ssaProc *p, Type *t, i64 c) { + switch (8*type_size_of(p->module->allocator, t)) { + case 8: return ssa_const_i8 (p, t, cast(i8)c); + case 16: return ssa_const_i16(p, t, cast(i16)c); + case 32: return ssa_const_i32(p, t, cast(i32)c); + case 64: return ssa_const_i64(p, t, cast(i64)c); + } + GB_PANIC("Unknown int size"); + return NULL; +} + +void ssa_reset_value_args(ssaValue *v) { + if (ssa_op_uses_var_args(v->op)) { + for_array(i, v->var_args) { + v->var_args.e[i]->uses--; + } + v->var_args.count = 0; + } else { + for (isize i = 0; i < v->arg_count; i++) { + v->args[i]->uses--; + } + v->arg_count = 0; + } +} + + bool ssa_is_blank_ident(AstNode *node) { if (node->kind == AstNode_Ident) { @@ -527,6 +638,7 @@ bool ssa_is_blank_ident(AstNode *node) { typedef enum ssaAddrKind { ssaAddr_Default, + ssaAddr_Map, } ssaAddrKind; typedef struct ssaAddr { @@ -543,6 +655,21 @@ ssaAddr ssa_addr(ssaValue *v) { return addr; } +Type *ssa_addr_type(ssaAddr addr) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == ssaAddr_Map) { + GB_PANIC("TODO: ssa_addr_type"); + return NULL; + } + + Type *t = addr.addr->type; + GB_ASSERT(is_type_pointer(t)); + return type_deref(t); +} + ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_info) { @@ -559,27 +686,24 @@ ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_ } ssaAddr ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) { - ssaAddr result = {0}; - Type *t = make_type_pointer(p->module->allocator, e->type); ssaValue *local = ssa_new_value0(p->entry, ssaOp_Local, t); map_ssa_value_set(&p->values, hash_pointer(e), local); map_ssa_value_set(&p->module->values, hash_pointer(e), local); + local->comment_string = e->token.string; ssaValue *addr = ssa_new_value1(p->curr_block, ssaOp_Addr, local->type, local); ssa_new_value1(p->curr_block, ssaOp_Zero, t, addr); - result.addr = addr; - return result; + return ssa_addr(addr); } ssaAddr ssa_add_local_for_ident(ssaProc *p, AstNode *name) { - ssaAddr result = {0}; - Entity **found = map_entity_get(&p->module->info->definitions, hash_pointer(name)); if (found) { Entity *e = *found; return ssa_add_local(p, e, name); } - return result; + + return ssa_addr(NULL); } ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) { @@ -594,12 +718,9 @@ ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) { } void ssa_emit_comment(ssaProc *p, String s) { - ssa_new_value0v(p->curr_block, ssaOp_Comment, NULL, exact_value_string(s)); + // ssa_new_value0v(p->curr_block, ssaOp_Comment, NULL, exact_value_string(s)); } - - - void ssa_build_stmt(ssaProc *p, AstNode *node); void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes); @@ -607,12 +728,312 @@ void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) { if (addr.addr == NULL) { return; } + if (addr.kind == ssaAddr_Map) { + GB_PANIC("TODO(bill): ssa_addr_store"); + return; + } + + ssa_new_value2(p->curr_block, ssaOp_Store, addr.addr->type, addr.addr, value); +} + +ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == ssaAddr_Map) { + GB_PANIC("here\n"); + return NULL; + } + + Type *t = addr.addr->type; + Type *bt = base_type(t); + if (bt->kind == Type_Proc) { + return addr.addr; + } + + return ssa_new_value1(p->curr_block, ssaOp_Load, type_deref(t), addr.addr); +} + +ssaValue *ssa_get_using_variable(ssaProc *p, Entity *e) { + GB_PANIC("TODO(bill): ssa_get_using_variable"); + return NULL; + // GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); + // String name = e->token.string; + // Entity *parent = e->using_parent; + // Selection sel = lookup_field(proc->module->allocator, parent->type, name, false); + // GB_ASSERT(sel.entity != NULL); + // irValue **pv = map_ir_value_get(&proc->module->values, hash_pointer(parent)); + // irValue *v = NULL; + // if (pv != NULL) { + // v = *pv; + // } else { + // v = ir_build_addr(proc, e->using_expr).addr; + // } + // GB_ASSERT(v != NULL); + // return ir_emit_deep_field_gep(proc, parent->type, v, sel); } -ssaAddr ssa_build_addr(ssaProc *p, AstNode *node) { +ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) { + GB_ASSERT(e != NULL); + + ssaValue *v = NULL; + ssaValue **found = map_ssa_value_get(&p->module->values, hash_pointer(e)); + if (found) { + v = *found; + } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + // NOTE(bill): Calculate the using variable every time + v = ssa_get_using_variable(p, e); + } + + if (v == NULL) { + GB_PANIC("Unknown value: %.*s, entity: %p %.*s\n", LIT(e->token.string), e, LIT(entity_strings[e->kind])); + } + + return ssa_addr(v); +} + +ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) { + switch (expr->kind) { + case_ast_node(i, Ident, expr); + if (ssa_is_blank_ident(expr)) { + ssaAddr val = {0}; + return val; + } + Entity *e = entity_of_ident(p->module->info, expr); + return ssa_build_addr_from_entity(p, e, expr); + case_end; + + case_ast_node(pe, ParenExpr, expr); + return ssa_build_addr(p, unparen_expr(expr)); + case_end; + } + + GB_PANIC("Cannot get entity's address"); return ssa_addr(NULL); } + +Type *ssa_proper_type(Type *t) { + t = default_type(base_type(base_enum_type(t))); + + if (t->kind == Type_Basic) { + switch (t->Basic.kind) { + case Basic_int: + if (build_context.word_size == 8) { + return t_i64; + } + return t_i32; + case Basic_uint: + if (build_context.word_size == 8) { + return t_u64; + } + return t_u32; + } + } + + return t; +} + +ssaOp ssa_determine_op(TokenKind op, Type *t) { + t = ssa_proper_type(t); + if (t->kind == Type_Basic) { + switch (t->Basic.kind) { + case Basic_bool: + switch (op) { + case Token_And: return ssaOp_And8; + case Token_Or: return ssaOp_Or8; + case Token_Xor: return ssaOp_Xor8; + case Token_AndNot: return ssaOp_AndNot8; + } + break; + case Basic_i8: + switch (op) { + case Token_Add: return ssaOp_Add8; + case Token_Sub: return ssaOp_Sub8; + case Token_Mul: return ssaOp_Mul8; + case Token_Quo: return ssaOp_Div8; + case Token_Mod: return ssaOp_Mod8; + case Token_And: return ssaOp_And8; + case Token_Or: return ssaOp_Or8; + case Token_Xor: return ssaOp_Xor8; + case Token_AndNot: return ssaOp_AndNot8; + case Token_Lt: return ssaOp_Lt8; + case Token_LtEq: return ssaOp_Le8; + case Token_Gt: return ssaOp_Gt8; + case Token_GtEq: return ssaOp_Ge8; + case Token_CmpEq: return ssaOp_Eq8; + case Token_NotEq: return ssaOp_Ne8; + } + break; + case Basic_u8: + switch (op) { + case Token_Add: return ssaOp_Add8; + case Token_Sub: return ssaOp_Sub8; + case Token_Mul: return ssaOp_Mul8; + case Token_Quo: return ssaOp_Div8U; + case Token_Mod: return ssaOp_Mod8U; + case Token_And: return ssaOp_And8; + case Token_Or: return ssaOp_Or8; + case Token_Xor: return ssaOp_Xor8; + case Token_AndNot: return ssaOp_AndNot8; + case Token_Lt: return ssaOp_Lt8; + case Token_LtEq: return ssaOp_Le8; + case Token_Gt: return ssaOp_Gt8; + case Token_GtEq: return ssaOp_Ge8; + case Token_CmpEq: return ssaOp_Eq8; + case Token_NotEq: return ssaOp_Ne8; + } + break; + case Basic_i16: + switch (op) { + case Token_Add: return ssaOp_Add16; + case Token_Sub: return ssaOp_Sub16; + case Token_Mul: return ssaOp_Mul16; + case Token_Quo: return ssaOp_Div16; + case Token_Mod: return ssaOp_Mod16; + case Token_And: return ssaOp_And16; + case Token_Or: return ssaOp_Or16; + case Token_Xor: return ssaOp_Xor16; + case Token_AndNot: return ssaOp_AndNot16; + case Token_Lt: return ssaOp_Lt16; + case Token_LtEq: return ssaOp_Le16; + case Token_Gt: return ssaOp_Gt16; + case Token_GtEq: return ssaOp_Ge16; + case Token_CmpEq: return ssaOp_Eq16; + case Token_NotEq: return ssaOp_Ne16; + } + break; + case Basic_u16: + switch (op) { + case Token_Add: return ssaOp_Add16; + case Token_Sub: return ssaOp_Sub16; + case Token_Mul: return ssaOp_Mul16; + case Token_Quo: return ssaOp_Div16U; + case Token_Mod: return ssaOp_Mod16U; + case Token_And: return ssaOp_And16; + case Token_Or: return ssaOp_Or16; + case Token_Xor: return ssaOp_Xor16; + case Token_AndNot: return ssaOp_AndNot16; + case Token_Lt: return ssaOp_Lt16; + case Token_LtEq: return ssaOp_Le16; + case Token_Gt: return ssaOp_Gt16; + case Token_GtEq: return ssaOp_Ge16; + case Token_CmpEq: return ssaOp_Eq16; + case Token_NotEq: return ssaOp_Ne16; + } + break; + case Basic_i32: + switch (op) { + case Token_Add: return ssaOp_Add32; + case Token_Sub: return ssaOp_Sub32; + case Token_Mul: return ssaOp_Mul32; + case Token_Quo: return ssaOp_Div32; + case Token_Mod: return ssaOp_Mod32; + case Token_And: return ssaOp_And32; + case Token_Or: return ssaOp_Or32; + case Token_Xor: return ssaOp_Xor32; + case Token_AndNot: return ssaOp_AndNot32; + case Token_Lt: return ssaOp_Lt32; + case Token_LtEq: return ssaOp_Le32; + case Token_Gt: return ssaOp_Gt32; + case Token_GtEq: return ssaOp_Ge32; + case Token_CmpEq: return ssaOp_Eq32; + case Token_NotEq: return ssaOp_Ne32; + } + break; + case Basic_u32: + switch (op) { + case Token_Add: return ssaOp_Add32; + case Token_Sub: return ssaOp_Sub32; + case Token_Mul: return ssaOp_Mul32; + case Token_Quo: return ssaOp_Div32U; + case Token_Mod: return ssaOp_Mod32U; + case Token_And: return ssaOp_And32; + case Token_Or: return ssaOp_Or32; + case Token_Xor: return ssaOp_Xor32; + case Token_AndNot: return ssaOp_AndNot32; + case Token_Lt: return ssaOp_Lt32; + case Token_LtEq: return ssaOp_Le32; + case Token_Gt: return ssaOp_Gt32; + case Token_GtEq: return ssaOp_Ge32; + case Token_CmpEq: return ssaOp_Eq32; + case Token_NotEq: return ssaOp_Ne32; + } + break; + case Basic_i64: + switch (op) { + case Token_Add: return ssaOp_Add64; + case Token_Sub: return ssaOp_Sub64; + case Token_Mul: return ssaOp_Mul64; + case Token_Quo: return ssaOp_Div64; + case Token_Mod: return ssaOp_Mod64; + case Token_And: return ssaOp_And64; + case Token_Or: return ssaOp_Or64; + case Token_Xor: return ssaOp_Xor64; + case Token_AndNot: return ssaOp_AndNot64; + case Token_Lt: return ssaOp_Lt64; + case Token_LtEq: return ssaOp_Le64; + case Token_Gt: return ssaOp_Gt64; + case Token_GtEq: return ssaOp_Ge64; + case Token_CmpEq: return ssaOp_Eq64; + case Token_NotEq: return ssaOp_Ne64; + } + break; + case Basic_u64: + switch (op) { + case Token_Add: return ssaOp_Add64; + case Token_Sub: return ssaOp_Sub64; + case Token_Mul: return ssaOp_Mul64; + case Token_Quo: return ssaOp_Div64U; + case Token_Mod: return ssaOp_Mod64U; + case Token_And: return ssaOp_And64; + case Token_Or: return ssaOp_Or64; + case Token_Xor: return ssaOp_Xor64; + case Token_AndNot: return ssaOp_AndNot64; + case Token_Lt: return ssaOp_Lt64; + case Token_LtEq: return ssaOp_Le64; + case Token_Gt: return ssaOp_Gt64; + case Token_GtEq: return ssaOp_Ge64; + case Token_CmpEq: return ssaOp_Eq64; + case Token_NotEq: return ssaOp_Ne64; + } + break; + case Basic_f32: + switch (op) { + case Token_Add: return ssaOp_Add32F; + case Token_Sub: return ssaOp_Sub32F; + case Token_Mul: return ssaOp_Mul32F; + case Token_Quo: return ssaOp_Div32F; + case Token_Lt: return ssaOp_Lt32F; + case Token_LtEq: return ssaOp_Le32F; + case Token_Gt: return ssaOp_Gt32F; + case Token_GtEq: return ssaOp_Ge32F; + case Token_CmpEq: return ssaOp_Eq32F; + case Token_NotEq: return ssaOp_Ne32F; + } + break; + case Basic_f64: + switch (op) { + case Token_Add: return ssaOp_Add64F; + case Token_Sub: return ssaOp_Sub64F; + case Token_Mul: return ssaOp_Mul64F; + case Token_Quo: return ssaOp_Div64F; + case Token_Lt: return ssaOp_Lt64F; + case Token_LtEq: return ssaOp_Le64F; + case Token_Gt: return ssaOp_Gt64F; + case Token_GtEq: return ssaOp_Ge64F; + case Token_CmpEq: return ssaOp_Eq64F; + case Token_NotEq: return ssaOp_Ne64F; + } + break; + } + } + + GB_PANIC("Invalid Op for type"); + return ssaOp_Invalid; +} + ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { expr = unparen_expr(expr); @@ -620,15 +1041,53 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { GB_ASSERT_NOT_NULL(tv); if (tv->value.kind != ExactValue_Invalid) { - return NULL; - // return llir_add_module_constant(p->module, tv->type, tv->value); + Type *t = base_type(base_enum_type(tv->type)); + if (is_type_boolean(t)) { + return ssa_const_bool(p, tv->type, tv->value.value_bool); + } else if (is_type_string(t)) { + GB_ASSERT(tv->value.kind == ExactValue_String); + return ssa_const_string(p, tv->type, tv->value.value_string); + } else if(is_type_slice(t)) { + return ssa_const_slice(p, tv->type, tv->value); + } else if (is_type_integer(t)) { + GB_ASSERT(tv->value.kind == ExactValue_Integer); + + i64 s = 8*type_size_of(p->module->allocator, t); + switch (s) { + case 8: return ssa_const_i8 (p, tv->type, tv->value.value_integer); + case 16: return ssa_const_i16(p, tv->type, tv->value.value_integer); + case 32: return ssa_const_i32(p, tv->type, tv->value.value_integer); + case 64: return ssa_const_i64(p, tv->type, tv->value.value_integer); + default: GB_PANIC("Unknown integer size"); + } + } else if (is_type_float(t)) { + GB_ASSERT(tv->value.kind == ExactValue_Float); + i64 s = 8*type_size_of(p->module->allocator, t); + switch (s) { + case 32: return ssa_const_f32(p, tv->type, tv->value.value_float); + case 64: return ssa_const_f64(p, tv->type, tv->value.value_float); + default: GB_PANIC("Unknown float size"); + } + } + // IMPORTANT TODO(bill): Do constant record/array literals correctly + return ssa_const_nil(p, tv->type); + } + + if (tv->mode == Addressing_Variable) { + return ssa_addr_load(p, ssa_build_addr(p, expr)); } + switch (expr->kind) { case_ast_node(bl, BasicLit, expr); GB_PANIC("Non-constant basic literal"); case_end; + case_ast_node(bd, BasicDirective, expr); + TokenPos pos = bd->token.pos; + GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(bd->name)); + case_end; + case_ast_node(i, Ident, expr); Entity *e = *map_entity_get(&p->module->info->uses, hash_pointer(expr)); if (e->kind == Entity_Builtin) { @@ -648,7 +1107,103 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { if (v->op == ssaOp_Proc) { return v; } - return v; + + ssaAddr addr = ssa_build_addr(p, expr); + return ssa_addr_load(p, addr); + } + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: { + ssaValue *ptr = ssa_build_addr(p, ue->expr).addr; + return ssa_new_value1(p->curr_block, ssaOp_Copy, tv->type, ptr); + } break; + + case Token_Add: + return ssa_build_expr(p, ue->expr); + + case Token_Not: // Boolean not + return ssa_new_value1(p->curr_block, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr)); + case Token_Xor: { // Bitwise not + ssaValue *x = ssa_build_expr(p, ue->expr); + isize bits = 8*type_size_of(p->module->allocator, x->type); + switch (bits) { + case 8: return ssa_new_value1(p->curr_block, ssaOp_Not8, tv->type, x); + case 16: return ssa_new_value1(p->curr_block, ssaOp_Not16, tv->type, x); + case 32: return ssa_new_value1(p->curr_block, ssaOp_Not32, tv->type, x); + case 64: return ssa_new_value1(p->curr_block, ssaOp_Not64, tv->type, x); + } + GB_PANIC("unknown integer size"); + } break; + + case Token_Sub: { // 0-x + ssaValue *x = ssa_build_expr(p, ue->expr); + isize bits = 8*type_size_of(p->module->allocator, x->type); + if (is_type_integer(x->type)) { + switch (bits) { + case 8: return ssa_new_value1(p->curr_block, ssaOp_Neg8, tv->type, x); + case 16: return ssa_new_value1(p->curr_block, ssaOp_Neg16, tv->type, x); + case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32, tv->type, x); + case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64, tv->type, x); + } + } else if (is_type_float(x->type)) { + switch (bits) { + case 32: return ssa_new_value1(p->curr_block, ssaOp_Neg32F, tv->type, x); + case 64: return ssa_new_value1(p->curr_block, ssaOp_Neg64F, tv->type, x); + } + } + GB_PANIC("unknown type for -x"); + } break; + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + Type *type = default_type(tv->type); + + switch (be->op.kind) { + case Token_Add: + case Token_Sub: + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_Or: + case Token_Xor: + case Token_AndNot: { + ssaValue *x = ssa_build_expr(p, be->left); + ssaValue *y = ssa_build_expr(p, be->right); + GB_ASSERT(x != NULL && y != NULL); + return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y); + } + + case Token_Shl: + case Token_Shr: { + GB_PANIC("TODO: shifts"); + return NULL; + } + + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_LtEq: + case Token_Gt: + case Token_GtEq: { + ssaValue *x = ssa_build_expr(p, be->left); + ssaValue *y = ssa_build_expr(p, be->right); + GB_ASSERT(x != NULL && y != NULL); + return ssa_new_value2(p->curr_block, ssa_determine_op(be->op.kind, x->type), tv->type, x, y); + } break; + + case Token_CmpAnd: + case Token_CmpOr: + GB_PANIC("TODO: inline && and ||"); + return NULL; + // return ir_emit_logical_binary_expr(proc, expr); + + default: + GB_PANIC("Invalid binary expression"); + break; } case_end; } @@ -673,9 +1228,82 @@ ssaValue *ssa_emit_struct_ep(ssaProc *p, ssaValue *ptr, i32 index) { } +ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + return ssa_build_cond(p, pe->expr, yes, no); + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + return ssa_build_cond(p, ue->expr, no, yes); + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmd.and"); + ssa_build_cond(p, be->left, block, no); + ssa_start_block(p, block); + return ssa_build_cond(p, be->right, yes, no); + } else if (be->op.kind == Token_CmpOr) { + ssaBlock *block = ssa_new_block(p, ssaBlock_Plain, "cmp.or"); + ssa_build_cond(p, be->left, yes, block); + ssa_start_block(p, block); + return ssa_build_cond(p, be->right, yes, no); + } + case_end; + } + + ssaValue *c = ssa_build_expr(p, cond); + ssaBlock *b = ssa_end_block(p); + b->kind = ssaBlock_If; + ssa_set_control(b, c); + ssa_add_edge_to(b, yes); + ssa_add_edge_to(b, no); + return c; +} + +void ssa_build_when_stmt(ssaProc *p, AstNodeWhenStmt *ws) { + ssaValue *cond = ssa_build_expr(p, ws->cond); + GB_ASSERT(is_type_boolean(cond->type)); + + GB_ASSERT(cond->exact_value.kind == ExactValue_Bool); + if (cond->exact_value.value_bool) { + ssa_build_stmt_list(p, ws->body->BlockStmt.stmts); + } else if (ws->else_stmt) { + switch (ws->else_stmt->kind) { + case AstNode_BlockStmt: + ssa_build_stmt_list(p, ws->else_stmt->BlockStmt.stmts); + break; + case AstNode_WhenStmt: + ssa_build_when_stmt(p, &ws->else_stmt->WhenStmt); + break; + default: + GB_PANIC("Invalid `else` statement in `when` statement"); + break; + } + } +} + +void ssa_build_assign_op(ssaProc *p, ssaAddr lhs, ssaValue *value, TokenKind op) { + // ssaValue *old_value = ssa_addr_load(p, lhs); + // Type *type = old_value->type; + + // ssaValue *change = value; + // if (is_type_pointer(type) && is_type_integer(value->type)) { + // change = ssa_emit_conv(p, value, default_type(value->type)); + // } else { + // change = ssa_emit_conv(p, value, type); + // } + // ssaValue *new_value = ssa_emit_arith(p, op, old_value, change, type); + // ssa_addr_store(p, lhs, new_value); +} + + void ssa_build_stmt(ssaProc *p, AstNode *node) { if (p->curr_block == NULL) { - ssaBlock *dead_block = ssa_new_block(p, ssaBlock_Plain); + ssaBlock *dead_block = ssa_new_block(p, ssaBlock_Plain, ""); ssa_start_block(p, dead_block); } @@ -694,6 +1322,20 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) { } case_end; + case_ast_node(ws, WhenStmt, node); + ssa_build_when_stmt(p, ws); + case_end; + + case_ast_node(s, IncDecStmt, node); + TokenKind op = Token_Add; + if (s->op.kind == Token_Dec) { + op = Token_Sub; + } + ssaAddr addr = ssa_build_addr(p, s->expr); + Type *t = ssa_addr_type(addr); + ssa_build_assign_op(p, addr, ssa_const_int(p, t, 1), op); + case_end; + case_ast_node(vd, ValueDecl, node); if (vd->is_var) { ssaModule *m = p->module; @@ -829,9 +1471,336 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) { // NOTE(bill): No need to use return value ssa_build_expr(p, es->expr); case_end; + + case_ast_node(ds, DeferStmt, node); + GB_PANIC("TODO: DeferStmt"); + case_end; + + case_ast_node(rs, ReturnStmt, node); + GB_PANIC("TODO: ReturnStmt"); + case_end; + + case_ast_node(is, IfStmt, node); + ssa_emit_comment(p, str_lit("IfStmt")); + if (is->init != NULL) { + ssaBlock *init = ssa_new_block(p, ssaBlock_Plain, "if.init"); + ssa_emit_jump(p, init); + ssa_start_block(p, init); + ssa_build_stmt(p, is->init); + } + ssaBlock *then = ssa_new_block(p, ssaBlock_Plain, "if.then"); + ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "if.done"); + ssaBlock *else_ = done; + if (is->else_stmt != NULL) { + else_ = ssa_new_block(p, ssaBlock_Plain, "if.else"); + } + ssaBlock *b = NULL; + + ssa_build_cond(p, is->cond, then, else_); + ssa_start_block(p, then); + + // ssa_open_scope(p); + ssa_build_stmt(p, is->body); + // ssa_close_scope(p, ssaDeferExit_Default, NULL); + + ssa_emit_jump(p, done); + + if (is->else_stmt != NULL) { + ssa_start_block(p, else_); + + // ssa_open_scope(p); + ssa_build_stmt(p, is->else_stmt); + // ssa_close_scope(p, ssaDeferExit_Default, NULL); + + ssa_emit_jump(p, done); + } + + ssa_start_block(p, done); + case_end; + + + case_ast_node(fs, ForStmt, node); + ssa_emit_comment(p, str_lit("ForStmt")); + if (fs->init != NULL) { + ssaBlock *init = ssa_new_block(p, ssaBlock_Plain, "for.init"); + ssa_emit_jump(p, init); + ssa_start_block(p, init); + ssa_build_stmt(p, fs->init); + } + + ssaBlock *body = ssa_new_block(p, ssaBlock_Plain, "for.body"); + ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "for.done"); + ssaBlock *loop = body; + if (fs->cond != NULL) { + loop = ssa_new_block(p, ssaBlock_Plain, "for.loop"); + } + ssaBlock *post = loop; + if (fs->post != NULL) { + post = ssa_new_block(p, ssaBlock_Plain, "for.post"); + } + + ssa_emit_jump(p, loop); + ssa_start_block(p, loop); + + if (loop != body) { + ssa_build_cond(p, fs->cond, body, done); + ssa_start_block(p, body); + } + + ssa_push_target_list(p, done, post, NULL); + // ssa_open_scope(p); + ssa_build_stmt(p, fs->body); + // ssa_close_scope(p, ssaDeferExit_Default, NULL); + ssa_pop_target_list(p); + + ssa_emit_jump(p, post); + + if (fs->post != NULL) { + ssa_start_block(p, post); + ssa_build_stmt(p, fs->post); + ssa_emit_jump(p, post); + } + + ssa_start_block(p, done); + case_end; + + case_ast_node(rs, RangeStmt, node); + GB_PANIC("TODO: RangeStmt"); + case_end; + + case_ast_node(rs, MatchStmt, node); + GB_PANIC("TODO: MatchStmt"); + case_end; + + case_ast_node(rs, TypeMatchStmt, node); + GB_PANIC("TODO: TypeMatchStmt"); + case_end; + + case_ast_node(bs, BranchStmt, node); + ssaBlock *b = NULL; + switch (bs->token.kind) { + case Token_break: + for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) { + b = t->break_; + } + break; + case Token_continue: + for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) { + b = t->continue_; + } + break; + case Token_fallthrough: + for (ssaTargetList *t = p->target_list; t != NULL && b == NULL; t = t->prev) { + b = t->fallthrough_; + } + break; + } + if (b != NULL) { + // ssa_emit_defer_stmts(p, irDeferExit_Branch, b); + } + switch (bs->token.kind) { + case Token_break: ssa_emit_comment(p, str_lit("break")); break; + case Token_continue: ssa_emit_comment(p, str_lit("continue")); break; + case Token_fallthrough: ssa_emit_comment(p, str_lit("fallthrough")); break; + } + ssa_emit_jump(p, b); + case_end; + + case_ast_node(pa, PushAllocator, node); + GB_PANIC("TODO: PushAllocator"); + case_end; + case_ast_node(pc, PushContext, node); + GB_PANIC("TODO: PushContext"); + case_end; } } +void ssa_print_value(gbFile *f, ssaValue *v) { + if (v == NULL) { + gb_fprintf(f, "nil"); + } + gb_fprintf(f, "v%d", v->id); +} + +void ssa_print_exact_value(gbFile *f, ssaValue *v) { + Type *t = default_type(v->type); + ExactValue ev = v->exact_value; + switch (ev.kind) { + case ExactValue_Bool: + if (ev.value_bool == false) { + gb_fprintf(f, " [false]"); + } else { + gb_fprintf(f, " [true]"); + } + break; + case ExactValue_Integer: + if (is_type_unsigned(t)) { + gb_fprintf(f, " [%llu]", ev.value_integer); + } else { + gb_fprintf(f, " [%lld]", ev.value_integer); + } + break; + case ExactValue_Float: + if (is_type_f32(t)) { + f32 fp = cast(f32)ev.value_float; + u32 x = *cast(u32 *)&fp; + gb_fprintf(f, " [0x%x]", x); + } else if (is_type_f64(t)) { + f64 fp = cast(f64)ev.value_float; + u64 x = *cast(u64 *)&fp; + gb_fprintf(f, " [0x%llx]", x); + } else { + GB_PANIC("unhandled integer"); + } + break; + case ExactValue_String: + gb_fprintf(f, " [%.*s]", LIT(ev.value_string)); + break; + case ExactValue_Pointer: + gb_fprintf(f, " [0x%llx]", ev.value_pointer); + break; + } +} + + +void ssa_print_reg_value(gbFile *f, ssaValue *v) { + gb_fprintf(f, " "); + gb_fprintf(f, "v%d = %.*s", v->id, LIT(ssa_op_strings[v->op])); + + if (v->type != NULL) { + gbString type_str = type_to_string(default_type(v->type)); + gb_fprintf(f, " %s", type_str); + gb_string_free(type_str); + } + + ssa_print_exact_value(f, v); + + if (ssa_op_uses_var_args(v->op)) { + for_array(i, v->var_args) { + gb_fprintf(f, " "); + ssa_print_value(f, v->var_args.e[i]); + } + } else { + for (isize i = 0; i < v->arg_count; i++) { + gb_fprintf(f, " "); + ssa_print_value(f, v->args[i]); + } + } + + if (v->comment_string.len > 0) { + gb_fprintf(f, " ; %.*s", LIT(v->comment_string)); + } + + gb_fprintf(f, "\n"); + +} + +void ssa_print_proc(gbFile *f, ssaProc *p) { + gbString type_str = type_to_string(p->entity->type); + gb_fprintf(f, "%.*s %s\n", LIT(p->name), type_str); + gb_string_free(type_str); + + bool *printed = gb_alloc_array(heap_allocator(), bool, p->value_id+1); + + for_array(i, p->blocks) { + ssaBlock *b = p->blocks.e[i]; + gb_fprintf(f, " b%d:", b->id); + if (b->preds.count > 0) { + gb_fprintf(f, " <-"); + for_array(j, b->preds) { + ssaBlock *pred = b->preds.e[j].block; + gb_fprintf(f, " b%d", pred->id); + } + } + gb_fprintf(f, "\n"); + + isize n = 0; + for_array(j, b->values) { + ssaValue *v = b->values.e[j]; + if (v->op != ssaOp_Phi) { + continue; + } + ssa_print_reg_value(f, v); + printed[v->id] = true; + n++; + } + + while (n < b->values.count) { + isize m = 0; + for_array(j, b->values) { + ssaValue *v = b->values.e[j]; + if (printed[v->id]) { + continue; + } + bool skip = false; + if (ssa_op_uses_var_args(v->op)) { + for_array(k, v->var_args) { + ssaValue *w = v->var_args.e[k]; + if (w != NULL && w->block == b && !printed[w->id]) { + skip = true; + break; + } + } + } else { + for (isize k = 0; k < v->arg_count; k++) { + ssaValue *w = v->args[k]; + if (w != NULL && w->block == b && !printed[w->id]) { + skip = true; + break; + } + } + } + + if (skip) { + break; + } + + ssa_print_reg_value(f, v); + printed[v->id] = true; + n++; + } + if (m == n) { + gb_fprintf(f, "!!!!DepCycle!!!!\n"); + for_array(k, b->values) { + ssaValue *v = b->values.e[k]; + if (printed[v->id]) { + continue; + } + + ssa_print_reg_value(f, v); + printed[v->id] = true; + n++; + } + } + } + + if (b->kind == ssaBlock_Plain) { + GB_ASSERT(b->succs.count == 1); + ssaBlock *next = b->succs.e[0].block; + gb_fprintf(f, " "); + gb_fprintf(f, "jump b%d", next->id); + gb_fprintf(f, "\n"); + } else if (b->kind == ssaBlock_If) { + GB_ASSERT(b->succs.count == 2); + ssaBlock *yes = b->succs.e[0].block; + ssaBlock *no = b->succs.e[1].block; + gb_fprintf(f, " "); + gb_fprintf(f, "branch v%d, b%d, b%d", b->control->id, yes->id, no->id); + gb_fprintf(f, "\n"); + } else if (b->kind == ssaBlock_Exit) { + gb_fprintf(f, " "); + gb_fprintf(f, "exit"); + gb_fprintf(f, "\n"); + } else if (b->kind == ssaBlock_Ret) { + gb_fprintf(f, " "); + gb_fprintf(f, "ret"); + gb_fprintf(f, "\n"); + } + } + + gb_free(heap_allocator(), printed); +} + void ssa_build_proc(ssaModule *m, ssaProc *p) { p->module = m; @@ -846,13 +1815,19 @@ void ssa_build_proc(ssaModule *m, ssaProc *p) { if (pl->body == NULL) { return; } - p->entry = ssa_new_block(p, ssaBlock_Entry); - p->curr_block = ssa_new_block(p, ssaBlock_Plain); + p->entry = ssa_new_block(p, ssaBlock_Entry, "entry"); + ssa_start_block(p, p->entry); ssa_build_stmt(p, pl->body); + + p->exit = ssa_new_block(p, ssaBlock_Exit, "exit"); + ssa_emit_jump(p, p->exit); + + ssa_print_proc(gb_file_get_standard(gbFileStandard_Error), p); } + bool ssa_generate(Parser *parser, CheckerInfo *info) { if (global_error_collector.count != 0) { return false; @@ -956,8 +1931,6 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) { } if (e == entry_point) { - gb_printf("%.*s\n", LIT(name)); - ssaProc *p = ssa_new_proc(&m, name, e, decl); ssa_build_proc(&m, p); } |