From 7792f009b8d8fbc4949b8110ebbcffc943878cae Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Wed, 23 Nov 2016 14:41:20 +0000 Subject: [PATCH] Numpty forgot to add .c files --- README.md | 2 + logo-slim.png | Bin 0 -> 251710 bytes src/array.c | 235 ++ src/checker/checker.c | 1353 ++++++++++ src/checker/decl.c | 545 +++++ src/checker/entity.c | 179 ++ src/checker/expr.c | 4465 +++++++++++++++++++++++++++++++++ src/checker/stmt.c | 1130 +++++++++ src/checker/types.c | 1487 +++++++++++ src/common.c | 195 ++ src/exact_value.c | 400 +++ src/main.c | 272 +++ src/old_vm.c | 1305 ++++++++++ src/parser.c | 3250 ++++++++++++++++++++++++ src/printer.c | 221 ++ src/ssa.c | 5419 +++++++++++++++++++++++++++++++++++++++++ src/ssa_opt.c | 493 ++++ src/ssa_print.c | 1439 +++++++++++ src/string.c | 422 ++++ src/timings.c | 105 + src/tokenizer.c | 816 +++++++ src/unicode.c | 66 + 22 files changed, 23799 insertions(+) create mode 100644 logo-slim.png create mode 100644 src/array.c create mode 100644 src/checker/checker.c create mode 100644 src/checker/decl.c create mode 100644 src/checker/entity.c create mode 100644 src/checker/expr.c create mode 100644 src/checker/stmt.c create mode 100644 src/checker/types.c create mode 100644 src/common.c create mode 100644 src/exact_value.c create mode 100644 src/main.c create mode 100644 src/old_vm.c create mode 100644 src/parser.c create mode 100644 src/printer.c create mode 100644 src/ssa.c create mode 100644 src/ssa_opt.c create mode 100644 src/ssa_print.c create mode 100644 src/string.c create mode 100644 src/timings.c create mode 100644 src/tokenizer.c create mode 100644 src/unicode.c diff --git a/README.md b/README.md index 0a5496ba6..5b962f919 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Odin logo + # The Odin Programming Language Odin is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals: diff --git a/logo-slim.png b/logo-slim.png new file mode 100644 index 0000000000000000000000000000000000000000..2b70e6a0c8a01ef6fffff2a59d1d7409551a529a GIT binary patch literal 251710 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�$DH%_J&N$|w-KjMElr`f;6$Z4kLU{BV+GUc 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

axzTfr;Q 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

yKYT}C6?vL zX6wOb)_Xt1O2~=BCMKt%0B~c{6y`TK4|Lxab{MPWz-}i%m?#l;p9+nWt<(x?n zjj%w{2p458v%S(NrLdSZ3JQyKp%4SNB6VQQmP>_#4|ouwZQk)9axIl~0Q`k@ey-$z zM+||F=VEvk3nPiw9sfLnF*lzDY>OrOB(OjQ>^uvUErd`A;XZ# zYVcX7xDXm>(YomCJsbD)&)P({xQBO^qQi1FUJ{qetQ^i|5fKDr{%ix##X?G<=$eMP z^~Phl&DYzf6*H3*oh`c9li?^gYpg0P(L3eKU?tj#bK7|bf9F0Fj?i&mTd?}(E zX`84*CXq8+E!HP!u4o25NUd`x*Btk@PN&yqf{H=1=$0kzFW+(hr|vuL-h)DF^#(9P zcv~|o6DL=tw;T5FfrI{r8*VtvL4U^sx8MJis3r8RK5{RBD0m&!x|<(5y7Rn z8B$DxHQZ8**r()@FLqdj!qD@jkq$M4sD}e0thEAWMybA2CYkZaO4hAS6qd|SIQiUQ z_99`Cw5fR?=LJ{<@}8LX^{AOm({#vLIp=0%qGohsoNm@}GKM=PoRi2hGB|OHn;*(S zZv12Os}$ylHU*LQmL#qGAvLT+cghi@h3gUsW;o$QgmN76rT#kD8vdQX{)TUV%|a8% zkj8FoF?)wt<{~v&Fl*%hjD@9U9?uLs8jap{#}jXS`}MzOY291Z5L7nZsVxMx_QPoHixU#G9yRW+5(N(z8{BRad%Hm8`+42Q`+i%{*T++KW&v`TtOKn=b?xsw3W-UNVC3KkJ5 zs5}IV15{nGs7Fu+)iMk0o+2!6hL$Tgl-|P)ZWdh3*e6LHNJ>^1THHD_=Ww!PWWxQ|pb|1;ZmE;yO4ImjJyL_CBT*LQ$lKhXGV+)>uliDF4{}LNAiR zIHH2#mS`HxVKUpSuEX;BDK)brdjHS(kIB~XFa4SKyzy_m@`fvB;&#I^f_Q44b8drm zp5Jy-Jpc%&M$SMXhH~ouXQwy4>tiokQ2KSR{PLfE^Y{JGU-+tJQH)3PsheG5SOPmt zx}NdN!tCC{oO&YK;X<)Crk4g*4LBCcz#GldHXWfO^-050?T?XIs*bT8BzUC;&@E&} zH92%H4*6KmP;uT^9x2Q?Czd%2pMOH<+&y9G!F645Tmex+BtYxhdOysx zE&`~XTu%cl8jLob(C)Jg0~U~&8ih690M?K;9>g%1=U?cpwL@M-A-D#EU^&vw_RY4t z1wZ%r**y|YB$XB^1CrGO`$BgjDjaaozx?GdzyA8`523WbMV3NYL;bJ7-R#EsL{N6snXoOie%tby;%=m9IaEPeQ z(`D_BJSN642fwUyQSXzKirG$>dmACxA!ekcUUF(3?%oaP3AZVu2AFa1fypjqm}P+h zRW|xR)7ycYggsHxoq^viQFZVh${N@QyRQw&C-~i=aB6RgtBUyz^6!zxf^S|594RxBT$y zZasA@!dXg$AzIv{&xw+X4P5%w`y?!3&5`HaCur!j3@prWh_aC3Md-7qd5g!xEyu=z z^>Yt1WYeVYmYHDcjX8f1qVCLC72*IWc%CN~d~%roZw9Tn^qC~uLO9P`DCaI6SoG(8dFWZA7%2}o{1{S)%m+bk1BS?bvNm~dS8gX3{q!Se?tJ77TzZm}?TJ;1 zMe2Z+1Ni0%-|=?kne7_QKY0AHGZzSHO8Ia%T`X8CjO~HKpI8

l2kiJ_qFh$W&)M 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

kD%9TWjf%8bQOVB6>ZFU^Tr`tH@!A7zU{(~* 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 0); + +#define Array(Type_) struct { \ + gbAllocator allocator; \ + Type_ * e; \ + isize count; \ + isize capacity; \ +} + +typedef Array(void) ArrayVoid; + +#define array_init_reserve(x_, allocator_, init_capacity_) do { \ + GB_ASSERT((x_) != NULL); \ + void **e = cast(void **)&((x_)->e); \ + (x_)->allocator = (allocator_); \ + (x_)->count = 0; \ + (x_)->capacity = (init_capacity_); \ + *e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \ +} while (0) + +#define array_init_count(x_, allocator_, init_count_) do { \ + GB_ASSERT((x_) != NULL); \ + void **e = cast(void **)&((x_)->e); \ + (x_)->allocator = (allocator_); \ + (x_)->count = (init_count_); \ + (x_)->capacity = (init_count_); \ + *e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \ +} while (0) + +#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0) +#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0) +#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0) + +#define array_grow(x_, min_capacity_) do { \ + isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \ + if (new_capacity < (min_capacity_)) { \ + new_capacity = (min_capacity_); \ + } \ + array_set_capacity(x_, new_capacity); \ +} while (0) + +#define array_add(x_, item_) do { \ + if ((x_)->capacity < (x_)->count+1) { \ + array_grow(x_, 0); \ + } \ + (x_)->e[(x_)->count++] = item_; \ +} while (0) + +#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0) +#define array_clear(x_) do { (x_)->count = 0; } while (0) + +#define array_resize(x_, new_count_) do { \ + if ((x_)->capacity < (new_count_)) { \ + array_grow((x_), (new_count_)); \ + } \ + (x_)->count = (new_count_); \ +} while (0) + +#define array_reserve(x_, new_capacity_) do { \ + if ((x_)->capacity < (new_capacity_)) { \ + array_set_capacity((x_), (new_capacity_)); \ + } \ +} while (0) + + + + +void array__set_capacity(void *ptr, isize capacity, isize element_size) { + GB_ASSERT(ptr != NULL); + ArrayVoid *x = cast(ArrayVoid *)ptr; + + GB_ASSERT(element_size > 0); + + if (capacity == x->capacity) { + return; + } + + if (capacity < x->count) { + if (x->capacity < capacity) { + isize new_capacity = ARRAY_GROW_FORMULA(x->capacity); + if (new_capacity < capacity) { + new_capacity = capacity; + } + array__set_capacity(ptr, new_capacity, element_size); + } + x->count = capacity; + } + + { + // TODO(bill): Resize rather than copy and delete + void *new_data = gb_alloc(x->allocator, element_size*capacity); + gb_memmove(new_data, x->e, element_size*x->count); + gb_free(x->allocator, x->e); + x->capacity = capacity; + x->e = new_data; + } +} + + +#if 0 +template +struct Array { + gbAllocator allocator; + T * data; + isize count; + isize capacity; + + T &operator[](isize index) { + GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds"); + return data[index]; + } + + T const &operator[](isize index) const { + GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds"); + return data[index]; + } +}; + +template void array_init (Array *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0)); +template void array_init_count (Array *array, gbAllocator a, isize count); +template Array array_make (T *data, isize count, isize capacity); +template void array_free (Array *array); +template void array_add (Array *array, T const &t); +template T array_pop (Array *array); +template void array_clear (Array *array); +template void array_reserve (Array *array, isize capacity); +template void array_resize (Array *array, isize count); +template void array_set_capacity(Array *array, isize capacity); + + +template +void array_init(Array *array, gbAllocator a, isize init_capacity) { + array->allocator = a; + array->data = gb_alloc_array(a, T, init_capacity); + array->count = 0; + array->capacity = init_capacity; +} + +template +void array_init_count(Array *array, gbAllocator a, isize count) { + array->allocator = a; + array->data = gb_alloc_array(a, T, count); + array->count = count; + array->capacity = count; +} + + +template +Array array_make(T *data, isize count, isize capacity) { + Array a = {0}; + a.data = data; + a.count = count; + a.capacity = capacity; + return a; +} + + +template +void array_free(Array *array) { + if (array->allocator.proc != NULL) { + gb_free(array->allocator, array->data); + } + array->count = 0; + array->capacity = 0; +} + +template +void array__grow(Array *array, isize min_capacity) { + isize new_capacity = ARRAY_GROW_FORMULA(array->capacity); + if (new_capacity < min_capacity) { + new_capacity = min_capacity; + } + array_set_capacity(array, new_capacity); +} + +template +void array_add(Array *array, T const &t) { + if (array->capacity < array->count+1) { + array__grow(array, 0); + } + array->data[array->count] = t; + array->count++; +} + +template +T array_pop(Array *array) { + GB_ASSERT(array->count > 0); + array->count--; + return array->data[array->count]; +} + +template +void array_clear(Array *array) { + array->count = 0; +} + +template +void array_reserve(Array *array, isize capacity) { + if (array->capacity < capacity) { + array_set_capacity(array, capacity); + } +} + +template +void array_resize(Array *array, isize count) { + if (array->capacity < count) { + array__grow(array, count); + } + array->count = count; +} + +template +void array_set_capacity(Array *array, isize capacity) { + if (capacity == array->capacity) { + return; + } + + if (capacity < array->count) { + array_resize(array, capacity); + } + + T *new_data = NULL; + if (capacity > 0) { + new_data = gb_alloc_array(array->allocator, T, capacity); + gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity); + } + gb_free(array->allocator, array->data); + array->data = new_data; + array->capacity = capacity; +} + + + +#endif diff --git a/src/checker/checker.c b/src/checker/checker.c new file mode 100644 index 000000000..889efa1d3 --- /dev/null +++ b/src/checker/checker.c @@ -0,0 +1,1353 @@ +#include "../exact_value.c" +#include "entity.c" +#include "types.c" + +#define MAP_TYPE Entity * +#define MAP_PROC map_entity_ +#define MAP_NAME MapEntity +#include "../map.c" + +typedef enum AddressingMode { + Addressing_Invalid, + Addressing_NoValue, + Addressing_Value, + Addressing_Variable, + Addressing_Constant, + Addressing_Type, + Addressing_Builtin, + Addressing_Count, +} AddressingMode; + +typedef struct Operand { + AddressingMode mode; + Type * type; + ExactValue value; + AstNode * expr; + BuiltinProcId builtin_id; +} Operand; + +typedef struct TypeAndValue { + AddressingMode mode; + Type * type; + ExactValue value; +} TypeAndValue; + + + +typedef struct DeclInfo { + Scope *scope; + + Entity **entities; + isize entity_count; + + AstNode *type_expr; + AstNode *init_expr; + AstNode *proc_decl; // AstNode_ProcDecl + u32 var_decl_tags; + + MapBool deps; // Key: Entity * +} DeclInfo; + +typedef struct ExprInfo { + bool is_lhs; // Debug info + AddressingMode mode; + Type * type; // Type_Basic + ExactValue value; +} ExprInfo; + +ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) { + ExprInfo ei = {is_lhs, mode, type, value}; + return ei; +} + +typedef struct ProcedureInfo { + AstFile * file; + Token token; + DeclInfo *decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStatement + u32 tags; +} ProcedureInfo; + +typedef struct Scope { + Scope * parent; + Scope * prev, *next; + Scope * first_child; + Scope * last_child; + MapEntity elements; // Key: String + MapEntity implicit; // Key: String + + Array(Scope *) shared; + Array(Scope *) imported; + bool is_proc; + bool is_global; + bool is_file; + bool is_init; + AstFile * file; +} Scope; +gb_global Scope *universal_scope = NULL; + +typedef enum ExprKind { + Expr_Expr, + Expr_Stmt, +} ExprKind; + +typedef enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_new, + BuiltinProc_new_slice, + + BuiltinProc_size_of, + BuiltinProc_size_of_val, + BuiltinProc_align_of, + BuiltinProc_align_of_val, + BuiltinProc_offset_of, + BuiltinProc_offset_of_val, + BuiltinProc_type_of_val, + + BuiltinProc_type_info, + BuiltinProc_type_info_of_val, + + BuiltinProc_compile_assert, + BuiltinProc_assert, + BuiltinProc_panic, + + BuiltinProc_copy, + BuiltinProc_append, + + BuiltinProc_swizzle, + + // BuiltinProc_ptr_offset, + // BuiltinProc_ptr_sub, + BuiltinProc_slice_ptr, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + + BuiltinProc_enum_to_string, + + BuiltinProc_Count, +} BuiltinProcId; +typedef struct BuiltinProc { + String name; + isize arg_count; + bool variadic; + ExprKind kind; +} BuiltinProc; +gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { + {STR_LIT(""), 0, false, Expr_Stmt}, + + {STR_LIT("new"), 1, false, Expr_Expr}, + {STR_LIT("new_slice"), 2, true, Expr_Expr}, + + {STR_LIT("size_of"), 1, false, Expr_Expr}, + {STR_LIT("size_of_val"), 1, false, Expr_Expr}, + {STR_LIT("align_of"), 1, false, Expr_Expr}, + {STR_LIT("align_of_val"), 1, false, Expr_Expr}, + {STR_LIT("offset_of"), 2, false, Expr_Expr}, + {STR_LIT("offset_of_val"), 1, false, Expr_Expr}, + {STR_LIT("type_of_val"), 1, false, Expr_Expr}, + + {STR_LIT("type_info"), 1, false, Expr_Expr}, + {STR_LIT("type_info_of_val"), 1, false, Expr_Expr}, + + {STR_LIT("compile_assert"), 1, false, Expr_Stmt}, + {STR_LIT("assert"), 1, false, Expr_Stmt}, + {STR_LIT("panic"), 1, false, Expr_Stmt}, + + {STR_LIT("copy"), 2, false, Expr_Expr}, + {STR_LIT("append"), 2, false, Expr_Expr}, + + {STR_LIT("swizzle"), 1, true, Expr_Expr}, + + // {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, + // {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, + {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, + + {STR_LIT("min"), 2, false, Expr_Expr}, + {STR_LIT("max"), 2, false, Expr_Expr}, + {STR_LIT("abs"), 1, false, Expr_Expr}, + + {STR_LIT("enum_to_string"), 1, false, Expr_Expr}, +}; + +typedef enum ImplicitValueId { + ImplicitValue_Invalid, + + ImplicitValue_context, + + ImplicitValue_Count, +} ImplicitValueId; +typedef struct ImplicitValueInfo { + String name; + String backing_name; + Type * type; +} ImplicitValueInfo; +// NOTE(bill): This is initialized later +gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {0}; + + + +typedef struct CheckerContext { + Scope * scope; + DeclInfo *decl; + u32 stmt_state_flags; +} CheckerContext; + +#define MAP_TYPE TypeAndValue +#define MAP_PROC map_tav_ +#define MAP_NAME MapTypeAndValue +#include "../map.c" + +#define MAP_TYPE Scope * +#define MAP_PROC map_scope_ +#define MAP_NAME MapScope +#include "../map.c" + +#define MAP_TYPE DeclInfo * +#define MAP_PROC map_decl_info_ +#define MAP_NAME MapDeclInfo +#include "../map.c" + +#define MAP_TYPE AstFile * +#define MAP_PROC map_ast_file_ +#define MAP_NAME MapAstFile +#include "../map.c" + +#define MAP_TYPE ExprInfo +#define MAP_PROC map_expr_info_ +#define MAP_NAME MapExprInfo +#include "../map.c" + + +// NOTE(bill): Symbol tables +typedef struct CheckerInfo { + MapTypeAndValue types; // Key: AstNode * | Expression -> Type (and value) + MapEntity definitions; // Key: AstNode * | Identifier -> Entity + MapEntity uses; // Key: AstNode * | Identifier -> Entity + MapScope scopes; // Key: AstNode * | Node -> Scope + MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo + MapDeclInfo entities; // Key: Entity * + MapEntity foreign_procs; // Key: String + MapAstFile files; // Key: String (full path) + MapIsize type_info_map; // Key: Type * + isize type_info_count; + Entity * implicit_values[ImplicitValue_Count]; +} CheckerInfo; + +typedef struct Checker { + Parser * parser; + CheckerInfo info; + + AstFile * curr_ast_file; + BaseTypeSizes sizes; + Scope * global_scope; + Array(ProcedureInfo) procs; // NOTE(bill): Procedures to check + + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; + + CheckerContext context; + + Array(Type *) proc_stack; + bool in_defer; // TODO(bill): Actually handle correctly +} Checker; + +typedef struct CycleChecker { + Array(Entity *) path; // Entity_TypeName +} CycleChecker; + + + + +CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) { + if (cc == NULL) { + return NULL; + } + if (cc->path.e == NULL) { + array_init(&cc->path, heap_allocator()); + } + GB_ASSERT(e != NULL && e->kind == Entity_TypeName); + array_add(&cc->path, e); + return cc; +} + +void cycle_checker_destroy(CycleChecker *cc) { + if (cc != NULL && cc->path.e != NULL) { + array_free(&cc->path); + } +} + + +void init_declaration_info(DeclInfo *d, Scope *scope) { + d->scope = scope; + map_bool_init(&d->deps, heap_allocator()); +} + +DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) { + DeclInfo *d = gb_alloc_item(a, DeclInfo); + init_declaration_info(d, scope); + return d; +} + +void destroy_declaration_info(DeclInfo *d) { + map_bool_destroy(&d->deps); +} + +bool decl_info_has_init(DeclInfo *d) { + if (d->init_expr != NULL) { + return true; + } + if (d->proc_decl != NULL) { + ast_node(pd, ProcDecl, d->proc_decl); + if (pd->body != NULL) { + return true; + } + } + + return false; +} + + + + + +Scope *make_scope(Scope *parent, gbAllocator allocator) { + Scope *s = gb_alloc_item(allocator, Scope); + s->parent = parent; + map_entity_init(&s->elements, heap_allocator()); + map_entity_init(&s->implicit, heap_allocator()); + array_init(&s->shared, heap_allocator()); + array_init(&s->imported, heap_allocator()); + + if (parent != NULL && parent != universal_scope) { + DLIST_APPEND(parent->first_child, parent->last_child, s); + } + return s; +} + +void destroy_scope(Scope *scope) { + for_array(i, scope->elements.entries) { + Entity *e =scope->elements.entries.e[i].value; + if (e->kind == Entity_Variable) { + if (!(e->flags & EntityFlag_Used)) { +#if 0 + warning(e->token, "Unused variable `%.*s`", LIT(e->token.string)); +#endif + } + } + } + + for (Scope *child = scope->first_child; child != NULL; child = child->next) { + destroy_scope(child); + } + + map_entity_destroy(&scope->elements); + map_entity_destroy(&scope->implicit); + array_free(&scope->shared); + array_free(&scope->imported); + + // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) +} + +void add_scope(Checker *c, AstNode *node, Scope *scope) { + GB_ASSERT(node != NULL); + GB_ASSERT(scope != NULL); + map_scope_set(&c->info.scopes, hash_pointer(node), scope); +} + + +void check_open_scope(Checker *c, AstNode *node) { + GB_ASSERT(node != NULL); + GB_ASSERT(node->kind == AstNode_Invalid || + is_ast_node_stmt(node) || + is_ast_node_type(node)); + Scope *scope = make_scope(c->context.scope, c->allocator); + add_scope(c, node, scope); + if (node->kind == AstNode_ProcType) { + scope->is_proc = true; + } + c->context.scope = scope; + c->context.stmt_state_flags |= StmtStateFlag_bounds_check; +} + +void check_close_scope(Checker *c) { + c->context.scope = c->context.scope->parent; +} + +void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) { + bool gone_thru_proc = false; + HashKey key = hash_string(name); + for (Scope *s = scope; s != NULL; s = s->parent) { + Entity **found = map_entity_get(&s->elements, key); + if (found) { + Entity *e = *found; + if (gone_thru_proc) { + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { + continue; + } + } + + if (entity_) *entity_ = e; + if (scope_) *scope_ = s; + return; + } + + if (s->is_proc) { + gone_thru_proc = true; + } else { + // Check shared scopes - i.e. other files @ global scope + for_array(i, s->shared) { + Scope *shared = s->shared.e[i]; + Entity **found = map_entity_get(&shared->elements, key); + if (found) { + Entity *e = *found; + if (e->kind == Entity_Variable && + !e->scope->is_file && + !e->scope->is_global) { + continue; + } + + if (e->scope != shared) { + // Do not return imported entities even #load ones + continue; + } + + if (entity_) *entity_ = e; + if (scope_) *scope_ = shared; + return; + } + } + } + } + + + if (entity_) *entity_ = NULL; + if (scope_) *scope_ = NULL; +} + +Entity *scope_lookup_entity(Scope *s, String name) { + Entity *entity = NULL; + scope_lookup_parent_entity(s, name, NULL, &entity); + return entity; +} + +Entity *current_scope_lookup_entity(Scope *s, String name) { + HashKey key = hash_string(name); + Entity **found = map_entity_get(&s->elements, key); + if (found) { + return *found; + } + for_array(i, s->shared) { + Entity **found = map_entity_get(&s->shared.e[i]->elements, key); + if (found) { + return *found; + } + } + return NULL; +} + + + +Entity *scope_insert_entity(Scope *s, Entity *entity) { + String name = entity->token.string; + HashKey key = hash_string(name); + Entity **found = map_entity_get(&s->elements, key); + if (found) { + return *found; + } + map_entity_set(&s->elements, key, entity); + if (entity->scope == NULL) { + entity->scope = s; + } + return NULL; +} + +void check_scope_usage(Checker *c, Scope *scope) { + // TODO(bill): Use this? +} + + +void add_dependency(DeclInfo *d, Entity *e) { + map_bool_set(&d->deps, hash_pointer(e), cast(bool)true); +} + +void add_declaration_dependency(Checker *c, Entity *e) { + if (e == NULL) { + return; + } + if (c->context.decl != NULL) { + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + if (found) { + add_dependency(c->context.decl, e); + } + } +} + + +void add_global_entity(Entity *entity) { + String name = entity->token.string; + if (gb_memchr(name.text, ' ', name.len)) { + return; // NOTE(bill): `untyped thing` + } + if (scope_insert_entity(universal_scope, entity)) { + compiler_error("double declaration"); + } +} + +void add_global_constant(gbAllocator a, String name, Type *type, ExactValue value) { + Entity *entity = alloc_entity(a, Entity_Constant, NULL, make_token_ident(name), type); + entity->Constant.value = value; + add_global_entity(entity); +} + + + +void init_universal_scope(void) { + // NOTE(bill): No need to free these + gbAllocator a = heap_allocator(); + universal_scope = make_scope(NULL, a); + +// Types + for (isize i = 0; i < gb_count_of(basic_types); i++) { + add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_types[i].Basic.name), &basic_types[i])); + } + for (isize i = 0; i < gb_count_of(basic_type_aliases); i++) { + add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_type_aliases[i].Basic.name), &basic_type_aliases[i])); + } + +// Constants + add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true)); + add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false)); + + add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil)); + +// Builtin Procedures + for (isize i = 0; i < gb_count_of(builtin_procs); i++) { + BuiltinProcId id = cast(BuiltinProcId)i; + Entity *entity = alloc_entity(a, Entity_Builtin, NULL, make_token_ident(builtin_procs[i].name), t_invalid); + entity->Builtin.id = id; + add_global_entity(entity); + } + + t_u8_ptr = make_type_pointer(a, t_u8); + t_int_ptr = make_type_pointer(a, t_int); +} + + + + +void init_checker_info(CheckerInfo *i) { + gbAllocator a = heap_allocator(); + map_tav_init(&i->types, a); + map_entity_init(&i->definitions, a); + map_entity_init(&i->uses, a); + map_scope_init(&i->scopes, a); + map_decl_info_init(&i->entities, a); + map_expr_info_init(&i->untyped, a); + map_entity_init(&i->foreign_procs, a); + map_isize_init(&i->type_info_map, a); + map_ast_file_init(&i->files, a); + i->type_info_count = 0; + +} + +void destroy_checker_info(CheckerInfo *i) { + map_tav_destroy(&i->types); + map_entity_destroy(&i->definitions); + map_entity_destroy(&i->uses); + map_scope_destroy(&i->scopes); + map_decl_info_destroy(&i->entities); + map_expr_info_destroy(&i->untyped); + map_entity_destroy(&i->foreign_procs); + map_isize_destroy(&i->type_info_map); + map_ast_file_destroy(&i->files); +} + + +void init_checker(Checker *c, Parser *parser, BaseTypeSizes sizes) { + gbAllocator a = heap_allocator(); + + c->parser = parser; + init_checker_info(&c->info); + c->sizes = sizes; + + array_init(&c->proc_stack, a); + array_init(&c->procs, a); + + // NOTE(bill): Is this big enough or too small? + isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope)); + isize total_token_count = 0; + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + total_token_count += f->tokens.count; + } + isize arena_size = 2 * item_size * total_token_count; + gb_arena_init_from_allocator(&c->arena, a, arena_size); + gb_arena_init_from_allocator(&c->tmp_arena, a, arena_size); + + + c->allocator = gb_arena_allocator(&c->arena); + c->tmp_allocator = gb_arena_allocator(&c->tmp_arena); + + c->global_scope = make_scope(universal_scope, c->allocator); + c->context.scope = c->global_scope; +} + +void destroy_checker(Checker *c) { + destroy_checker_info(&c->info); + destroy_scope(c->global_scope); + array_free(&c->proc_stack); + array_free(&c->procs); + + gb_arena_free(&c->arena); +} + + +TypeAndValue *type_and_value_of_expression(CheckerInfo *i, AstNode *expression) { + TypeAndValue *found = map_tav_get(&i->types, hash_pointer(expression)); + return found; +} + + +Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) { + if (identifier->kind == AstNode_Ident) { + Entity **found = map_entity_get(&i->definitions, hash_pointer(identifier)); + if (found) { + return *found; + } + found = map_entity_get(&i->uses, hash_pointer(identifier)); + if (found) { + return *found; + } + } + return NULL; +} + +Type *type_of_expr(CheckerInfo *i, AstNode *expression) { + TypeAndValue *found = type_and_value_of_expression(i, expression); + if (found) { + return found->type; + } + if (expression->kind == AstNode_Ident) { + Entity *entity = entity_of_ident(i, expression); + if (entity) { + return entity->type; + } + } + + return NULL; +} + + +void add_untyped(CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value) { + map_expr_info_set(&i->untyped, hash_pointer(expression), make_expr_info(lhs, mode, basic_type, value)); +} + +void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) { + GB_ASSERT(expression != NULL); + if (mode == Addressing_Invalid) { + return; + } + + if (mode == Addressing_Constant) { + if (is_type_constant_type(type)) { + GB_ASSERT(value.kind != ExactValue_Invalid); + if (!(type != t_invalid || is_type_constant_type(type))) { + compiler_error("add_type_and_value - invalid type: %s", type_to_string(type)); + } + } + } + + TypeAndValue tv = {0}; + tv.type = type; + tv.value = value; + tv.mode = mode; + map_tav_set(&i->types, hash_pointer(expression), tv); +} + +void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) { + GB_ASSERT(identifier != NULL); + if (identifier->kind == AstNode_Ident) { + GB_ASSERT(identifier->kind == AstNode_Ident); + HashKey key = hash_pointer(identifier); + map_entity_set(&i->definitions, key, entity); + } else { + // NOTE(bill): Error should handled elsewhere + } +} + +bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { + if (str_ne(entity->token.string, str_lit("_"))) { + Entity *insert_entity = scope_insert_entity(scope, entity); + if (insert_entity) { + Entity *up = insert_entity->using_parent; + if (up != NULL) { + error(entity->token, + "Redeclararation of `%.*s` in this scope through `using`\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + return false; + } else { + TokenPos pos = insert_entity->token.pos; + if (token_pos_are_equal(pos, entity->token.pos)) { + // NOTE(bill): Error should have been handled already + return false; + } + error(entity->token, + "Redeclararation of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(pos.file), pos.line, pos.column); + return false; + } + } + } + if (identifier != NULL) { + add_entity_definition(&c->info, identifier, entity); + } + return true; +} + +void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) { + GB_ASSERT(identifier != NULL); + if (identifier->kind != AstNode_Ident) { + return; + } + map_entity_set(&c->info.uses, hash_pointer(identifier), entity); + add_declaration_dependency(c, entity); // TODO(bill): Should this be here? +} + + +void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { + GB_ASSERT(str_eq(identifier->Ident.string, e->token.string)); + add_entity(c, e->scope, identifier, e); + map_decl_info_set(&c->info.entities, hash_pointer(e), d); +} + +void add_type_info_type(Checker *c, Type *t) { + if (t == NULL) { + return; + } + t = default_type(t); + if (is_type_untyped(t)) { + return; // Could be nil + } + + if (map_isize_get(&c->info.type_info_map, hash_pointer(t)) != NULL) { + // Types have already been added + return; + } + + isize ti_index = -1; + for_array(i, c->info.type_info_map.entries) { + MapIsizeEntry *e = &c->info.type_info_map.entries.e[i]; + Type *prev_type = cast(Type *)e->key.ptr; + if (are_types_identical(t, prev_type)) { + // Duplicate entry + ti_index = e->value; + break; + } + } + if (ti_index < 0) { + // Unique entry + // NOTE(bill): map entries grow linearly and in order + ti_index = c->info.type_info_count; + c->info.type_info_count++; + } + map_isize_set(&c->info.type_info_map, hash_pointer(t), ti_index); + + + + + // Add nested types + + if (t->kind == Type_Named) { + // NOTE(bill): Just in case + add_type_info_type(c, t->Named.base); + return; + } + + Type *bt = base_type(t); + add_type_info_type(c, bt); + + switch (bt->kind) { + case Type_Basic: { + switch (bt->Basic.kind) { + case Basic_string: + add_type_info_type(c, t_u8_ptr); + add_type_info_type(c, t_int); + break; + case Basic_any: + add_type_info_type(c, t_type_info_ptr); + add_type_info_type(c, t_rawptr); + break; + } + } break; + + case Type_Maybe: + add_type_info_type(c, bt->Maybe.elem); + add_type_info_type(c, t_bool); + break; + + case Type_Pointer: + add_type_info_type(c, bt->Pointer.elem); + break; + + case Type_Array: + add_type_info_type(c, bt->Array.elem); + add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem)); + add_type_info_type(c, t_int); + break; + case Type_Slice: + add_type_info_type(c, bt->Slice.elem); + add_type_info_type(c, make_type_pointer(c->allocator, bt->Slice.elem)); + add_type_info_type(c, t_int); + break; + case Type_Vector: + add_type_info_type(c, bt->Vector.elem); + add_type_info_type(c, t_int); + break; + + case Type_Record: { + switch (bt->Record.kind) { + case TypeRecord_Enum: + add_type_info_type(c, bt->Record.enum_base); + break; + + case TypeRecord_Union: + add_type_info_type(c, t_int); + /* fallthrough */ + default: + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->Record.fields[i]; + add_type_info_type(c, f->type); + } + break; + } + } break; + + case Type_Tuple: + for (isize i = 0; i < bt->Tuple.variable_count; i++) { + Entity *var = bt->Tuple.variables[i]; + add_type_info_type(c, var->type); + } + break; + + case Type_Proc: + add_type_info_type(c, bt->Proc.params); + add_type_info_type(c, bt->Proc.results); + break; + } +} + + +void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) { + ProcedureInfo info = {0}; + info.file = file; + info.token = token; + info.decl = decl; + info.type = type; + info.body = body; + info.tags = tags; + array_add(&c->procs, info); +} + +void push_procedure(Checker *c, Type *type) { + array_add(&c->proc_stack, type); +} + +void pop_procedure(Checker *c) { + array_pop(&c->proc_stack); +} + +Type *const curr_procedure(Checker *c) { + isize count = c->proc_stack.count; + if (count > 0) { + return c->proc_stack.e[count-1]; + } + return NULL; +} + +void add_curr_ast_file(Checker *c, AstFile *file) { + TokenPos zero_pos = {0}; + global_error_collector.prev = zero_pos; + c->curr_ast_file = file; + c->context.decl = file->decl_info; +} + + + + +void add_dependency_to_map(MapEntity *map, CheckerInfo *info, Entity *node) { + if (node == NULL) { + return; + } + if (map_entity_get(map, hash_pointer(node)) != NULL) { + return; + } + map_entity_set(map, hash_pointer(node), node); + + + DeclInfo **found = map_decl_info_get(&info->entities, hash_pointer(node)); + if (found == NULL) { + return; + } + + DeclInfo *decl = *found; + for_array(i, decl->deps.entries) { + Entity *e = cast(Entity *)decl->deps.entries.e[i].key.ptr; + add_dependency_to_map(map, info, e); + } +} + +MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) { + MapEntity map = {0}; // Key: Entity * + map_entity_init(&map, heap_allocator()); + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->scope->is_global) { + // NOTE(bill): Require runtime stuff + add_dependency_to_map(&map, info, e); + } + } + + add_dependency_to_map(&map, info, start); + + return map; +} + + + + +#include "expr.c" +#include "decl.c" +#include "stmt.c" + +void init_preload_types(Checker *c) { + if (t_type_info == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Type_Info")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Type_Info`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_type_info = e->type; + t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); + GB_ASSERT(is_type_union(e->type)); + TypeRecord *record = &base_type(e->type)->Record; + + t_type_info_member = record->other_fields[0]->type; + t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member); + + if (record->field_count != 18) { + compiler_error("Invalid `Type_Info` layout"); + } + t_type_info_named = record->fields[ 1]->type; + t_type_info_integer = record->fields[ 2]->type; + t_type_info_float = record->fields[ 3]->type; + t_type_info_any = record->fields[ 4]->type; + t_type_info_string = record->fields[ 5]->type; + t_type_info_boolean = record->fields[ 6]->type; + t_type_info_pointer = record->fields[ 7]->type; + t_type_info_maybe = record->fields[ 8]->type; + t_type_info_procedure = record->fields[ 9]->type; + t_type_info_array = record->fields[10]->type; + t_type_info_slice = record->fields[11]->type; + t_type_info_vector = record->fields[12]->type; + t_type_info_tuple = record->fields[13]->type; + t_type_info_struct = record->fields[14]->type; + t_type_info_union = record->fields[15]->type; + t_type_info_raw_union = record->fields[16]->type; + t_type_info_enum = record->fields[17]->type; + } + + if (t_allocator == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Allocator")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Allocator`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_allocator = e->type; + t_allocator_ptr = make_type_pointer(c->allocator, t_allocator); + } + + if (t_context == NULL) { + Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Context")); + if (e == NULL) { + compiler_error("Could not find type declaration for `Context`\n" + "Is `runtime.odin` missing from the `core` directory relative to odin.exe?"); + } + t_context = e->type; + t_context_ptr = make_type_pointer(c->allocator, t_context); + + } + +} + +void add_implicit_value(Checker *c, ImplicitValueId id, String name, String backing_name, Type *type) { + ImplicitValueInfo info = {name, backing_name, type}; + Entity *value = make_entity_implicit_value(c->allocator, info.name, info.type, id); + Entity *prev = scope_insert_entity(c->global_scope, value); + GB_ASSERT(prev == NULL); + implicit_value_infos[id] = info; + c->info.implicit_values[id] = value; +} + + +void check_global_entity(Checker *c, EntityKind kind) { + for_array(i, c->info.entities.entries) { + MapDeclInfoEntry *entry = &c->info.entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->kind == kind) { + DeclInfo *d = entry->value; + + add_curr_ast_file(c, d->scope->file); + + if (d->scope == e->scope) { + if (kind != Entity_Procedure && str_eq(e->token.string, str_lit("main"))) { + if (e->scope->is_init) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + } else if (e->scope->is_global && str_eq(e->token.string, str_lit("main"))) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + + Scope *prev_scope = c->context.scope; + c->context.scope = d->scope; + check_entity_decl(c, e, d, NULL, NULL); + } + } + } +} + +void check_parsed_files(Checker *c) { + AstNodeArray import_decls; + array_init(&import_decls, heap_allocator()); + + MapScope file_scopes; // Key: String (fullpath) + map_scope_init(&file_scopes, heap_allocator()); + + // Map full filepaths to Scopes + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + Scope *scope = NULL; + scope = make_scope(c->global_scope, c->allocator); + scope->is_global = f->is_global_scope; + scope->is_file = true; + scope->file = f; + if (i == 0) { + // NOTE(bill): First file is always the initial file + // thus it must contain main + scope->is_init = true; + } + + if (scope->is_global) { + array_add(&c->global_scope->shared, scope); + } + + f->scope = scope; + f->decl_info = make_declaration_info(c->allocator, f->scope); + HashKey key = hash_string(f->tokenizer.fullpath); + map_scope_set(&file_scopes, key, scope); + map_ast_file_set(&c->info.files, key, f); + } + + // Collect Entities + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + + for_array(decl_index, f->decls) { + AstNode *decl = f->decls.e[decl_index]; + if (!is_ast_node_decl(decl)) { + continue; + } + + switch (decl->kind) { + case_ast_node(bd, BadDecl, decl); + case_end; + case_ast_node(id, ImportDecl, decl); + // NOTE(bill): Handle later + case_end; + case_ast_node(fsl, ForeignLibrary, decl); + // NOTE(bill): ignore + case_end; + + case_ast_node(cd, ConstDecl, decl); + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + ExactValue v = {ExactValue_Invalid}; + Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v); + e->identifier = name; + DeclInfo *di = make_declaration_info(c->allocator, file_scope); + di->type_expr = cd->type; + di->init_expr = value; + add_entity_and_decl_info(c, name, e, di); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(decl), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(decl), "Extra initial expression"); + } + case_end; + + case_ast_node(vd, VarDecl, decl); + isize entity_count = vd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + DeclInfo *di = NULL; + if (vd->values.count > 0) { + di = make_declaration_info(heap_allocator(), file_scope); + di->entities = entities; + di->entity_count = entity_count; + di->type_expr = vd->type; + di->init_expr = vd->values.e[0]; + } + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + AstNode *value = NULL; + if (i < vd->values.count) { + value = vd->values.e[i]; + } + Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL); + e->identifier = name; + entities[entity_index++] = e; + + DeclInfo *d = di; + if (d == NULL) { + AstNode *init_expr = value; + d = make_declaration_info(heap_allocator(), file_scope); + d->type_expr = vd->type; + d->init_expr = init_expr; + d->var_decl_tags = vd->tags; + } + + add_entity_and_decl_info(c, name, e, d); + } + case_end; + + case_ast_node(td, TypeDecl, decl); + ast_node(n, Ident, td->name); + Entity *e = make_entity_type_name(c->allocator, file_scope, *n, NULL); + e->identifier = td->name; + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = td->type; + add_entity_and_decl_info(c, td->name, e, d); + case_end; + + case_ast_node(pd, ProcDecl, decl); + ast_node(n, Ident, pd->name); + Token token = *n; + Entity *e = make_entity_procedure(c->allocator, file_scope, token, NULL); + e->identifier = pd->name; + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->proc_decl = decl; + add_entity_and_decl_info(c, pd->name, e, d); + case_end; + + default: + error(ast_node_token(decl), "Only declarations are allowed at file scope"); + break; + } + } + } + + for_array(i, c->parser->files) { + AstFile *f = &c->parser->files.e[i]; + add_curr_ast_file(c, f); + + Scope *file_scope = f->scope; + + for_array(decl_index, f->decls) { + AstNode *decl = f->decls.e[decl_index]; + if (decl->kind != AstNode_ImportDecl) { + continue; + } + ast_node(id, ImportDecl, decl); + + HashKey key = hash_string(id->fullpath); + Scope **found = map_scope_get(&file_scopes, key); + GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath)); + Scope *scope = *found; + + if (scope->is_global) { + error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary"); + continue; + } + + bool previously_added = false; + for_array(import_index, file_scope->imported) { + Scope *prev = file_scope->imported.e[import_index]; + if (prev == scope) { + previously_added = true; + break; + } + } + + if (!previously_added) { + array_add(&file_scope->imported, scope); + } else { + warning(id->token, "Multiple #import of the same file within this scope"); + } + + if (str_eq(id->import_name.string, str_lit("."))) { + // NOTE(bill): Add imported entities to this file's scope + for_array(elem_index, scope->elements.entries) { + Entity *e = scope->elements.entries.e[elem_index].value; + if (e->scope == file_scope) { + continue; + } + + // NOTE(bill): Do not add other imported entities + add_entity(c, file_scope, NULL, e); + if (!id->is_load) { // `#import`ed entities don't get exported + HashKey key = hash_string(e->token.string); + map_entity_set(&file_scope->implicit, key, e); + } + } + } else { + String import_name = id->import_name.string; + if (import_name.len == 0) { + // NOTE(bill): use file name (without extension) as the identifier + // If it is a valid identifier + String filename = id->fullpath; + isize slash = 0; + isize dot = 0; + for (isize i = filename.len-1; i >= 0; i--) { + u8 c = filename.text[i]; + if (c == '/' || c == '\\') { + break; + } + slash = i; + } + + filename.text += slash; + filename.len -= slash; + + dot = filename.len; + while (dot --> 0) { + u8 c = filename.text[dot]; + if (c == '.') { + break; + } + } + + filename.len = dot; + + if (is_string_an_identifier(filename)) { + import_name = filename; + } else { + error(ast_node_token(decl), + "File name, %.*s, cannot be as an import name as it is not a valid identifier", + LIT(filename)); + } + } + + if (import_name.len > 0) { + id->import_name.string = import_name; + Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid, + id->fullpath, id->import_name.string, + scope); + add_entity(c, file_scope, NULL, e); + } + } + } + } + + check_global_entity(c, Entity_TypeName); + + init_preload_types(c); + add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context); + + check_global_entity(c, Entity_Constant); + check_global_entity(c, Entity_Procedure); + check_global_entity(c, Entity_Variable); + + for (isize i = 1; i < ImplicitValue_Count; i++) { + // NOTE(bill): First is invalid + Entity *e = c->info.implicit_values[i]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + + ImplicitValueInfo *ivi = &implicit_value_infos[i]; + Entity *backing = scope_lookup_entity(e->scope, ivi->backing_name); + GB_ASSERT(backing != NULL); + e->ImplicitValue.backing = backing; + } + + + // Check procedure bodies + for_array(i, c->procs) { + ProcedureInfo *pi = &c->procs.e[i]; + add_curr_ast_file(c, pi->file); + + bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; + bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; + + CheckerContext prev_context = c->context; + + if (bounds_check) { + c->context.stmt_state_flags |= StmtStateFlag_bounds_check; + c->context.stmt_state_flags &= ~StmtStateFlag_no_bounds_check; + } else if (no_bounds_check) { + c->context.stmt_state_flags |= StmtStateFlag_no_bounds_check; + c->context.stmt_state_flags &= ~StmtStateFlag_bounds_check; + } + + check_proc_body(c, pi->token, pi->decl, pi->type, pi->body); + + c->context = prev_context; + } + + // Add untyped expression values + for_array(i, c->info.untyped.entries) { + MapExprInfoEntry *entry = &c->info.untyped.entries.e[i]; + HashKey key = entry->key; + AstNode *expr = cast(AstNode *)cast(uintptr)key.key; + ExprInfo *info = &entry->value; + if (info != NULL && expr != NULL) { + if (is_type_typed(info->type)) { + compiler_error("%s (type %s) is typed!", expr_to_string(expr), type_to_string(info->type)); + } + add_type_and_value(&c->info, expr, info->mode, info->type, info->value); + } + } + + for (isize i = 0; i < gb_count_of(basic_types)-1; i++) { + Type *t = &basic_types[i]; + if (t->Basic.size > 0) { + add_type_info_type(c, t); + } + } + + for (isize i = 0; i < gb_count_of(basic_type_aliases)-1; i++) { + Type *t = &basic_type_aliases[i]; + if (t->Basic.size > 0) { + add_type_info_type(c, t); + } + } + + map_scope_destroy(&file_scopes); + array_free(&import_decls); +} + + + diff --git a/src/checker/decl.c b/src/checker/decl.c new file mode 100644 index 000000000..f5a5daad6 --- /dev/null +++ b/src/checker/decl.c @@ -0,0 +1,545 @@ +bool check_is_terminating(AstNode *node); +void check_stmt (Checker *c, AstNode *node, u32 flags); +void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags); +void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker); +void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr); +void check_proc_decl (Checker *c, Entity *e, DeclInfo *d); +void check_var_decl (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr); + +// NOTE(bill): `content_name` is for debugging and error messages +Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) { + if (operand->mode == Addressing_Invalid || + operand->type == t_invalid || + e->type == t_invalid) { + + if (operand->mode == Addressing_Builtin) { + gbString expr_str = expr_to_string(operand->expr); + + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign builtin procedure `%s` in %.*s", + expr_str, + LIT(context_name)); + + operand->mode = Addressing_Invalid; + + gb_string_free(expr_str); + } + + + if (e->type == NULL) { + e->type = t_invalid; + } + return NULL; + } + + if (e->type == NULL) { + // NOTE(bill): Use the type of the operand + Type *t = operand->type; + if (is_type_untyped(t)) { + if (t == t_invalid || is_type_untyped_nil(t)) { + error(e->token, "Use of untyped nil in %.*s", LIT(context_name)); + e->type = t_invalid; + return NULL; + } + t = default_type(t); + } + e->type = t; + } + + check_assignment(c, operand, e->type, context_name); + if (operand->mode == Addressing_Invalid) { + return NULL; + } + + return e->type; +} + +void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) { + if ((lhs == NULL || lhs_count == 0) && inits.count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be + // an extra allocation + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count); + + for_array(i, inits) { + AstNode *rhs = inits.e[i]; + Operand o = {0}; + check_multi_expr(c, &o, rhs); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + isize rhs_count = operands.count; + for_array(i, operands) { + if (operands.e[i].mode == Addressing_Invalid) { + rhs_count--; + } + } + + + isize max = gb_min(lhs_count, rhs_count); + for (isize i = 0; i < max; i++) { + check_init_variable(c, lhs[i], &operands.e[i], context_name); + } + + if (rhs_count > 0 && lhs_count != rhs_count) { + error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count); + } + + gb_temp_arena_memory_end(tmp); +} + + + +void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) { + if (e->type != NULL) { + return; + } + + if (d == NULL) { + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + if (found) { + d = *found; + } else { + e->type = t_invalid; + set_base_type(named_type, t_invalid); + return; + // GB_PANIC("`%.*s` should been declared!", LIT(e->token.string)); + } + } + + if (e->kind == Entity_Procedure) { + check_proc_decl(c, e, d); + return; + } + CheckerContext prev = c->context; + c->context.scope = d->scope; + c->context.decl = d; + + switch (e->kind) { + case Entity_Constant: + check_const_decl(c, e, d->type_expr, d->init_expr); + break; + case Entity_Variable: + check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr); + break; + case Entity_TypeName: + check_type_decl(c, e, d->type_expr, named_type, cycle_checker); + break; + } + + c->context = prev; +} + + + +void check_var_decl_node(Checker *c, AstNode *node) { + ast_node(vd, VarDecl, node); + isize entity_count = vd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + Entity *entity = NULL; + if (name->kind == AstNode_Ident) { + Token token = name->Ident; + String str = token.string; + Entity *found = NULL; + // NOTE(bill): Ignore assignments to `_` + if (str_ne(str, str_lit("_"))) { + found = current_scope_lookup_entity(c->context.scope, str); + } + if (found == NULL) { + entity = make_entity_variable(c->allocator, c->context.scope, token, NULL); + add_entity_definition(&c->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } else { + error(ast_node_token(name), "A variable declaration must be an identifier"); + } + if (entity == NULL) { + entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + } + entities[entity_index++] = entity; + } + + Type *init_type = NULL; + if (vd->type) { + init_type = check_type_extra(c, vd->type, NULL, NULL); + if (init_type == NULL) + init_type = t_invalid; + } + + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != NULL); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; + } + e->flags |= EntityFlag_Visited; + + if (e->type == NULL) + e->type = init_type; + } + + check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); + + for_array(i, vd->names) { + if (entities[i] != NULL) { + add_entity(c, c->context.scope, vd->names.e[i], entities[i]); + } + } + +} + + + +void check_init_constant(Checker *c, Entity *e, Operand *operand) { + if (operand->mode == Addressing_Invalid || + operand->type == t_invalid || + e->type == t_invalid) { + if (e->type == NULL) { + e->type = t_invalid; + } + return; + } + + if (operand->mode != Addressing_Constant) { + // TODO(bill): better error + error(ast_node_token(operand->expr), + "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string)); + if (e->type == NULL) { + e->type = t_invalid; + } + return; + } + // if (!is_type_constant_type(operand->type)) { + // gbString type_str = type_to_string(operand->type); + // defer (gb_string_free(type_str)); + // error(ast_node_token(operand->expr), + // "Invalid constant type: `%s`", type_str); + // if (e->type == NULL) { + // e->type = t_invalid; + // } + // return; + // } + + if (e->type == NULL) { // NOTE(bill): type inference + e->type = operand->type; + } + + check_assignment(c, operand, e->type, str_lit("constant declaration")); + if (operand->mode == Addressing_Invalid) { + return; + } + + e->Constant.value = operand->value; +} + + +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) { + GB_ASSERT(e->type == NULL); + + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + return; + } + e->flags |= EntityFlag_Visited; + + if (type_expr) { + Type *t = check_type(c, type_expr); + // if (!is_type_constant_type(t)) { + // gbString str = type_to_string(t); + // defer (gb_string_free(str)); + // error(ast_node_token(type_expr), + // "Invalid constant type `%s`", str); + // e->type = t_invalid; + // return; + // } + e->type = t; + } + + Operand operand = {0}; + if (init_expr) { + check_expr(c, &operand, init_expr); + } + check_init_constant(c, e, &operand); +} + +void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) { + GB_ASSERT(e->type == NULL); + Type *named = make_type_named(c->allocator, e->token.string, NULL, e); + named->Named.type_name = e; + if (def != NULL && def->kind == Type_Named) { + def->Named.base = named; + } + e->type = named; + + CycleChecker local_cycle_checker = {0}; + if (cycle_checker == NULL) { + cycle_checker = &local_cycle_checker; + } + + Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e)); + named->Named.base = bt; + named->Named.base = base_type(named->Named.base); + if (named->Named.base == t_invalid) { + gb_printf("check_type_decl: %s\n", type_to_string(named)); + } + + cycle_checker_destroy(&local_cycle_checker); +} + + +bool are_signatures_similar_enough(Type *a_, Type *b_) { + GB_ASSERT(a_->kind == Type_Proc); + GB_ASSERT(b_->kind == Type_Proc); + TypeProc *a = &a_->Proc; + TypeProc *b = &b_->Proc; + + if (a->param_count != b->param_count) { + return false; + } + if (a->result_count != b->result_count) { + return false; + } + for (isize i = 0; i < a->param_count; i++) { + Type *x = base_type(a->params->Tuple.variables[i]->type); + Type *y = base_type(b->params->Tuple.variables[i]->type); + if (is_type_pointer(x) && is_type_pointer(y)) { + continue; + } + + if (!are_types_identical(x, y)) { + return false; + } + } + for (isize i = 0; i < a->result_count; i++) { + Type *x = base_type(a->results->Tuple.variables[i]->type); + Type *y = base_type(b->results->Tuple.variables[i]->type); + if (is_type_pointer(x) && is_type_pointer(y)) { + continue; + } + + if (!are_types_identical(x, y)) { + return false; + } + } + + return true; +} + +void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { + GB_ASSERT(e->type == NULL); + + Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false); + e->type = proc_type; + ast_node(pd, ProcDecl, d->proc_decl); + + check_open_scope(c, pd->type); + check_procedure_type(c, proc_type, pd->type); + + bool is_foreign = (pd->tags & ProcTag_foreign) != 0; + bool is_link_name = (pd->tags & ProcTag_link_name) != 0; + bool is_inline = (pd->tags & ProcTag_inline) != 0; + bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0; + + if ((d->scope->is_file || d->scope->is_global) && + str_eq(e->token.string, str_lit("main"))) { + if (proc_type != NULL) { + TypeProc *pt = &proc_type->Proc; + if (pt->param_count != 0 || + pt->result_count) { + gbString str = type_to_string(proc_type); + error(e->token, + "Procedure type of `main` was expected to be `proc()`, got %s", str); + gb_string_free(str); + } + } + } + + if (is_inline && is_no_inline) { + error(ast_node_token(pd->type), + "You cannot apply both `inline` and `no_inline` to a procedure"); + } + + if (is_foreign && is_link_name) { + error(ast_node_token(pd->type), + "You cannot apply both `foreign` and `link_name` to a procedure"); + } + + if (pd->body != NULL) { + if (is_foreign) { + error(ast_node_token(pd->body), + "A procedure tagged as `#foreign` cannot have a body"); + } + + d->scope = c->context.scope; + + GB_ASSERT(pd->body->kind == AstNode_BlockStmt); + check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags); + } + + if (is_foreign) { + MapEntity *fp = &c->info.foreign_procs; + AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl; + String name = proc_decl->name->Ident.string; + if (proc_decl->foreign_name.len > 0) { + name = proc_decl->foreign_name; + } + HashKey key = hash_string(name); + Entity **found = map_entity_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_signatures_similar_enough(this_type, other_type)) { + error(ast_node_token(d->proc_decl), + "Redeclaration of #foreign procedure `%.*s` with different type signatures\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } + } else { + map_entity_set(fp, key, e); + } + } else if (is_link_name) { + MapEntity *fp = &c->info.foreign_procs; + AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl; + String name = proc_decl->link_name; + + HashKey key = hash_string(name); + Entity **found = map_entity_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + error(ast_node_token(d->proc_decl), + "Non unique #link_name for procedure `%.*s`\n" + "\tother at %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } else { + map_entity_set(fp, key, e); + } + } + + check_close_scope(c); +} + +void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) { + GB_ASSERT(e->type == NULL); + GB_ASSERT(e->kind == Entity_Variable); + + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + return; + } + e->flags |= EntityFlag_Visited; + + if (type_expr != NULL) + e->type = check_type_extra(c, type_expr, NULL, NULL); + + if (init_expr == NULL) { + if (type_expr == NULL) + e->type = t_invalid; + return; + } + + if (entities == NULL || entity_count == 1) { + GB_ASSERT(entities == NULL || entities[0] == e); + Operand operand = {0}; + check_expr(c, &operand, init_expr); + check_init_variable(c, e, &operand, str_lit("variable declaration")); + } + + if (type_expr != NULL) { + for (isize i = 0; i < entity_count; i++) + entities[i]->type = e->type; + } + + AstNodeArray inits; + array_init_reserve(&inits, c->allocator, 1); + array_add(&inits, init_expr); + check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration")); +} + +void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) { + GB_ASSERT(body->kind == AstNode_BlockStmt); + + CheckerContext old_context = c->context; + c->context.scope = decl->scope; + c->context.decl = decl; + + GB_ASSERT(type->kind == Type_Proc); + if (type->Proc.param_count > 0) { + TypeTuple *params = &type->Proc.params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (!(e->flags & EntityFlag_Anonymous)) { + continue; + } + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + break; + } + } + } + } else { + error(e->token, "`using` can only be applied to variables of type struct or raw_union"); + break; + } + } + } + + push_procedure(c, type); + { + ast_node(bs, BlockStmt, body); + // TODO(bill): Check declarations first (except mutable variable declarations) + check_stmt_list(c, bs->stmts, 0); + if (type->Proc.result_count > 0) { + if (!check_is_terminating(body)) { + error(bs->close, "Missing return statement at the end of the procedure"); + } + } + } + pop_procedure(c); + + + check_scope_usage(c, c->context.scope); + + c->context = old_context; +} + + + diff --git a/src/checker/entity.c b/src/checker/entity.c new file mode 100644 index 000000000..df1ecf28d --- /dev/null +++ b/src/checker/entity.c @@ -0,0 +1,179 @@ +typedef struct Scope Scope; +typedef struct Checker Checker; +typedef struct Type Type; +typedef enum BuiltinProcId BuiltinProcId; +typedef enum ImplicitValueId ImplicitValueId; + +#define ENTITY_KINDS \ + ENTITY_KIND(Invalid) \ + ENTITY_KIND(Constant) \ + ENTITY_KIND(Variable) \ + ENTITY_KIND(TypeName) \ + ENTITY_KIND(Procedure) \ + ENTITY_KIND(Builtin) \ + ENTITY_KIND(ImportName) \ + ENTITY_KIND(Nil) \ + ENTITY_KIND(ImplicitValue) \ + ENTITY_KIND(Count) + +typedef enum EntityKind { +#define ENTITY_KIND(k) GB_JOIN2(Entity_, k), + ENTITY_KINDS +#undef ENTITY_KIND +} EntityKind; + +String const entity_strings[] = { +#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1}, + ENTITY_KINDS +#undef ENTITY_KIND +}; + +typedef enum EntityFlag { + EntityFlag_Visited = 1<<0, + EntityFlag_Used = 1<<1, + EntityFlag_Anonymous = 1<<2, + EntityFlag_Field = 1<<3, + EntityFlag_Param = 1<<4, + EntityFlag_VectorElem = 1<<5, +} EntityFlag; + +typedef struct Entity Entity; +struct Entity { + EntityKind kind; + u32 flags; + Token token; + Scope * scope; + Type * type; + AstNode * identifier; // Can be NULL + + // TODO(bill): Cleanup how `using` works for entities + Entity * using_parent; + AstNode * using_expr; + + union { + struct { + ExactValue value; + } Constant; + struct { + i32 field_index; + i32 field_src_index; + } Variable; + i32 TypeName; + i32 Procedure; + struct { + BuiltinProcId id; + } Builtin; + struct { + String path; + String name; + Scope *scope; + bool used; + } ImportName; + i32 Nil; + struct { + // TODO(bill): Should this be a user-level construct rather than compiler-level? + ImplicitValueId id; + Entity * backing; + } ImplicitValue; + }; +}; + +Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) { + Entity *entity = gb_alloc_item(a, Entity); + entity->kind = kind; + entity->scope = scope; + entity->token = token; + entity->type = type; + return entity; +} + +Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) { + Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type); + return entity; +} + +Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) { + GB_ASSERT(parent != NULL); + Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type); + entity->using_parent = parent; + entity->flags |= EntityFlag_Anonymous; + return entity; +} + + +Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) { + Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type); + entity->Constant.value = value; + return entity; +} + +Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) { + Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type); + return entity; +} + +Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->flags |= EntityFlag_Used; + entity->flags |= EntityFlag_Anonymous*(anonymous != 0); + entity->flags |= EntityFlag_Param; + return entity; +} + +Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->Variable.field_src_index = field_src_index; + entity->Variable.field_index = field_src_index; + entity->flags |= EntityFlag_Field; + entity->flags |= EntityFlag_Anonymous*(anonymous != 0); + return entity; +} + +Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) { + Entity *entity = make_entity_variable(a, scope, token, type); + entity->Variable.field_src_index = field_src_index; + entity->Variable.field_index = field_src_index; + entity->flags |= EntityFlag_Field; + entity->flags |= EntityFlag_VectorElem; + return entity; +} + +Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) { + Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type); + return entity; +} + +Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) { + Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type); + entity->Builtin.id = id; + return entity; +} + +Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type, + String path, String name, Scope *import_scope) { + Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type); + entity->ImportName.path = path; + entity->ImportName.name = name; + entity->ImportName.scope = import_scope; + 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); + return entity; +} + +Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) { + Token token = make_token_ident(name); + Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type); + entity->ImplicitValue.id = id; + return entity; +} + + +Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) { + token.string = str_lit("_"); + return make_entity_variable(a, file_scope, token, NULL); +} + diff --git a/src/checker/expr.c b/src/checker/expr.c new file mode 100644 index 000000000..6f16da451 --- /dev/null +++ b/src/checker/expr.c @@ -0,0 +1,4465 @@ +void check_expr (Checker *c, Operand *operand, AstNode *expression); +void check_multi_expr (Checker *c, Operand *operand, AstNode *expression); +void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression); +ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint); +Type * check_type_extra (Checker *c, AstNode *expression, Type *named_type, CycleChecker *cycle_checker); +Type * check_type (Checker *c, AstNode *expression); +void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker); +Entity * check_selector (Checker *c, Operand *operand, AstNode *node); +void check_not_tuple (Checker *c, Operand *operand); +bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value); +void convert_to_typed (Checker *c, Operand *operand, Type *target_type, i32 level); +gbString expr_to_string (AstNode *expression); +void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker); +void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body); +void update_expr_type (Checker *c, AstNode *e, Type *type, bool final); + +gb_inline Type *check_type(Checker *c, AstNode *expression) { + return check_type_extra(c, expression, NULL, NULL); +} + + + +bool check_is_assignable_to_using_subtype(Type *dst, Type *src) { + Type *prev_src = src; + // Type *prev_dst = dst; + src = base_type(type_deref(src)); + // dst = base_type(type_deref(dst)); + bool src_is_ptr = src != prev_src; + // bool dst_is_ptr = dst != prev_dst; + + if (is_type_struct(src)) { + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind == Entity_Variable && (f->flags & EntityFlag_Anonymous)) { + if (are_types_identical(dst, f->type)) { + return true; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(type_deref(dst), f->type)) { + return true; + } + } + bool ok = check_is_assignable_to_using_subtype(dst, f->type); + if (ok) { + return true; + } + } + } + } + return false; +} + + +bool check_is_assignable_to(Checker *c, Operand *operand, Type *type) { + if (operand->mode == Addressing_Invalid || + type == t_invalid) { + return true; + } + + if (operand->mode == Addressing_Builtin) { + return false; + } + + Type *s = operand->type; + + if (are_types_identical(s, type)) { + return true; + } + + Type *src = base_type(s); + Type *dst = base_type(type); + + if (is_type_untyped(src)) { + switch (dst->kind) { + case Type_Basic: + if (operand->mode == Addressing_Constant) { + return check_value_is_expressible(c, operand->value, dst, NULL); + } + if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) { + return is_type_boolean(dst); + } + break; + } + if (type_has_nil(dst)) { + return operand->mode == Addressing_Value && operand->type == t_untyped_nil; + } + } + + if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) { + if (is_type_enum(dst) && is_type_enum(src)) { + return are_types_identical(s, type); + } + return true; + } + + if (is_type_maybe(dst)) { + Type *elem = base_type(dst)->Maybe.elem; + return are_types_identical(elem, s); + } + + if (is_type_untyped_nil(src)) { + return type_has_nil(dst); + } + + // ^T <- rawptr + // TODO(bill): Should C-style (not C++) pointer cast be allowed? + // if (is_type_pointer(dst) && is_type_rawptr(src)) { + // return true; + // } + + // rawptr <- ^T + if (is_type_rawptr(dst) && is_type_pointer(src)) { + return true; + } + + + + if (dst->kind == Type_Array && src->kind == Type_Array) { + if (are_types_identical(dst->Array.elem, src->Array.elem)) { + return dst->Array.count == src->Array.count; + } + } + + if (dst->kind == Type_Slice && src->kind == Type_Slice) { + if (are_types_identical(dst->Slice.elem, src->Slice.elem)) { + return true; + } + } + + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, s)) { + return true; + } + } + } + + + if (dst == t_any) { + // NOTE(bill): Anything can cast to `Any` + add_type_info_type(c, s); + return true; + } + + return false; +} + + +// NOTE(bill): `content_name` is for debugging and error messages +void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) { + check_not_tuple(c, operand); + if (operand->mode == Addressing_Invalid) { + return; + } + + if (is_type_untyped(operand->type)) { + Type *target_type = type; + + if (type == NULL || is_type_any(type) || is_type_untyped_nil(type)) { + if (type == NULL && base_type(operand->type) == t_untyped_nil) { + error(ast_node_token(operand->expr), "Use of untyped nil in %.*s", LIT(context_name)); + operand->mode = Addressing_Invalid; + return; + } + + add_type_info_type(c, type); + target_type = default_type(operand->type); + } + convert_to_typed(c, operand, target_type, 0); + if (operand->mode == Addressing_Invalid) { + return; + } + } + + if (type != NULL) { + if (!check_is_assignable_to(c, operand, type)) { + gbString type_str = type_to_string(type); + gbString op_type_str = type_to_string(operand->type); + gbString expr_str = expr_to_string(operand->expr); + + if (operand->mode == Addressing_Builtin) { + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign builtin procedure `%s` in %.*s", + expr_str, + LIT(context_name)); + } else { + // TODO(bill): is this a good enough error message? + error(ast_node_token(operand->expr), + "Cannot assign value `%s` of type `%s` to `%s` in %.*s", + expr_str, + op_type_str, + type_str, + LIT(context_name)); + } + operand->mode = Addressing_Invalid; + + gb_string_free(expr_str); + gb_string_free(op_type_str); + gb_string_free(type_str); + return; + } + } +} + + +void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *entity_map) { + t = base_type(type_deref(t)); + gbString str = expr_to_string(node); + + if (t->kind == Type_Record) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + HashKey key = hash_string(name); + Entity **found = map_entity_get(entity_map, key); + if (found != NULL) { + Entity *e = *found; + // TODO(bill): Better type error + error(e->token, "`%.*s` is already declared in `%s`", LIT(name), str); + } else { + map_entity_set(entity_map, key, f); + add_entity(c, c->context.scope, NULL, f); + if (f->flags & EntityFlag_Anonymous) { + populate_using_entity_map(c, node, f->type, entity_map); + } + } + } + } + + gb_string_free(str); +} + +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr); + +void check_fields(Checker *c, AstNode *node, AstNodeArray decls, + Entity **fields, isize field_count, + Entity **other_fields, isize other_field_count, + CycleChecker *cycle_checker, String context) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + MapEntity entity_map = {0}; + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(field_count+other_field_count)); + + isize other_field_index = 0; + Entity *using_index_expr = NULL; + + + typedef struct { + Entity *e; + AstNode *t; + } Delay; + Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count); + Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, other_field_count); + + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind == AstNode_ConstDecl) { + ast_node(cd, ConstDecl, decl); + + isize entity_count = cd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + + GB_ASSERT(name->kind == AstNode_Ident); + ExactValue v = {ExactValue_Invalid}; + Token name_token = name->Ident; + Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v); + entities[entity_index++] = e; + + Delay delay = {e, cd->type}; + array_add(&delayed_const, delay); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + // TODO(bill): Better error messages or is this good enough? + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(node), "Extra initial expression"); + } + + for_array(i, cd->names) { + AstNode *name = cd->names.e[i]; + Entity *e = entities[i]; + Token name_token = name->Ident; + if (str_eq(name_token.string, str_lit("_"))) { + other_fields[other_field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity(c, c->context.scope, name, e); + } + } + } else if (decl->kind == AstNode_TypeDecl) { + ast_node(td, TypeDecl, decl); + Token name_token = td->name->Ident; + + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL); + Delay delay = {e, td->type}; + array_add(&delayed_type, delay); + + if (str_eq(name_token.string, str_lit("_"))) { + other_fields[other_field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity(c, c->context.scope, td->name, e); + add_entity_use(c, td->name, e); + } + } + } + + for_array(i, delayed_type) { + check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL); + } + for_array(i, delayed_const) { + check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL); + } + + if (node->kind == AstNode_UnionType) { + isize field_index = 0; + fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL); + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind != AstNode_VarDecl) { + continue; + } + + ast_node(vd, VarDecl, decl); + Type *base_type = check_type_extra(c, vd->type, NULL, cycle_checker); + + for_array(name_index, vd->names) { + AstNode *name = vd->names.e[name_index]; + Token name_token = name->Ident; + + Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type); + type->Named.type_name = e; + add_entity(c, c->context.scope, name, e); + + if (str_eq(name_token.string, str_lit("_"))) { + error(name_token, "`_` cannot be used a union subtype"); + continue; + } + + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + fields[field_index++] = e; + } + add_entity_use(c, name, e); + } + } + } else { + isize field_index = 0; + for_array(decl_index, decls) { + AstNode *decl = decls.e[decl_index]; + if (decl->kind != AstNode_VarDecl) { + continue; + } + ast_node(vd, VarDecl, decl); + + Type *type = check_type_extra(c, vd->type, NULL, cycle_checker); + + if (vd->is_using) { + if (vd->names.count > 1) { + error(ast_node_token(vd->names.e[0]), + "Cannot apply `using` to more than one of the same type"); + } + } + + for_array(name_index, vd->names) { + AstNode *name = vd->names.e[name_index]; + Token name_token = name->Ident; + + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using, cast(i32)field_index); + e->identifier = name; + if (str_eq(name_token.string, str_lit("_"))) { + fields[field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + fields[field_index++] = e; + add_entity(c, c->context.scope, name, e); + } + add_entity_use(c, name, e); + } + } + + + if (vd->is_using) { + Type *t = base_type(type_deref(type)); + if (!is_type_struct(t) && !is_type_raw_union(t)) { + Token name_token = vd->names.e[0]->Ident; + if (is_type_indexable(t)) { + bool ok = true; + for_array(emi, entity_map.entries) { + Entity *e = entity_map.entries.e[emi].value; + if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + if (is_type_indexable(e->type)) { + if (e->identifier != vd->names.e[0]) { + ok = false; + using_index_expr = e; + break; + } + } + } + } + if (ok) { + using_index_expr = fields[field_index-1]; + } else { + fields[field_index-1]->flags &= ~EntityFlag_Anonymous; + error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string)); + } + } else { + error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string)); + continue; + } + } + + populate_using_entity_map(c, node, type, &entity_map); + } + } + } + + gb_temp_arena_memory_end(tmp); +} + + +// TODO(bill): Cleanup struct field reordering +// TODO(bill): Inline sorting procedure? +gb_global BaseTypeSizes __checker_sizes = {0}; +gb_global gbAllocator __checker_allocator = {0}; + +GB_COMPARE_PROC(cmp_struct_entity_size) { + // Rule: + // Biggest to smallest alignment + // if same alignment: biggest to smallest size + // if same size: order by source order + Entity *x = *(Entity **)a; + Entity *y = *(Entity **)b; + GB_ASSERT(x != NULL); + GB_ASSERT(y != NULL); + GB_ASSERT(x->kind == Entity_Variable); + GB_ASSERT(y->kind == Entity_Variable); + i64 xa = type_align_of(__checker_sizes, __checker_allocator, x->type); + i64 ya = type_align_of(__checker_sizes, __checker_allocator, y->type); + i64 xs = type_size_of(__checker_sizes, __checker_allocator, x->type); + i64 ys = type_size_of(__checker_sizes, __checker_allocator, y->type); + + if (xa == ya) { + if (xs == ys) { + i32 diff = x->Variable.field_index - y->Variable.field_index; + return diff < 0 ? -1 : diff > 0; + } + return xs > ys ? -1 : xs < ys; + } + return xa > ya ? -1 : xa < ya; +} + +void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(is_type_struct(struct_type)); + ast_node(st, StructType, node); + + isize field_count = 0; + isize other_field_count = 0; + for_array(decl_index, st->decls) { + AstNode *decl = st->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, st->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("struct")); + + + struct_type->Record.struct_is_packed = st->is_packed; + struct_type->Record.struct_is_ordered = st->is_ordered; + struct_type->Record.fields = fields; + struct_type->Record.fields_in_src_order = fields; + struct_type->Record.field_count = field_count; + struct_type->Record.other_fields = other_fields; + struct_type->Record.other_field_count = other_field_count; + + + + if (!st->is_packed && !st->is_ordered) { + // NOTE(bill): Reorder fields for reduced size/performance + + Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, field_count); + for (isize i = 0; i < field_count; i++) { + reordered_fields[i] = struct_type->Record.fields_in_src_order[i]; + } + + // NOTE(bill): Hacky thing + // TODO(bill): Probably make an inline sorting procedure rather than use global variables + __checker_sizes = c->sizes; + __checker_allocator = c->allocator; + // NOTE(bill): compound literal order must match source not layout + gb_sort_array(reordered_fields, field_count, cmp_struct_entity_size); + + for (isize i = 0; i < field_count; i++) { + reordered_fields[i]->Variable.field_index = i; + } + + struct_type->Record.fields = reordered_fields; + } + + type_set_offsets(c->sizes, c->allocator, struct_type); +} + +void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(is_type_union(union_type)); + ast_node(ut, UnionType, node); + + isize field_count = 1; + isize other_field_count = 0; + for_array(decl_index, ut->decls) { + AstNode *decl = ut->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; +} + +void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { + GB_ASSERT(node->kind == AstNode_RawUnionType); + GB_ASSERT(is_type_raw_union(union_type)); + ast_node(ut, RawUnionType, node); + + isize field_count = 0; + isize other_field_count = 0; + for_array(decl_index, ut->decls) { + AstNode *decl = ut->decls.e[decl_index]; + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + field_count += vd->names.count; + case_end; + + case_ast_node(cd, ConstDecl, decl); + other_field_count += cd->names.count; + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; + } + } + + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + + check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("raw union")); + + union_type->Record.fields = fields; + union_type->Record.field_count = field_count; + union_type->Record.other_fields = other_fields; + union_type->Record.other_field_count = other_field_count; +} + +GB_COMPARE_PROC(cmp_enum_order) { + // Rule: + // Biggest to smallest alignment + // if same alignment: biggest to smallest size + // if same size: order by source order + Entity *x = *(Entity **)a; + Entity *y = *(Entity **)b; + GB_ASSERT(x != NULL); + GB_ASSERT(y != NULL); + GB_ASSERT(x->kind == Entity_Constant); + GB_ASSERT(y->kind == Entity_Constant); + GB_ASSERT(x->Constant.value.kind == ExactValue_Integer); + GB_ASSERT(y->Constant.value.kind == ExactValue_Integer); + i64 i = x->Constant.value.value_integer; + i64 j = y->Constant.value.value_integer; + + return i < j ? -1 : i > j; +} + + + +void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) { + GB_ASSERT(node->kind == AstNode_EnumType); + GB_ASSERT(is_type_enum(enum_type)); + ast_node(et, EnumType, node); + + + + Type *base_type = t_int; + if (et->base_type != NULL) { + base_type = check_type(c, et->base_type); + } + + if (base_type == NULL || !is_type_integer(base_type)) { + error(et->token, "Base type for enumeration must be an integer"); + return; + } else + if (base_type == NULL) { + base_type = t_int; + } + enum_type->Record.enum_base = base_type; + + Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count); + isize field_index = 0; + ExactValue iota = make_exact_value_integer(-1); + i64 min_value = 0; + i64 max_value = 0; + + Type *constant_type = enum_type; + if (named_type != NULL) { + constant_type = named_type; + } + + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + MapEntity entity_map = {0}; + map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count)); + + Entity *blank_entity = make_entity_constant(c->allocator, c->context.scope, blank_token, constant_type, make_exact_value_integer(0));; + + for_array(i, et->fields) { + AstNode *field = et->fields.e[i]; + + ast_node(f, FieldValue, field); + Token name_token = f->field->Ident; + + if (str_eq(name_token.string, str_lit("count"))) { + error(name_token, "`count` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } else if (str_eq(name_token.string, str_lit("min_value"))) { + error(name_token, "`min_value` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } else if (str_eq(name_token.string, str_lit("max_value"))) { + error(name_token, "`max_value` is a reserved identifier for enumerations"); + fields[field_index++] = blank_entity; + continue; + } + + Operand o = {0}; + if (f->value != NULL) { + check_expr(c, &o, f->value); + if (o.mode != Addressing_Constant) { + error(ast_node_token(f->value), "Enumeration value must be a constant integer"); + o.mode = Addressing_Invalid; + } + if (o.mode != Addressing_Invalid) { + check_assignment(c, &o, constant_type, str_lit("enumeration")); + } + if (o.mode != Addressing_Invalid) { + iota = o.value; + } else { + Token add_token = {Token_Add}; + iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1)); + } + } else { + Token add_token = {Token_Add}; + iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1)); + } + + + Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, constant_type, iota); + if (min_value > iota.value_integer) { + min_value = iota.value_integer; + } + if (max_value < iota.value_integer) { + max_value = iota.value_integer; + } + + HashKey key = hash_string(name_token.string); + if (map_entity_get(&entity_map, key)) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this enumeration", LIT(name_token.string)); + } else { + map_entity_set(&entity_map, key, e); + add_entity(c, c->context.scope, NULL, e); + fields[field_index++] = e; + } + add_entity_use(c, f->field, e); + } + + GB_ASSERT(field_index <= et->fields.count); + + gb_sort_array(fields, field_index, cmp_enum_order); + + enum_type->Record.other_fields = fields; + enum_type->Record.other_field_count = field_index; + + enum_type->Record.enum_count = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("count")), t_int, make_exact_value_integer(enum_type->Record.other_field_count)); + enum_type->Record.min_value = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("min_value")), constant_type, make_exact_value_integer(min_value)); + enum_type->Record.max_value = make_entity_constant(c->allocator, NULL, + make_token_ident(str_lit("max_value")), constant_type, make_exact_value_integer(max_value)); + + gb_temp_arena_memory_end(tmp); +} + +Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) { + if (params.count == 0) { + return NULL; + } + + bool is_variadic = false; + + Type *tuple = make_type_tuple(c->allocator); + + isize variable_count = 0; + for_array(i, params) { + AstNode *field = params.e[i]; + ast_node(p, Parameter, field); + variable_count += p->names.count; + } + + Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count); + isize variable_index = 0; + for_array(i, params) { + ast_node(p, Parameter, params.e[i]); + AstNode *type_expr = p->type; + if (type_expr) { + if (type_expr->kind == AstNode_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + if (i+1 == params.count) { + is_variadic = true; + } else { + error(ast_node_token(params.e[i]), "Invalid AST: Invalid variadic parameter"); + } + } + + Type *type = check_type(c, type_expr); + for_array(j, p->names) { + AstNode *name = p->names.e[j]; + if (name->kind == AstNode_Ident) { + Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->is_using); + add_entity(c, scope, name, param); + variables[variable_index++] = param; + } else { + error(ast_node_token(name), "Invalid AST: Invalid parameter"); + } + } + } + } + + variable_count = variable_index; + + if (is_variadic) { + GB_ASSERT(params.count > 0); + // NOTE(bill): Change last variadic parameter to be a slice + // Custom Calling convention for variadic parameters + Entity *end = variables[variable_count-1]; + end->type = make_type_slice(c->allocator, end->type); + } + + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = variable_count; + + if (is_variadic_) *is_variadic_ = is_variadic; + + return tuple; +} + +Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) { + if (results.count == 0) { + return NULL; + } + Type *tuple = make_type_tuple(c->allocator); + + Entity **variables = gb_alloc_array(c->allocator, Entity *, results.count); + isize variable_index = 0; + for_array(i, results) { + AstNode *item = results.e[i]; + Type *type = check_type(c, item); + Token token = ast_node_token(item); + token.string = str_lit(""); // NOTE(bill): results are not named + // TODO(bill): Should I have named results? + Entity *param = make_entity_param(c->allocator, scope, token, type, false); + // NOTE(bill): No need to record + variables[variable_index++] = param; + } + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = results.count; + + return tuple; +} + + +void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { + ast_node(pt, ProcType, proc_type_node); + + bool variadic = false; + Type *params = check_get_params(c, c->context.scope, pt->params, &variadic); + Type *results = check_get_results(c, c->context.scope, pt->results); + + isize param_count = 0; + isize result_count = 0; + if (params) param_count = params ->Tuple.variable_count; + if (results) result_count = results->Tuple.variable_count; + + + type->Proc.scope = c->context.scope; + type->Proc.params = params; + type->Proc.param_count = param_count; + type->Proc.results = results; + type->Proc.result_count = result_count; + type->Proc.variadic = variadic; + // type->Proc.implicit_context = implicit_context; +} + + +void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, CycleChecker *cycle_checker) { + GB_ASSERT(n->kind == AstNode_Ident); + o->mode = Addressing_Invalid; + o->expr = n; + Entity *e = scope_lookup_entity(c->context.scope, n->Ident.string); + if (e == NULL) { + if (str_eq(n->Ident.string, str_lit("_"))) { + error(n->Ident, "`_` cannot be used as a value type"); + } else { + error(n->Ident, "Undeclared name: %.*s", LIT(n->Ident.string)); + } + o->type = t_invalid; + o->mode = Addressing_Invalid; + if (named_type != NULL) { + set_base_type(named_type, t_invalid); + } + return; + } + add_entity_use(c, n, e); + + // CycleChecker local_cycle_checker = {0}; + // if (cycle_checker == NULL) { + // cycle_checker = &local_cycle_checker; + // } + // defer (cycle_checker_destroy(&local_cycle_checker)); + + check_entity_decl(c, e, NULL, named_type, cycle_checker); + + if (e->type == NULL) { + compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string)); + return; + } + + Type *type = e->type; + + switch (e->kind) { + case Entity_Constant: + if (type == t_invalid) { + o->type = t_invalid; + return; + } + o->value = e->Constant.value; + GB_ASSERT(o->value.kind != ExactValue_Invalid); + o->mode = Addressing_Constant; + break; + + case Entity_Variable: + e->flags |= EntityFlag_Used; + if (type == t_invalid) { + o->type = t_invalid; + return; + } + #if 0 + if (e->Variable.param) { + o->mode = Addressing_Value; + } else { + o->mode = Addressing_Variable; + } + #else + o->mode = Addressing_Variable; + #endif + break; + + case Entity_TypeName: { + o->mode = Addressing_Type; +#if 0 + // TODO(bill): Fix cyclical dependancy checker + if (cycle_checker != NULL) { + for_array(i, cycle_checker->path) { + Entity *prev = cycle_checker->path[i]; + if (prev == e) { + error(e->token, "Illegal declaration cycle for %.*s", LIT(e->token.string)); + for (isize j = i; j < gb_array_count(cycle_checker->path); j++) { + Entity *ref = cycle_checker->path[j]; + error(ref->token, "\t%.*s refers to", LIT(ref->token.string)); + } + error(e->token, "\t%.*s", LIT(e->token.string)); + type = t_invalid; + break; + } + } + } +#endif + } break; + + case Entity_Procedure: + o->mode = Addressing_Value; + break; + + case Entity_Builtin: + o->builtin_id = e->Builtin.id; + o->mode = Addressing_Builtin; + break; + + case Entity_ImportName: + error(ast_node_token(n), "Use of import `%.*s` not in selector", LIT(e->ImportName.name)); + return; + + case Entity_Nil: + o->mode = Addressing_Value; + break; + + case Entity_ImplicitValue: + o->mode = Addressing_Value; + break; + + default: + compiler_error("Compiler error: Unknown EntityKind"); + break; + } + + o->type = type; +} + +i64 check_array_count(Checker *c, AstNode *e) { + if (e == NULL) { + return 0; + } + Operand o = {0}; + check_expr(c, &o, e); + if (o.mode != Addressing_Constant) { + if (o.mode != Addressing_Invalid) { + error(ast_node_token(e), "Array count must be a constant"); + } + return 0; + } + if (is_type_untyped(o.type) || is_type_integer(o.type)) { + if (o.value.kind == ExactValue_Integer) { + i64 count = o.value.value_integer; + if (count >= 0) { + return count; + } + error(ast_node_token(e), "Invalid array count"); + return 0; + } + } + + error(ast_node_token(e), "Array count must be an integer"); + return 0; +} + +Type *check_type_extra(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_checker) { + ExactValue null_value = {ExactValue_Invalid}; + Type *type = NULL; + gbString err_str = NULL; + + switch (e->kind) { + case_ast_node(i, Ident, e); + Operand o = {0}; + check_identifier(c, &o, e, named_type, cycle_checker); + + switch (o.mode) { + case Addressing_Invalid: + break; + case Addressing_Type: { + type = o.type; + goto end; + } break; + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type", err_str); + break; + default: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type when not a type", err_str); + break; + } + case_end; + + case_ast_node(se, SelectorExpr, e); + Operand o = {0}; + check_selector(c, &o, e); + + switch (o.mode) { + case Addressing_Invalid: + break; + case Addressing_Type: + GB_ASSERT(o.type != NULL); + type = o.type; + goto end; + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as a type", err_str); + break; + default: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not a type", err_str); + break; + } + case_end; + + case_ast_node(pe, ParenExpr, e); + type = check_type_extra(c, pe->expr, named_type, cycle_checker); + goto end; + case_end; + + case_ast_node(ue, UnaryExpr, e); + if (ue->op.kind == Token_Pointer) { + type = make_type_pointer(c->allocator, check_type(c, ue->expr)); + goto end; + } else if (ue->op.kind == Token_Maybe) { + type = make_type_maybe(c->allocator, check_type(c, ue->expr)); + goto end; + } + case_end; + + case_ast_node(pt, PointerType, e); + Type *elem = check_type(c, pt->type); + type = make_type_pointer(c->allocator, elem); + goto end; + case_end; + + case_ast_node(mt, MaybeType, e); + Type *elem = check_type(c, mt->type); + type = make_type_maybe(c->allocator, elem); + goto end; + case_end; + + case_ast_node(at, ArrayType, e); + if (at->count != NULL) { + Type *elem = check_type_extra(c, at->elem, NULL, cycle_checker); + type = make_type_array(c->allocator, elem, check_array_count(c, at->count)); + } else { + Type *elem = check_type(c, at->elem); + type = make_type_slice(c->allocator, elem); + } + goto end; + case_end; + + + case_ast_node(vt, VectorType, e); + Type *elem = check_type(c, vt->elem); + Type *be = base_type(elem); + i64 count = check_array_count(c, vt->count); + if (!is_type_boolean(be) && !is_type_numeric(be)) { + err_str = type_to_string(elem); + error(ast_node_token(vt->elem), "Vector element type must be numerical or a boolean. Got `%s`", err_str); + } + type = make_type_vector(c->allocator, elem, count); + goto end; + case_end; + + case_ast_node(st, StructType, e); + type = make_type_struct(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_struct_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(ut, UnionType, e); + type = make_type_union(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_union_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(rut, RawUnionType, e); + type = make_type_raw_union(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_raw_union_type(c, type, e, cycle_checker); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(et, EnumType, e); + type = make_type_enum(c->allocator); + set_base_type(named_type, type); + check_open_scope(c, e); + check_enum_type(c, type, named_type, e); + check_close_scope(c); + type->Record.node = e; + goto end; + case_end; + + case_ast_node(pt, ProcType, e); + type = alloc_type(c->allocator, Type_Proc); + set_base_type(named_type, type); + check_open_scope(c, e); + check_procedure_type(c, type, e); + check_close_scope(c); + goto end; + case_end; + + case_ast_node(ce, CallExpr, e); + Operand o = {0}; + check_expr_or_type(c, &o, e); + if (o.mode == Addressing_Type) { + type = o.type; + goto end; + } + case_end; + } + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not a type", err_str); + + type = t_invalid; +end: + gb_string_free(err_str); + + if (type == NULL) { + type = t_invalid; + } + + set_base_type(named_type, type); + GB_ASSERT(is_type_typed(type)); + + add_type_and_value(&c->info, e, Addressing_Type, type, null_value); + + + return type; +} + + +bool check_unary_op(Checker *c, Operand *o, Token op) { + // TODO(bill): Handle errors correctly + Type *type = base_type(base_vector_type(o->type)); + gbString str = NULL; + switch (op.kind) { + case Token_Add: + case Token_Sub: + if (!is_type_numeric(type)) { + str = expr_to_string(o->expr); + error(op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str); + gb_string_free(str); + } + break; + + case Token_Xor: + if (!is_type_integer(type)) { + error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + } + break; + + case Token_Not: + if (!is_type_boolean(type)) { + str = expr_to_string(o->expr); + error(op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string)); + gb_string_free(str); + } + break; + + default: + error(op, "Unknown operator `%.*s`", LIT(op.string)); + return false; + } + + return true; +} + +bool check_binary_op(Checker *c, Operand *o, Token op) { + // TODO(bill): Handle errors correctly + Type *type = base_type(base_vector_type(o->type)); + switch (op.kind) { + case Token_Sub: + case Token_SubEq: + if (!is_type_numeric(type) && !is_type_pointer(type)) { + error(op, "Operator `%.*s` is only allowed with numeric or pointer expressions", LIT(op.string)); + return false; + } + if (is_type_pointer(type)) { + o->type = t_int; + } + if (base_type(type) == t_rawptr) { + gbString str = type_to_string(type); + error(ast_node_token(o->expr), "Invalid pointer type for pointer arithmetic: `%s`", str); + gb_string_free(str); + return false; + } + break; + + case Token_Add: + case Token_Mul: + case Token_Quo: + case Token_AddEq: + case Token_MulEq: + case Token_QuoEq: + if (!is_type_numeric(type)) { + error(op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string)); + return false; + } + break; + + case Token_And: + case Token_Or: + case Token_AndEq: + case Token_OrEq: + if (!is_type_integer(type) && !is_type_boolean(type)) { + error(op, "Operator `%.*s` is only allowed with integers or booleans", LIT(op.string)); + return false; + } + break; + + case Token_Mod: + case Token_Xor: + case Token_AndNot: + case Token_ModEq: + case Token_XorEq: + case Token_AndNotEq: + if (!is_type_integer(type)) { + error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + return false; + } + break; + + case Token_CmpAnd: + case Token_CmpOr: + + case Token_CmpAndEq: + case Token_CmpOrEq: + if (!is_type_boolean(type)) { + error(op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string)); + return false; + } + break; + + default: + error(op, "Unknown operator `%.*s`", LIT(op.string)); + return false; + } + + return true; + +} +bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) { + if (in_value.kind == ExactValue_Invalid) { + // NOTE(bill): There's already been an error + return true; + } + + if (is_type_boolean(type)) { + return in_value.kind == ExactValue_Bool; + } else if (is_type_string(type)) { + return in_value.kind == ExactValue_String; + } else if (is_type_integer(type)) { + ExactValue v = exact_value_to_integer(in_value); + if (v.kind != ExactValue_Integer) { + return false; + } + if (out_value) *out_value = v; + i64 i = v.value_integer; + u64 u = *cast(u64 *)&i; + i64 s = 8*type_size_of(c->sizes, c->allocator, type); + u64 umax = ~0ull; + if (s < 64) { + umax = (1ull << s) - 1ull; + } else { + // TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats + s = 64; + } + i64 imax = (1ll << (s-1ll)); + + + switch (type->Basic.kind) { + case Basic_i8: + case Basic_i16: + case Basic_i32: + case Basic_i64: + case Basic_i128: + case Basic_int: + return gb_is_between(i, -imax, imax-1); + + case Basic_u8: + case Basic_u16: + case Basic_u32: + case Basic_u64: + case Basic_u128: + case Basic_uint: + return !(u < 0 || u > umax); + + case Basic_UntypedInteger: + return true; + + default: GB_PANIC("Compiler error: Unknown integer type!"); break; + } + } else if (is_type_float(type)) { + ExactValue v = exact_value_to_float(in_value); + if (v.kind != ExactValue_Float) { + return false; + } + + switch (type->Basic.kind) { + // case Basic_f16: + case Basic_f32: + case Basic_f64: + // case Basic_f128: + if (out_value) *out_value = v; + return true; + + case Basic_UntypedFloat: + return true; + } + } else if (is_type_pointer(type)) { + if (in_value.kind == ExactValue_Pointer) { + return true; + } + if (in_value.kind == ExactValue_Integer) { + return true; + } + if (out_value) *out_value = in_value; + } + + + return false; +} + +void check_is_expressible(Checker *c, Operand *o, Type *type) { + GB_ASSERT(type->kind == Type_Basic); + GB_ASSERT(o->mode == Addressing_Constant); + if (!check_value_is_expressible(c, o->value, type, &o->value)) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + if (is_type_numeric(o->type) && is_type_numeric(type)) { + if (!is_type_integer(o->type) && is_type_integer(type)) { + error(ast_node_token(o->expr), "`%s` truncated to `%s`", a, b); + } else { + error(ast_node_token(o->expr), "`%s = %lld` overflows `%s`", a, o->value.value_integer, b); + } + } else { + error(ast_node_token(o->expr), "Cannot convert `%s` to `%s`", a, b); + } + + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + } +} + +bool check_is_expr_vector_index(Checker *c, AstNode *expr) { + // HACK(bill): Handle this correctly. Maybe with a custom AddressingMode + expr = unparen_expr(expr); + if (expr->kind == AstNode_IndexExpr) { + ast_node(ie, IndexExpr, expr); + Type *t = type_deref(type_of_expr(&c->info, ie->expr)); + if (t != NULL) { + return is_type_vector(t); + } + } + return false; +} + +bool check_is_vector_elem(Checker *c, AstNode *expr) { + // HACK(bill): Handle this correctly. Maybe with a custom AddressingMode + expr = unparen_expr(expr); + if (expr->kind == AstNode_SelectorExpr) { + ast_node(se, SelectorExpr, expr); + Type *t = type_deref(type_of_expr(&c->info, se->expr)); + if (t != NULL && is_type_vector(t)) { + return true; + } + } + return false; +} + +void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) { + switch (op.kind) { + case Token_Pointer: { // Pointer address + if (o->mode != Addressing_Variable || + check_is_expr_vector_index(c, o->expr) || + check_is_vector_elem(c, o->expr)) { + ast_node(ue, UnaryExpr, node); + gbString str = expr_to_string(ue->expr); + error(op, "Cannot take the pointer address of `%s`", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + return; + } + o->mode = Addressing_Value; + o->type = make_type_pointer(c->allocator, o->type); + return; + } + + case Token_Maybe: { // Make maybe + Type *t = default_type(o->type); + bool is_value = + o->mode == Addressing_Variable || + o->mode == Addressing_Value || + o->mode == Addressing_Constant; + + if (!is_value || is_type_untyped(t)) { + ast_node(ue, UnaryExpr, node); + gbString str = expr_to_string(ue->expr); + error(op, "Cannot convert `%s` to a maybe", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + return; + } + o->mode = Addressing_Value; + o->type = make_type_maybe(c->allocator, t); + return; + } + } + + if (!check_unary_op(c, o, op)) { + o->mode = Addressing_Invalid; + return; + } + + if (o->mode == Addressing_Constant) { + Type *type = base_type(o->type); + if (type->kind != Type_Basic) { + gbString xt = type_to_string(o->type); + gbString err_str = expr_to_string(node); + error(op, "Invalid type, `%s`, for constant unary expression `%s`", xt, err_str); + gb_string_free(err_str); + gb_string_free(xt); + o->mode = Addressing_Invalid; + return; + } + + + i32 precision = 0; + if (is_type_unsigned(type)) { + precision = cast(i32)(8 * type_size_of(c->sizes, c->allocator, type)); + } + o->value = exact_unary_operator_value(op, o->value, precision); + + if (is_type_typed(type)) { + if (node != NULL) { + o->expr = node; + } + check_is_expressible(c, o, type); + } + return; + } + + o->mode = Addressing_Value; +} + +void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + gbString err_str = NULL; + + if (check_is_assignable_to(c, x, y->type) || + check_is_assignable_to(c, y, x->type)) { + Type *err_type = x->type; + bool defined = false; + switch (op.kind) { + case Token_CmpEq: + case Token_NotEq: + defined = is_type_comparable(x->type); + break; + case Token_Lt: + case Token_Gt: + case Token_LtEq: + case Token_GtEq: { + defined = is_type_ordered(x->type); + } break; + } + + // CLEANUP(bill) NOTE(bill): there is an auto assignment to `any` which needs to be checked + if (is_type_any(x->type) && !is_type_any(y->type)) { + err_type = x->type; + defined = false; + } else if (is_type_any(y->type) && !is_type_any(x->type)) { + err_type = y->type; + defined = false; + } + + if (!defined) { + gbString type_string = type_to_string(err_type); + err_str = gb_string_make(c->tmp_allocator, + gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string)); + gb_string_free(type_string); + } + } else { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + err_str = gb_string_make(c->tmp_allocator, + gb_bprintf("mismatched types `%s` and `%s`", xt, yt)); + gb_string_free(yt); + gb_string_free(xt); + } + + if (err_str != NULL) { + error(ast_node_token(x->expr), "Cannot compare expression, %s", err_str); + x->type = t_untyped_bool; + } else { + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value)); + } else { + x->mode = Addressing_Value; + + update_expr_type(c, x->expr, default_type(x->type), true); + update_expr_type(c, y->expr, default_type(y->type), true); + } + + if (is_type_vector(base_type(y->type))) { + x->type = make_type_vector(c->allocator, t_bool, base_type(y->type)->Vector.count); + } else { + x->type = t_untyped_bool; + } + } + + if (err_str != NULL) { + gb_string_free(err_str); + }; + + gb_temp_arena_memory_end(tmp); +} + +void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + ast_node(be, BinaryExpr, node); + + ExactValue x_val = {0}; + if (x->mode == Addressing_Constant) { + x_val = exact_value_to_integer(x->value); + } + + bool x_is_untyped = is_type_untyped(x->type); + if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) { + gbString err_str = expr_to_string(x->expr); + error(ast_node_token(node), + "Shifted operand `%s` must be an integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_unsigned(y->type)) { + + } else if (is_type_untyped(y->type)) { + convert_to_typed(c, y, t_untyped_integer, 0); + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + return; + } + } else { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount `%s` must be an unsigned integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + + if (x->mode == Addressing_Constant) { + if (y->mode == Addressing_Constant) { + ExactValue y_val = exact_value_to_integer(y->value); + if (y_val.kind != ExactValue_Integer) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount `%s` must be an unsigned integer", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + u64 amount = cast(u64)y_val.value_integer; + if (amount > 1074) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount too large: `%s`", err_str); + gb_string_free(err_str); + x->mode = Addressing_Invalid; + return; + } + + if (!is_type_integer(x->type)) { + // NOTE(bill): It could be an untyped float but still representable + // as an integer + x->type = t_untyped_integer; + } + + x->value = exact_value_shift(be->op, x_val, make_exact_value_integer(amount)); + + if (is_type_typed(x->type)) { + check_is_expressible(c, x, base_type(x->type)); + } + return; + } + + if (x_is_untyped) { + ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr)); + if (info != NULL) { + info->is_lhs = true; + } + x->mode = Addressing_Value; + return; + } + } + + if (y->mode == Addressing_Constant && y->value.value_integer < 0) { + gbString err_str = expr_to_string(y->expr); + error(ast_node_token(node), + "Shift amount cannot be negative: `%s`", err_str); + gb_string_free(err_str); + } + + x->mode = Addressing_Value; +} + +bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { + if (check_is_assignable_to(c, operand, y)) { + return true; + } + + Type *x = operand->type; + Type *xb = base_type(x); + Type *yb = base_type(y); + if (are_types_identical(xb, yb)) { + return true; + } + xb = get_enum_base_type(x); + yb = get_enum_base_type(y); + + + // Cast between booleans and integers + if (is_type_boolean(xb) || is_type_integer(xb)) { + if (is_type_boolean(yb) || is_type_integer(yb)) { + return true; + } + } + + // Cast between numbers + if (is_type_integer(xb) || is_type_float(xb)) { + if (is_type_integer(yb) || is_type_float(yb)) { + return true; + } + } + + // Cast between pointers + if (is_type_pointer(xb) && is_type_pointer(yb)) { + return true; + } + + // (u)int <-> pointer + if (is_type_int_or_uint(xb) && is_type_rawptr(yb)) { + return true; + } + if (is_type_rawptr(xb) && is_type_int_or_uint(yb)) { + return true; + } + + // []byte/[]u8 <-> string + if (is_type_u8_slice(xb) && is_type_string(yb)) { + return true; + } + if (is_type_string(xb) && is_type_u8_slice(yb)) { + if (is_type_typed(xb)) { + return true; + } + } + + // proc <-> proc + if (is_type_proc(xb) && is_type_proc(yb)) { + return true; + } + + // proc -> rawptr + if (is_type_proc(xb) && is_type_rawptr(yb)) { + return true; + } + + return false; +} + +String check_down_cast_name(Type *dst_, Type *src_) { + String result = {0}; + Type *dst = type_deref(dst_); + Type *src = type_deref(src_); + Type *dst_s = base_type(dst); + GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s)); + for (isize i = 0; i < dst_s->Record.field_count; i++) { + Entity *f = dst_s->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + if (f->flags & EntityFlag_Anonymous) { + if (are_types_identical(f->type, src_)) { + return f->token.string; + } + if (are_types_identical(type_deref(f->type), src_)) { + return f->token.string; + } + + if (!is_type_pointer(f->type)) { + result = check_down_cast_name(f->type, src_); + if (result.len > 0) { + return result; + } + } + } + } + + return result; +} + +Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offset, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + ast_node(be, BinaryExpr, node); + GB_ASSERT(is_type_pointer(ptr->type)); + GB_ASSERT(is_type_integer(offset->type)); + GB_ASSERT(op == Token_Add || op == Token_Sub); + + Operand operand = {0}; + operand.mode = Addressing_Value; + operand.type = ptr->type; + operand.expr = node; + + if (base_type(ptr->type) == t_rawptr) { + gbString str = type_to_string(ptr->type); + error(ast_node_token(node), "Invalid pointer type for pointer arithmetic: `%s`", str); + gb_string_free(str); + operand.mode = Addressing_Invalid; + return operand; + } + + + if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { + i64 elem_size = type_size_of(c->sizes, c->allocator, ptr->type); + i64 ptr_val = ptr->value.value_pointer; + i64 offset_val = exact_value_to_integer(offset->value).value_integer; + i64 new_ptr_val = ptr_val; + if (op == Token_Add) { + new_ptr_val += elem_size*offset_val; + } else { + new_ptr_val -= elem_size*offset_val; + } + operand.mode = Addressing_Constant; + operand.value = make_exact_value_pointer(new_ptr_val); + } + + return operand; +} + +void check_binary_expr(Checker *c, Operand *x, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + Operand y_ = {0}, *y = &y_; + + ast_node(be, BinaryExpr, node); + + if (be->op.kind == Token_as) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + bool is_const_expr = x->mode == Addressing_Constant; + bool can_convert = false; + + Type *bt = base_type(type); + if (is_const_expr && is_type_constant_type(bt)) { + if (bt->kind == Type_Basic) { + if (check_value_is_expressible(c, x->value, bt, &x->value)) { + can_convert = true; + } + } + } else if (check_is_castable_to(c, x, type)) { + if (x->mode != Addressing_Constant) { + x->mode = Addressing_Value; + } + can_convert = true; + } + + if (!can_convert) { + gbString expr_str = expr_to_string(x->expr); + gbString to_type = type_to_string(type); + gbString from_type = type_to_string(x->type); + error(ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); + gb_string_free(from_type); + gb_string_free(to_type); + gb_string_free(expr_str); + + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + Type *final_type = type; + if (is_const_expr && !is_type_constant_type(type)) { + final_type = default_type(x->type); + } + update_expr_type(c, x->expr, final_type, true); + } + + x->type = type; + return; + } else if (be->op.kind == Token_transmute) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(x->expr); + error(ast_node_token(x->expr), "Cannot transmute constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(x->expr); + error(ast_node_token(x->expr), "Cannot transmute untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + i64 srcz = type_size_of(c->sizes, c->allocator, x->type); + i64 dstz = type_size_of(c->sizes, c->allocator, type); + if (srcz != dstz) { + gbString expr_str = expr_to_string(x->expr); + gbString type_str = type_to_string(type); + error(ast_node_token(x->expr), "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, srcz, dstz); + gb_string_free(type_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + x->type = type; + + return; + } else if (be->op.kind == Token_down_cast) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `down_cast` a constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `down_cast` an untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(is_type_pointer(x->type) && is_type_pointer(type))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointers: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + Type *src = type_deref(x->type); + Type *dst = type_deref(type); + Type *bsrc = base_type(src); + Type *bdst = base_type(dst); + + if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(is_type_struct(bdst) || is_type_raw_union(bdst))) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + String param_name = check_down_cast_name(dst, src); + if (param_name.len == 0) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Illegal `down_cast`: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + x->mode = Addressing_Value; + x->type = type; + return; + } else if (be->op.kind == Token_union_cast) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `union_cast` a constant expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(node); + error(ast_node_token(node), "Cannot `union_cast` an untyped expression: `%s`", expr_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + bool src_is_ptr = is_type_pointer(x->type); + bool dst_is_ptr = is_type_pointer(type); + Type *src = type_deref(x->type); + Type *dst = type_deref(type); + Type *bsrc = base_type(src); + Type *bdst = base_type(dst); + + if (src_is_ptr != dst_is_ptr) { + gbString src_type_str = type_to_string(x->type); + gbString dst_type_str = type_to_string(type); + error(ast_node_token(node), "Invalid `union_cast` types: `%s` and `%s`", src_type_str, dst_type_str); + gb_string_free(dst_type_str); + gb_string_free(src_type_str); + x->mode = Addressing_Invalid; + return; + } + + if (!is_type_union(src)) { + error(ast_node_token(node), "`union_cast` can only operate on unions"); + x->mode = Addressing_Invalid; + return; + } + + bool ok = false; + for (isize i = 1; i < bsrc->Record.field_count; i++) { + Entity *f = bsrc->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + ok = true; + break; + } + } + + if (!ok) { + gbString expr_str = expr_to_string(node); + gbString dst_type_str = type_to_string(type); + error(ast_node_token(node), "Cannot `union_cast` `%s` to `%s`", expr_str, dst_type_str); + gb_string_free(dst_type_str); + gb_string_free(expr_str); + x->mode = Addressing_Invalid; + return; + } + + Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); + Token tok = make_token_ident(str_lit("")); + variables[0] = make_entity_param(c->allocator, NULL, tok, type, false); + variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false); + + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = 2; + + x->type = tuple; + x->mode = Addressing_Value; + return; + } + + check_expr(c, x, be->left); + check_expr(c, y, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + x->expr = y->expr; + return; + } + + Token op = be->op; + + if (token_is_shift(op)) { + check_shift(c, x, y, node); + return; + } + + if (op.kind == Token_Add || op.kind == Token_Sub) { + if (is_type_pointer(x->type) && is_type_integer(y->type)) { + *x = check_ptr_addition(c, op.kind, x, y, node); + return; + } else if (is_type_integer(x->type) && is_type_pointer(y->type)) { + if (op.kind == Token_Sub) { + gbString lhs = expr_to_string(x->expr); + gbString rhs = expr_to_string(y->expr); + error(ast_node_token(node), "Invalid pointer arithmetic, did you mean `%s %.*s %s`?", rhs, LIT(op.string), lhs); + gb_string_free(rhs); + gb_string_free(lhs); + x->mode = Addressing_Invalid; + return; + } + *x = check_ptr_addition(c, op.kind, y, x, node); + return; + } + } + + + convert_to_typed(c, x, y->type, 0); + if (x->mode == Addressing_Invalid) { + return; + } + convert_to_typed(c, y, x->type, 0); + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + return; + } + + if (token_is_comparison(op)) { + check_comparison(c, x, y, op); + return; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + x->mode = Addressing_Invalid; + return; + } + + if (!check_binary_op(c, x, op)) { + x->mode = Addressing_Invalid; + return; + } + + switch (op.kind) { + case Token_Quo: + case Token_Mod: + case Token_QuoEq: + case Token_ModEq: + if ((x->mode == Addressing_Constant || is_type_integer(x->type)) && + y->mode == Addressing_Constant) { + bool fail = false; + switch (y->value.kind) { + case ExactValue_Integer: + if (y->value.value_integer == 0) { + fail = true; + } + break; + case ExactValue_Float: + if (y->value.value_float == 0.0) { + fail = true; + } + break; + } + + if (fail) { + error(ast_node_token(y->expr), "Division by zero not allowed"); + x->mode = Addressing_Invalid; + return; + } + } + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + Type *type = base_type(x->type); + if (is_type_pointer(type)) { + GB_ASSERT(op.kind == Token_Sub); + i64 bytes = a.value_pointer - b.value_pointer; + i64 diff = bytes/type_size_of(c->sizes, c->allocator, type); + x->value = make_exact_value_pointer(diff); + return; + } + + if (type->kind != Type_Basic) { + gbString xt = type_to_string(x->type); + gbString err_str = expr_to_string(node); + error(op, "Invalid type, `%s`, for constant binary expression `%s`", xt, err_str); + gb_string_free(err_str); + gb_string_free(xt); + x->mode = Addressing_Invalid; + return; + } + + if (op.kind == Token_Quo && is_type_integer(type)) { + op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers + } + x->value = exact_binary_operator_value(op, a, b); + if (is_type_typed(type)) { + if (node != NULL) { + x->expr = node; + } + check_is_expressible(c, x, type); + } + return; + } + + x->mode = Addressing_Value; +} + + +void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) { + HashKey key = hash_pointer(e); + ExprInfo *found = map_expr_info_get(&c->info.untyped, key); + if (found == NULL) { + return; + } + + switch (e->kind) { + case_ast_node(ue, UnaryExpr, e); + if (found->value.kind != ExactValue_Invalid) { + break; + } + update_expr_type(c, ue->expr, type, final); + case_end; + + case_ast_node(be, BinaryExpr, e); + if (found->value.kind != ExactValue_Invalid) { + break; + } + if (!token_is_comparison(be->op)) { + if (token_is_shift(be->op)) { + update_expr_type(c, be->left, type, final); + } else { + update_expr_type(c, be->left, type, final); + update_expr_type(c, be->right, type, final); + } + } + case_end; + } + + if (!final && is_type_untyped(type)) { + found->type = base_type(type); + map_expr_info_set(&c->info.untyped, key, *found); + } else { + ExprInfo old = *found; + map_expr_info_remove(&c->info.untyped, key); + + if (old.is_lhs && !is_type_integer(type)) { + gbString expr_str = expr_to_string(e); + gbString type_str = type_to_string(type); + error(ast_node_token(e), "Shifted operand %s must be an integer, got %s", expr_str, type_str); + gb_string_free(type_str); + gb_string_free(expr_str); + return; + } + + add_type_and_value(&c->info, e, found->mode, type, found->value); + } +} + +void update_expr_value(Checker *c, AstNode *e, ExactValue value) { + ExprInfo *found = map_expr_info_get(&c->info.untyped, hash_pointer(e)); + if (found) { + found->value = value; + } +} + +void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) { + gbString expr_str = expr_to_string(operand->expr); + gbString type_str = type_to_string(target_type); + char *extra_text = ""; + + if (operand->mode == Addressing_Constant) { + if (operand->value.value_integer == 0) { + if (str_ne(make_string_c(expr_str), str_lit("nil"))) { // HACK NOTE(bill): Just in case + // NOTE(bill): Doesn't matter what the type is as it's still zero in the union + extra_text = " - Did you want `nil`?"; + } + } + } + error(ast_node_token(operand->expr), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text); + + gb_string_free(type_str); + gb_string_free(expr_str); + operand->mode = Addressing_Invalid; +} + +// NOTE(bill): Set initial level to 0 +void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level) { + GB_ASSERT_NOT_NULL(target_type); + if (operand->mode == Addressing_Invalid || + is_type_typed(operand->type) || + target_type == t_invalid) { + return; + } + + if (is_type_untyped(target_type)) { + Type *x = operand->type; + Type *y = target_type; + if (is_type_numeric(x) && is_type_numeric(y)) { + if (x < y) { + operand->type = target_type; + update_expr_type(c, operand->expr, target_type, false); + } + } else if (x != y) { + convert_untyped_error(c, operand, target_type); + } + return; + } + + Type *t = get_enum_base_type(base_type(target_type)); + switch (t->kind) { + case Type_Basic: + if (operand->mode == Addressing_Constant) { + check_is_expressible(c, operand, t); + if (operand->mode == Addressing_Invalid) { + return; + } + update_expr_value(c, operand->expr, operand->value); + } else { + switch (operand->type->Basic.kind) { + case Basic_UntypedBool: + if (!is_type_boolean(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + case Basic_UntypedInteger: + case Basic_UntypedFloat: + case Basic_UntypedRune: + if (!is_type_numeric(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + + case Basic_UntypedNil: + if (!type_has_nil(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + } + } + break; + + case Type_Maybe: + if (is_type_untyped_nil(operand->type)) { + // Okay + } else if (level == 0) { + convert_to_typed(c, operand, t->Maybe.elem, level+1); + return; + } + + default: + if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { + convert_untyped_error(c, operand, target_type); + return; + } + break; + } + + + + operand->type = target_type; +} + +bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) { + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, index_value); + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; + return false; + } + + convert_to_typed(c, &operand, t_int, 0); + if (operand.mode == Addressing_Invalid) { + if (value) *value = 0; + return false; + } + + if (!is_type_integer(get_enum_base_type(operand.type))) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` must be an integer", expr_str); + gb_string_free(expr_str); + if (value) *value = 0; + return false; + } + + if (operand.mode == Addressing_Constant && + (c->context.stmt_state_flags & StmtStateFlag_bounds_check) != 0) { + i64 i = exact_value_to_integer(operand.value).value_integer; + if (i < 0) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` cannot be a negative value", expr_str); + gb_string_free(expr_str); + if (value) *value = 0; + return false; + } + + if (max_count >= 0) { // NOTE(bill): Do array bound checking + if (value) *value = i; + if (i >= max_count) { + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(operand.expr), + "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count); + gb_string_free(expr_str); + return false; + } + + return true; + } + } + + // NOTE(bill): It's alright :D + if (value) *value = -1; + return true; +} + +Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { + ast_node(se, SelectorExpr, node); + + bool check_op_expr = true; + Entity *expr_entity = NULL; + Entity *entity = NULL; + Selection sel = {0}; // NOTE(bill): Not used if it's an import name + + AstNode *op_expr = se->expr; + AstNode *selector = unparen_expr(se->selector); + if (selector == NULL) { + goto error; + } + + GB_ASSERT(selector->kind == AstNode_Ident); + + + if (op_expr->kind == AstNode_Ident) { + String name = op_expr->Ident.string; + Entity *e = scope_lookup_entity(c->context.scope, name); + add_entity_use(c, op_expr, e); + expr_entity = e; + if (e != NULL && e->kind == Entity_ImportName) { + String sel_name = selector->Ident.string; + check_op_expr = false; + entity = scope_lookup_entity(e->ImportName.scope, sel_name); + if (entity == NULL) { + error(ast_node_token(op_expr), "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name)); + goto error; + } + if (entity->type == NULL) { // Not setup yet + check_entity_decl(c, entity, NULL, NULL, NULL); + } + GB_ASSERT(entity->type != NULL); + // bool is_not_exported = !is_entity_exported(entity); + + b32 is_not_exported = true; + + Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name)); + if (!found) { + is_not_exported = false; + } else { + Entity *f = *found; + if (f->kind == Entity_ImportName) { + is_not_exported = true; + } + } + + // // TODO(bill): Fix this for `#import "file.odin" as .` + // if (true || is_not_exported) { + // Entity **found = + // if (!found && e->ImportName.scope != entity->scope) { + // is_not_exported = false; + // } + // gb_printf("%.*s\n", LIT(entity->token.string)); + // } + + if (is_not_exported) { + gbString sel_str = expr_to_string(selector); + error(ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name)); + gb_string_free(sel_str); + // NOTE(bill): Not really an error so don't goto error + } + + add_entity_use(c, selector, entity); + } + } + if (check_op_expr) { + check_expr_base(c, operand, op_expr, NULL); + if (operand->mode == Addressing_Invalid) { + goto error; + } + } + + + if (entity == NULL) { + sel = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type); + entity = sel.entity; + } + if (entity == NULL) { + gbString op_str = expr_to_string(op_expr); + gbString type_str = type_to_string(operand->type); + gbString sel_str = expr_to_string(selector); + error(ast_node_token(op_expr), "`%s` (`%s`) has no field `%s`", op_str, type_str, sel_str); + gb_string_free(sel_str); + gb_string_free(type_str); + gb_string_free(op_str); + goto error; + } + + if (expr_entity != NULL && expr_entity->kind == Entity_Constant && entity->kind != Entity_Constant) { + gbString op_str = expr_to_string(op_expr); + gbString type_str = type_to_string(operand->type); + gbString sel_str = expr_to_string(selector); + error(ast_node_token(op_expr), "Cannot access non-constant field `%s` from `%s`", sel_str, op_str); + gb_string_free(sel_str); + gb_string_free(type_str); + gb_string_free(op_str); + goto error; + } + + + add_entity_use(c, selector, entity); + + switch (entity->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->value = entity->Constant.value; + break; + case Entity_Variable: + // TODO(bill): This is the rule I need? + if (sel.indirect || operand->mode != Addressing_Value) { + operand->mode = Addressing_Variable; + } + break; + case Entity_TypeName: + operand->mode = Addressing_Type; + break; + case Entity_Procedure: + operand->mode = Addressing_Value; + break; + case Entity_Builtin: + operand->mode = Addressing_Builtin; + operand->builtin_id = entity->Builtin.id; + break; + + // NOTE(bill): These cases should never be hit but are here for sanity reasons + case Entity_Nil: + operand->mode = Addressing_Value; + break; + case Entity_ImplicitValue: + operand->mode = Addressing_Value; + break; + } + + operand->type = entity->type; + operand->expr = node; + + return entity; + +error: + operand->mode = Addressing_Invalid; + operand->expr = node; + return NULL; +} + +bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { + GB_ASSERT(call->kind == AstNode_CallExpr); + ast_node(ce, CallExpr, call); + BuiltinProc *bp = &builtin_procs[id]; + { + char *err = NULL; + if (ce->args.count < bp->arg_count) { + err = "Too few"; + } else if (ce->args.count > bp->arg_count && !bp->variadic) { + err = "Too many"; + } + + if (err) { + ast_node(proc, Ident, ce->proc); + error(ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", + err, LIT(proc->string), + bp->arg_count, ce->args.count); + return false; + } + } + + switch (id) { + case BuiltinProc_new: + case BuiltinProc_new_slice: + case BuiltinProc_size_of: + case BuiltinProc_align_of: + case BuiltinProc_offset_of: + case BuiltinProc_type_info: + // NOTE(bill): The first arg may be a Type, this will be checked case by case + break; + default: + check_multi_expr(c, operand, ce->args.e[0]); + } + + switch (id) { + case BuiltinProc_new: { + // new :: proc(Type) -> ^Type + Operand op = {0}; + check_expr_or_type(c, &op, ce->args.e[0]); + Type *type = op.type; + if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `new`"); + return false; + } + operand->mode = Addressing_Value; + operand->type = make_type_pointer(c->allocator, type); + } break; + case BuiltinProc_new_slice: { + // new_slice :: proc(Type, len: int[, cap: int]) -> []Type + Operand op = {0}; + check_expr_or_type(c, &op, ce->args.e[0]); + Type *type = op.type; + if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `new_slice`"); + return false; + } + + AstNode *len = ce->args.e[1]; + AstNode *cap = NULL; + if (ce->args.count > 2) { + cap = ce->args.e[2]; + } + + check_expr(c, &op, len); + if (op.mode == Addressing_Invalid) { + return false; + } + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Length for `new_slice` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (cap != NULL) { + check_expr(c, &op, cap); + if (op.mode == Addressing_Invalid) { + return false; + } + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Capacity for `new_slice` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + if (ce->args.count > 3) { + error(ast_node_token(call), + "Too many arguments to `new_slice`, expected either 2 or 3"); + return false; + } + } + + operand->mode = Addressing_Value; + operand->type = make_type_slice(c->allocator, type); + } break; + + case BuiltinProc_size_of: { + // size_of :: proc(Type) -> untyped int + Type *type = check_type(c, ce->args.e[0]); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `size_of`"); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, type)); + operand->type = t_untyped_integer; + + } break; + + case BuiltinProc_size_of_val: + // size_of_val :: proc(val: Type) -> untyped int + check_assignment(c, operand, NULL, str_lit("argument of `size_of_val`")); + if (operand->mode == Addressing_Invalid) { + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, operand->type)); + operand->type = t_untyped_integer; + break; + + case BuiltinProc_align_of: { + // align_of :: proc(Type) -> untyped int + Type *type = check_type(c, ce->args.e[0]); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `align_of`"); + return false; + } + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, type)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_align_of_val: + // align_of_val :: proc(val: Type) -> untyped int + check_assignment(c, operand, NULL, str_lit("argument of `align_of_val`")); + if (operand->mode == Addressing_Invalid) { + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, operand->type)); + operand->type = t_untyped_integer; + break; + + case BuiltinProc_offset_of: { + // offset_of :: proc(Type, field) -> untyped int + Operand op = {0}; + Type *bt = check_type(c, ce->args.e[0]); + Type *type = base_type(bt); + if (type == NULL || type == t_invalid) { + error(ast_node_token(ce->args.e[0]), "Expected a type for `offset_of`"); + return false; + } + + AstNode *field_arg = unparen_expr(ce->args.e[1]); + if (field_arg == NULL || + field_arg->kind != AstNode_Ident) { + error(ast_node_token(field_arg), "Expected an identifier for field argument"); + return false; + } + if (is_type_array(type) || is_type_vector(type)) { + error(ast_node_token(field_arg), "Invalid type for `offset_of`"); + return false; + } + + + ast_node(arg, Ident, field_arg); + Selection sel = lookup_field(c->allocator, type, arg->string, operand->mode == Addressing_Type); + if (sel.entity == NULL) { + gbString type_str = type_to_string(bt); + error(ast_node_token(ce->args.e[0]), + "`%s` has no field named `%.*s`", type_str, LIT(arg->string)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ast_node_token(ce->args.e[0]), + "Field `%.*s` is embedded via a pointer in `%s`", LIT(arg->string), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_offset_of_val: { + // offset_of_val :: proc(val: expression) -> untyped int + AstNode *arg = unparen_expr(ce->args.e[0]); + if (arg->kind != AstNode_SelectorExpr) { + gbString str = expr_to_string(arg); + error(ast_node_token(arg), "`%s` is not a selector expression", str); + return false; + } + ast_node(s, SelectorExpr, arg); + + check_expr(c, operand, s->expr); + if (operand->mode == Addressing_Invalid) { + return false; + } + + Type *type = operand->type; + if (base_type(type)->kind == Type_Pointer) { + Type *p = base_type(type); + if (is_type_struct(p)) { + type = p->Pointer.elem; + } + } + if (is_type_array(type) || is_type_vector(type)) { + error(ast_node_token(arg), "Invalid type for `offset_of_val`"); + return false; + } + + ast_node(i, Ident, s->selector); + Selection sel = lookup_field(c->allocator, type, i->string, operand->mode == Addressing_Type); + if (sel.entity == NULL) { + gbString type_str = type_to_string(type); + error(ast_node_token(arg), + "`%s` has no field named `%.*s`", type_str, LIT(i->string)); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(type); + error(ast_node_token(ce->args.e[0]), + "Field `%.*s` is embedded via a pointer in `%s`", LIT(i->string), type_str); + gb_string_free(type_str); + return false; + } + + + operand->mode = Addressing_Constant; + // IMPORTANT TODO(bill): Fix for anonymous fields + operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel)); + operand->type = t_untyped_integer; + } break; + + case BuiltinProc_type_of_val: + // type_of_val :: proc(val: Type) -> type(Type) + check_assignment(c, operand, NULL, str_lit("argument of `type_of_val`")); + if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) { + return false; + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_info: { + // type_info :: proc(Type) -> ^Type_Info + AstNode *expr = ce->args.e[0]; + Type *type = check_type(c, expr); + if (type == NULL || type == t_invalid) { + error(ast_node_token(expr), "Invalid argument to `type_info`"); + return false; + } + + add_type_info_type(c, type); + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + } break; + + case BuiltinProc_type_info_of_val: { + // type_info_of_val :: proc(val: Type) -> ^Type_Info + AstNode *expr = ce->args.e[0]; + + check_assignment(c, operand, NULL, str_lit("argument of `type_info_of_val`")); + if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) + return false; + add_type_info_type(c, operand->type); + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + } break; + + + + case BuiltinProc_compile_assert: + // compile_assert :: proc(cond: bool) + + if (!is_type_boolean(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a constant boolean", str); + gb_string_free(str); + return false; + } + if (!operand->value.value_bool) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "Compile time assertion: `%s`", str); + gb_string_free(str); + } + break; + + case BuiltinProc_assert: + // assert :: proc(cond: bool) + + if (!is_type_boolean(operand->type)) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a boolean", str); + gb_string_free(str); + return false; + } + + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_panic: + // panic :: proc(msg: string) + + if (!is_type_string(operand->type)) { + gbString str = expr_to_string(ce->args.e[0]); + error(ast_node_token(call), "`%s` is not a string", str); + gb_string_free(str); + return false; + } + + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_copy: { + // copy :: proc(x, y: []Type) -> int + Type *dest_type = NULL, *src_type = NULL; + + Type *d = base_type(operand->type); + if (d->kind == Type_Slice) { + dest_type = d->Slice.elem; + } + Operand op = {0}; + check_expr(c, &op, ce->args.e[1]); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *s = base_type(op.type); + if (s->kind == Type_Slice) { + src_type = s->Slice.elem; + } + + if (dest_type == NULL || src_type == NULL) { + error(ast_node_token(call), "`copy` only expects slices as arguments"); + return false; + } + + if (!are_types_identical(dest_type, src_type)) { + gbString d_arg = expr_to_string(ce->args.e[0]); + gbString s_arg = expr_to_string(ce->args.e[1]); + gbString d_str = type_to_string(dest_type); + gbString s_str = type_to_string(src_type); + error(ast_node_token(call), + "Arguments to `copy`, %s, %s, have different elem types: %s vs %s", + d_arg, s_arg, d_str, s_str); + gb_string_free(s_str); + gb_string_free(d_str); + gb_string_free(s_arg); + gb_string_free(d_arg); + return false; + } + + operand->type = t_int; // Returns number of elems copied + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_append: { + // append :: proc(x : ^[]Type, y : Type) -> bool + Type *x_type = NULL, *y_type = NULL; + x_type = base_type(operand->type); + + Operand op = {0}; + check_expr(c, &op, ce->args.e[1]); + if (op.mode == Addressing_Invalid) { + return false; + } + y_type = base_type(op.type); + + if (!(is_type_pointer(x_type) && is_type_slice(x_type->Pointer.elem))) { + error(ast_node_token(call), "First argument to `append` must be a pointer to a slice"); + return false; + } + + Type *elem_type = x_type->Pointer.elem->Slice.elem; + if (!check_is_assignable_to(c, &op, elem_type)) { + gbString d_arg = expr_to_string(ce->args.e[0]); + gbString s_arg = expr_to_string(ce->args.e[1]); + gbString d_str = type_to_string(elem_type); + gbString s_str = type_to_string(y_type); + error(ast_node_token(call), + "Arguments to `append`, %s, %s, have different element types: %s vs %s", + d_arg, s_arg, d_str, s_str); + gb_string_free(s_str); + gb_string_free(d_str); + gb_string_free(s_arg); + gb_string_free(d_arg); + return false; + } + + operand->type = t_bool; // Returns if it was successful + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_swizzle: { + // swizzle :: proc(v: {N}T, T...) -> {M}T + Type *vector_type = base_type(operand->type); + if (!is_type_vector(vector_type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "You can only `swizzle` a vector, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + isize max_count = vector_type->Vector.count; + isize arg_count = 0; + for_array(i, ce->args) { + if (i == 0) { + continue; + } + AstNode *arg = ce->args.e[i]; + Operand op = {0}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) { + error(ast_node_token(op.expr), "Indices to `swizzle` must be constant integers"); + return false; + } + + if (op.value.value_integer < 0) { + error(ast_node_token(op.expr), "Negative `swizzle` index"); + return false; + } + + if (max_count <= op.value.value_integer) { + error(ast_node_token(op.expr), "`swizzle` index exceeds vector length"); + return false; + } + + arg_count++; + } + + if (arg_count > max_count) { + error(ast_node_token(call), "Too many `swizzle` indices, %td > %td", arg_count, max_count); + return false; + } + + Type *elem_type = vector_type->Vector.elem; + operand->type = make_type_vector(c->allocator, elem_type, arg_count); + operand->mode = Addressing_Value; + } break; + +#if 0 + case BuiltinProc_ptr_offset: { + // ptr_offset :: proc(ptr: ^T, offset: int) -> ^T + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_offset`, got `%s`", + type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + AstNode *offset = ce->args.e[1]; + Operand op = {0}; + check_expr(c, &op, offset); + if (op.mode == Addressing_Invalid) + return false; + Type *offset_type = base_type(op.type); + if (!is_type_integer(offset_type)) { + error(ast_node_token(op.expr), "Pointer offsets for `ptr_offset` must be an integer"); + return false; + } + + if (operand->mode == Addressing_Constant && + op.mode == Addressing_Constant) { + i64 ptr = operand->value.value_pointer; + i64 elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem); + ptr += elem_size * op.value.value_integer; + operand->value.value_pointer = ptr; + } else { + operand->mode = Addressing_Value; + } + + } break; + + case BuiltinProc_ptr_sub: { + // ptr_sub :: proc(a, b: ^T) -> int + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_add`, got `%s`", + type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + AstNode *offset = ce->args[1]; + Operand op = {0}; + check_expr(c, &op, offset); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_pointer(op.type)) { + gbString type_str = type_to_string(operand->type); + defer (gb_string_free(type_str)); + error(ast_node_token(call), + "Expected a pointer to `ptr_add`, got `%s`", + type_str); + return false; + } + + if (base_type(op.type) == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + if (!are_types_identical(operand->type, op.type)) { + gbString a = type_to_string(operand->type); + gbString b = type_to_string(op.type); + defer (gb_string_free(a)); + defer (gb_string_free(b)); + error(ast_node_token(op.expr), + "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b); + return false; + } + + operand->type = t_int; + + if (operand->mode == Addressing_Constant && + op.mode == Addressing_Constant) { + u8 *ptr_a = cast(u8 *)operand->value.value_pointer; + u8 *ptr_b = cast(u8 *)op.value.value_pointer; + isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem); + operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size); + } else { + operand->mode = Addressing_Value; + } + } break; +#endif + + case BuiltinProc_slice_ptr: { + // slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T + // ^T cannot be rawptr + Type *ptr_type = base_type(operand->type); + if (!is_type_pointer(ptr_type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a pointer to `slice_ptr`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (ptr_type == t_rawptr) { + error(ast_node_token(call), + "`rawptr` cannot have pointer arithmetic"); + return false; + } + + AstNode *len = ce->args.e[1]; + AstNode *cap = NULL; + if (ce->args.count > 2) { + cap = ce->args.e[2]; + } + + Operand op = {0}; + check_expr(c, &op, len); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Length for `slice_ptr` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (cap != NULL) { + check_expr(c, &op, cap); + if (op.mode == Addressing_Invalid) + return false; + if (!is_type_integer(op.type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Capacity for `slice_ptr` must be an integer, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + if (ce->args.count > 3) { + error(ast_node_token(call), + "Too many arguments to `slice_ptr`, expected either 2 or 3"); + return false; + } + } + + operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem); + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_min: { + // min :: proc(a, b: comparable) -> comparable + Type *type = base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + AstNode *other_arg = ce->args.e[1]; + Operand a = *operand; + Operand b = {0}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_comparable(b.type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(b.type); + error(ast_node_token(call), + "Expected a comparable numeric type to `min`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token lt = {Token_Lt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(lt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + convert_to_typed(c, &a, b.type, 0); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type, 0); + if (b.mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(operand->type, b.type)) { + gbString type_a = type_to_string(a.type); + gbString type_b = type_to_string(b.type); + error(ast_node_token(call), + "Mismatched types to `min`, `%s` vs `%s`", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + } break; + + case BuiltinProc_max: { + // min :: proc(a, b: comparable) -> comparable + Type *type = base_type(operand->type); + if (!is_type_comparable(type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + AstNode *other_arg = ce->args.e[1]; + Operand a = *operand; + Operand b = {0}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_comparable(b.type) || !is_type_numeric(type)) { + gbString type_str = type_to_string(b.type); + error(ast_node_token(call), + "Expected a comparable numeric type to `max`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (a.mode == Addressing_Constant && + b.mode == Addressing_Constant) { + ExactValue x = a.value; + ExactValue y = b.value; + Token gt = {Token_Gt}; + + operand->mode = Addressing_Constant; + if (compare_exact_values(gt, x, y)) { + operand->value = x; + operand->type = a.type; + } else { + operand->value = y; + operand->type = b.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + convert_to_typed(c, &a, b.type, 0); + if (a.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &b, a.type, 0); + if (b.mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(operand->type, b.type)) { + gbString type_a = type_to_string(a.type); + gbString type_b = type_to_string(b.type); + error(ast_node_token(call), + "Mismatched types to `max`, `%s` vs `%s`", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + } break; + + case BuiltinProc_abs: { + // abs :: proc(n: numeric) -> numeric + Type *type = base_type(operand->type); + if (!is_type_numeric(type)) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a numeric type to `abs`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + switch (operand->value.kind) { + case ExactValue_Integer: + operand->value.value_integer = gb_abs(operand->value.value_integer); + break; + case ExactValue_Float: + operand->value.value_float = gb_abs(operand->value.value_float); + break; + default: + GB_PANIC("Invalid numeric constant"); + break; + } + } else { + operand->mode = Addressing_Value; + } + + operand->type = type; + } break; + + case BuiltinProc_enum_to_string: { + Type *type = base_type(operand->type); + if (!is_type_enum(type)) { + gbString type_str = type_to_string(operand->type); + gb_string_free(type_str); + error(ast_node_token(call), + "Expected an enum to `enum_to_string`, got `%s`", + type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + ExactValue value = make_exact_value_string(str_lit("")); + if (operand->value.kind == ExactValue_Integer) { + i64 index = operand->value.value_integer; + for (isize i = 0; i < type->Record.other_field_count; i++) { + Entity *f = type->Record.other_fields[i]; + if (f->kind == Entity_Constant && f->Constant.value.kind == ExactValue_Integer) { + i64 fv = f->Constant.value.value_integer; + if (index == fv) { + value = make_exact_value_string(f->token.string); + break; + } + } + } + } + + operand->value = value; + operand->type = t_string; + return true; + } + + add_type_info_type(c, operand->type); + + operand->mode = Addressing_Value; + operand->type = t_string; + } break; + } + + return true; +} + + +void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { + GB_ASSERT(call->kind == AstNode_CallExpr); + GB_ASSERT(proc_type->kind == Type_Proc); + ast_node(ce, CallExpr, call); + + isize param_count = 0; + bool variadic = proc_type->Proc.variadic; + bool vari_expand = (ce->ellipsis.pos.line != 0); + + if (proc_type->Proc.params != NULL) { + param_count = proc_type->Proc.params->Tuple.variable_count; + if (variadic) { + param_count--; + } + } + + if (vari_expand && !variadic) { + error(ce->ellipsis, + "Cannot use `..` in call to a non-variadic procedure: `%.*s`", + LIT(ce->proc->Ident.string)); + return; + } + + if (ce->args.count == 0 && param_count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2*param_count); + + for_array(i, ce->args) { + Operand o = {0}; + check_multi_expr(c, &o, ce->args.e[i]); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + if (variadic && i >= param_count) { + error(ast_node_token(ce->args.e[i]), + "`..` in a variadic procedure cannot be applied to a %td-valued expression", tuple->variable_count); + operand->mode = Addressing_Invalid; + goto end; + } + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + i32 error_code = 0; + if (operands.count < param_count) { + error_code = -1; + } else if (!variadic && operands.count > param_count) { + error_code = +1; + } + if (error_code != 0) { + char *err_fmt = "Too many arguments for `%s`, expected %td arguments"; + if (error_code < 0) { + err_fmt = "Too few arguments for `%s`, expected %td arguments"; + } + + gbString proc_str = expr_to_string(ce->proc); + error(ast_node_token(call), err_fmt, proc_str, param_count); + gb_string_free(proc_str); + operand->mode = Addressing_Invalid; + goto end; + } + + GB_ASSERT(proc_type->Proc.params != NULL); + Entity **sig_params = proc_type->Proc.params->Tuple.variables; + isize operand_index = 0; + for (; operand_index < param_count; operand_index++) { + Type *arg_type = sig_params[operand_index]->type; + Operand o = operands.e[operand_index]; + if (variadic) { + o = operands.e[operand_index]; + } + check_assignment(c, &o, arg_type, str_lit("argument")); + } + + if (variadic) { + bool variadic_expand = false; + Type *slice = sig_params[param_count]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; + for (; operand_index < operands.count; operand_index++) { + Operand o = operands.e[operand_index]; + if (vari_expand) { + variadic_expand = true; + t = slice; + if (operand_index != param_count) { + error(ast_node_token(o.expr), + "`..` in a variadic procedure can only have one variadic argument at the end"); + break; + } + } + check_assignment(c, &o, t, str_lit("argument")); + } + } +end: + gb_temp_arena_memory_end(tmp); +} + + +Entity *find_using_index_expr(Type *t) { + t = base_type(t); + if (t->kind != Type_Record) { + return NULL; + } + + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + if (f->kind == Entity_Variable && + f->flags & (EntityFlag_Anonymous|EntityFlag_Field)) { + if (is_type_indexable(f->type)) { + return f; + } + Entity *res = find_using_index_expr(f->type); + if (res != NULL) { + return res; + } + } + } + return NULL; +} + +ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { + GB_ASSERT(call->kind == AstNode_CallExpr); + ast_node(ce, CallExpr, call); + check_expr_or_type(c, operand, ce->proc); + + if (operand->mode == Addressing_Invalid) { + for_array(i, ce->args) { + check_expr_base(c, operand, ce->args.e[i], NULL); + } + operand->mode = Addressing_Invalid; + operand->expr = call; + return Expr_Stmt; + } + + + if (operand->mode == Addressing_Builtin) { + i32 id = operand->builtin_id; + if (!check_builtin_procedure(c, operand, call, id)) { + operand->mode = Addressing_Invalid; + } + operand->expr = call; + return builtin_procs[id].kind; + } + + Type *proc_type = base_type(operand->type); + if (proc_type == NULL || proc_type->kind != Type_Proc) { + AstNode *e = operand->expr; + gbString str = expr_to_string(e); + error(ast_node_token(e), "Cannot call a non-procedure: `%s`", str); + gb_string_free(str); + + operand->mode = Addressing_Invalid; + operand->expr = call; + + return Expr_Stmt; + } + + check_call_arguments(c, operand, proc_type, call); + + switch (proc_type->Proc.result_count) { + case 0: + operand->mode = Addressing_NoValue; + break; + case 1: + operand->mode = Addressing_Value; + operand->type = proc_type->Proc.results->Tuple.variables[0]->type; + break; + default: + operand->mode = Addressing_Value; + operand->type = proc_type->Proc.results; + break; + } + + operand->expr = call; + return Expr_Stmt; +} + +void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) { + check_expr_base(c, o, e, t); + check_not_tuple(c, o); + char *err_str = NULL; + switch (o->mode) { + case Addressing_NoValue: + err_str = "used as a value"; + break; + case Addressing_Type: + err_str = "is not an expression"; + break; + case Addressing_Builtin: + err_str = "must be called"; + break; + } + if (err_str != NULL) { + gbString str = expr_to_string(e); + error(ast_node_token(e), "`%s` %s", str, err_str); + gb_string_free(str); + o->mode = Addressing_Invalid; + } +} + +bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { + t = base_type(type_deref(t)); + + switch (t->kind) { + case Type_Basic: + if (is_type_string(t)) { + if (o->mode == Addressing_Constant) { + *max_count = o->value.value_string.len; + } + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t_u8; + return true; + } + break; + + case Type_Array: + *max_count = t->Array.count; + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t->Array.elem; + return true; + + case Type_Vector: + *max_count = t->Vector.count; + if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + o->type = t->Vector.elem; + return true; + + + case Type_Slice: + o->type = t->Slice.elem; + o->mode = Addressing_Variable; + return true; + } + + return false; +} + +ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { + ExprKind kind = Expr_Stmt; + + o->mode = Addressing_Invalid; + o->type = t_invalid; + + switch (node->kind) { + default: + goto error; + break; + + case_ast_node(be, BadExpr, node) + goto error; + case_end; + + case_ast_node(i, Ident, node); + check_identifier(c, o, node, type_hint, NULL); + case_end; + + case_ast_node(bl, BasicLit, node); + Type *t = t_invalid; + switch (bl->kind) { + case Token_Integer: t = t_untyped_integer; break; + case Token_Float: t = t_untyped_float; break; + case Token_String: t = t_untyped_string; break; + case Token_Rune: t = t_untyped_rune; break; + default: GB_PANIC("Unknown literal"); break; + } + o->mode = Addressing_Constant; + o->type = t; + o->value = make_exact_value_from_basic_literal(*bl); + case_end; + + case_ast_node(pl, ProcLit, node); + check_open_scope(c, pl->type); + c->context.decl = make_declaration_info(c->allocator, c->context.scope); + Type *proc_type = check_type(c, pl->type); + if (proc_type != NULL) { + check_proc_body(c, empty_token, c->context.decl, proc_type, pl->body); + o->mode = Addressing_Value; + o->type = proc_type; + check_close_scope(c); + } else { + gbString str = expr_to_string(node); + error(ast_node_token(node), "Invalid procedure literal `%s`", str); + gb_string_free(str); + check_close_scope(c); + goto error; + } + case_end; + + case_ast_node(cl, CompoundLit, node); + Type *type = type_hint; + bool ellipsis_array = false; + bool is_constant = true; + if (cl->type != NULL) { + type = NULL; + + // [..]Type + if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != NULL) { + if (cl->type->ArrayType.count->kind == AstNode_Ellipsis) { + type = make_type_array(c->allocator, check_type(c, cl->type->ArrayType.elem), -1); + ellipsis_array = true; + } + } + + if (type == NULL) { + type = check_type(c, cl->type); + } + } + + if (type == NULL) { + error(ast_node_token(node), "Missing type in compound literal"); + goto error; + } + + Type *t = base_type(type); + switch (t->kind) { + case Type_Record: { + if (!is_type_struct(t)) { + if (cl->elems.count != 0) { + error(ast_node_token(node), "Illegal compound literal"); + } + break; + } + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + { // Checker values + isize field_count = t->Record.field_count; + if (cl->elems.e[0]->kind == AstNode_FieldValue) { + bool *fields_visited = gb_alloc_array(c->allocator, bool, field_count); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (elem->kind != AstNode_FieldValue) { + error(ast_node_token(elem), + "Mixture of `field = value` and value elements in a structure literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != AstNode_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(ast_node_token(elem), + "Invalid field name `%s` in structure literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.string; + + Selection sel = lookup_field(c->allocator, type, name, o->mode == Addressing_Type); + if (sel.entity == NULL) { + error(ast_node_token(elem), + "Unknown field `%.*s` in structure literal", LIT(name)); + continue; + } + + if (sel.index.count > 1) { + error(ast_node_token(elem), + "Cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name)); + continue; + } + + Entity *field = t->Record.fields[sel.index.e[0]]; + add_entity_use(c, fv->field, field); + + if (fields_visited[sel.index.e[0]]) { + error(ast_node_token(elem), + "Duplicate field `%.*s` in structure literal", LIT(name)); + continue; + } + + fields_visited[sel.index.e[0]] = true; + check_expr(c, o, fv->value); + + if (base_type(field->type) == t_any) { + is_constant = false; + } + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + + check_assignment(c, o, field->type, str_lit("structure literal")); + } + } else { + for_array(index, cl->elems) { + AstNode *elem = cl->elems.e[index]; + if (elem->kind == AstNode_FieldValue) { + error(ast_node_token(elem), + "Mixture of `field = value` and value elements in a structure literal is not allowed"); + continue; + } + Entity *field = t->Record.fields_in_src_order[index]; + + check_expr(c, o, elem); + if (index >= field_count) { + error(ast_node_token(o->expr), "Too many values in structure literal, expected %td", field_count); + break; + } + + if (base_type(field->type) == t_any) { + is_constant = false; + } + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + check_assignment(c, o, field->type, str_lit("structure literal")); + } + if (cl->elems.count < field_count) { + error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count); + } + } + } + + } break; + + case Type_Slice: + case Type_Array: + case Type_Vector: + { + Type *elem_type = NULL; + String context_name = {0}; + if (t->kind == Type_Slice) { + elem_type = t->Slice.elem; + context_name = str_lit("slice literal"); + } else if (t->kind == Type_Vector) { + elem_type = t->Vector.elem; + context_name = str_lit("vector literal"); + } else { + elem_type = t->Array.elem; + context_name = str_lit("array literal"); + } + + + i64 max = 0; + isize index = 0; + isize elem_count = cl->elems.count; + + if (base_type(elem_type) == t_any) { + is_constant = false; + } + + for (; index < elem_count; index++) { + AstNode *e = cl->elems.e[index]; + if (e->kind == AstNode_FieldValue) { + error(ast_node_token(e), + "`field = value` is only allowed in struct literals"); + continue; + } + + if (t->kind == Type_Array && + t->Array.count >= 0 && + index >= t->Array.count) { + error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for array literal", index, t->Array.count); + } + if (t->kind == Type_Vector && + t->Vector.count >= 0 && + index >= t->Vector.count) { + error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for vector literal", index, t->Vector.count); + } + + Operand operand = {0}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + if (is_constant) { + is_constant = operand.mode == Addressing_Constant; + } + } + if (max < index) { + max = index; + } + + if (t->kind == Type_Vector) { + if (t->Vector.count > 1 && gb_is_between(index, 2, t->Vector.count-1)) { + error(ast_node_token(cl->elems.e[0]), + "Expected either 1 (broadcast) or %td elements in vector literal, got %td", t->Vector.count, index); + } + } + + if (t->kind == Type_Array && ellipsis_array) { + t->Array.count = max; + } + } break; + + default: { + gbString str = type_to_string(type); + error(ast_node_token(node), "Invalid compound literal type `%s`", str); + gb_string_free(str); + goto error; + } break; + } + + if (is_constant) { + o->mode = Addressing_Constant; + o->value = make_exact_value_compound(node); + } else { + o->mode = Addressing_Value; + } + o->type = type; + case_end; + + case_ast_node(pe, ParenExpr, node); + kind = check_expr_base(c, o, pe->expr, type_hint); + o->expr = node; + case_end; + + + case_ast_node(te, TagExpr, node); + // TODO(bill): Tag expressions + error(ast_node_token(node), "Tag expressions are not supported yet"); + kind = check_expr_base(c, o, te->expr, type_hint); + o->expr = node; + case_end; + + case_ast_node(re, RunExpr, node); + // TODO(bill): Tag expressions + kind = check_expr_base(c, o, re->expr, type_hint); + o->expr = node; + case_end; + + + case_ast_node(ue, UnaryExpr, node); + check_expr(c, o, ue->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + check_unary_expr(c, o, ue->op, node); + if (o->mode == Addressing_Invalid) { + goto error; + } + case_end; + + + case_ast_node(be, BinaryExpr, node); + check_binary_expr(c, o, node); + if (o->mode == Addressing_Invalid) { + goto error; + } + case_end; + + + + case_ast_node(se, SelectorExpr, node); + check_selector(c, o, node); + case_end; + + + case_ast_node(ie, IndexExpr, node); + check_expr(c, o, ie->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + + Type *t = base_type(type_deref(o->type)); + bool is_const = o->mode == Addressing_Constant; + + i64 max_count = -1; + bool valid = check_set_index_data(o, t, &max_count); + + if (is_const) { + valid = false; + } + + if (!valid && (is_type_struct(t) || is_type_raw_union(t))) { + Entity *found = find_using_index_expr(t); + if (found != NULL) { + valid = check_set_index_data(o, found->type, &max_count); + } + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + if (is_const) { + error(ast_node_token(o->expr), "Cannot index a constant `%s`", str); + } else { + error(ast_node_token(o->expr), "Cannot index `%s`", str); + } + gb_string_free(str); + goto error; + } + + if (ie->index == NULL) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Missing index for `%s`", str); + gb_string_free(str); + goto error; + } + + i64 index = 0; + bool ok = check_index_value(c, ie->index, max_count, &index); + + case_end; + + + + case_ast_node(se, SliceExpr, node); + check_expr(c, o, se->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } + + bool valid = false; + i64 max_count = -1; + Type *t = base_type(type_deref(o->type)); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t)) { + valid = true; + if (o->mode == Addressing_Constant) { + max_count = o->value.value_string.len; + } + if (se->max != NULL) { + error(ast_node_token(se->max), "Max (3rd) index not needed in substring expression"); + } + o->type = t_string; + } + break; + + case Type_Array: + valid = true; + max_count = t->Array.count; + if (o->mode != Addressing_Variable) { + gbString str = expr_to_string(node); + error(ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str); + gb_string_free(str); + goto error; + } + o->type = make_type_slice(c->allocator, t->Array.elem); + break; + + case Type_Slice: + valid = true; + break; + } + + if (!valid) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot slice `%s`", str); + gb_string_free(str); + goto error; + } + + o->mode = Addressing_Value; + + i64 indices[3] = {0}; + AstNode *nodes[3] = {se->low, se->high, se->max}; + for (isize i = 0; i < gb_count_of(nodes); i++) { + i64 index = max_count; + if (nodes[i] != NULL) { + i64 capacity = -1; + if (max_count >= 0) + capacity = max_count; + i64 j = 0; + if (check_index_value(c, nodes[i], capacity, &j)) { + index = j; + } + } else if (i == 0) { + index = 0; + } + indices[i] = index; + } + + for (isize i = 0; i < gb_count_of(indices); i++) { + i64 a = indices[i]; + for (isize j = i+1; j < gb_count_of(indices); j++) { + i64 b = indices[j]; + if (a > b && b >= 0) { + error(se->close, "Invalid slice indices: [%td > %td]", a, b); + } + } + } + + case_end; + + + case_ast_node(ce, CallExpr, node); + return check_call_expr(c, o, node); + case_end; + + case_ast_node(de, DerefExpr, node); + check_expr_or_type(c, o, de->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } else { + Type *t = base_type(o->type); + if (t->kind == Type_Pointer) { + o->mode = Addressing_Variable; + o->type = t->Pointer.elem; + } else { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot dereference `%s`", str); + gb_string_free(str); + goto error; + } + } + case_end; + + case_ast_node(de, DemaybeExpr, node); + check_expr_or_type(c, o, de->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } else { + Type *t = base_type(o->type); + if (t->kind == Type_Maybe) { + Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); + Type *elem = t->Maybe.elem; + Token tok = make_token_ident(str_lit("")); + variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false); + variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false); + + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = 2; + + o->type = tuple; + o->mode = Addressing_Variable; + } else { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot demaybe `%s`", str); + gb_string_free(str); + goto error; + } + } + case_end; + + case AstNode_ProcType: + case AstNode_PointerType: + case AstNode_MaybeType: + case AstNode_ArrayType: + case AstNode_VectorType: + case AstNode_StructType: + case AstNode_RawUnionType: + o->mode = Addressing_Type; + o->type = check_type(c, node); + break; + } + + kind = Expr_Expr; + o->expr = node; + return kind; + +error: + o->mode = Addressing_Invalid; + o->expr = node; + return kind; +} + +ExprKind check_expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { + ExprKind kind = check__expr_base(c, o, node, type_hint); + Type *type = NULL; + ExactValue value = {ExactValue_Invalid}; + switch (o->mode) { + case Addressing_Invalid: + type = t_invalid; + break; + case Addressing_NoValue: + type = NULL; + break; + case Addressing_Constant: + type = o->type; + value = o->value; + break; + default: + type = o->type; + break; + } + + if (type != NULL && is_type_untyped(type)) { + add_untyped(&c->info, node, false, o->mode, type, value); + } else { + add_type_and_value(&c->info, node, o->mode, type, value); + } + return kind; +} + + +void check_multi_expr(Checker *c, Operand *o, AstNode *e) { + gbString err_str = NULL; + check_expr_base(c, o, e, NULL); + switch (o->mode) { + default: + return; // NOTE(bill): Valid + + case Addressing_NoValue: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` used as value", err_str); + break; + case Addressing_Type: + err_str = expr_to_string(e); + error(ast_node_token(e), "`%s` is not an expression", err_str); + break; + } + gb_string_free(err_str); + o->mode = Addressing_Invalid; +} + +void check_not_tuple(Checker *c, Operand *o) { + if (o->mode == Addressing_Value) { + // NOTE(bill): Tuples are not first class thus never named + if (o->type->kind == Type_Tuple) { + isize count = o->type->Tuple.variable_count; + GB_ASSERT(count != 1); + error(ast_node_token(o->expr), + "%td-valued tuple found where single value expected", count); + o->mode = Addressing_Invalid; + } + } +} + +void check_expr(Checker *c, Operand *o, AstNode *e) { + check_multi_expr(c, o, e); + check_not_tuple(c, o); +} + + +void check_expr_or_type(Checker *c, Operand *o, AstNode *e) { + check_expr_base(c, o, e, NULL); + check_not_tuple(c, o); + if (o->mode == Addressing_NoValue) { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), + "`%s` used as value or type", str); + o->mode = Addressing_Invalid; + gb_string_free(str); + } +} + + +gbString write_expr_to_string(gbString str, AstNode *node); + +gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) { + for_array(i, params) { + ast_node(p, Parameter, params.e[i]); + if (i > 0) { + str = gb_string_appendc(str, sep); + } + + str = write_expr_to_string(str, params.e[i]); + } + return str; +} + +gbString string_append_token(gbString str, Token token) { + if (token.string.len > 0) { + return gb_string_append_length(str, token.string.text, token.string.len); + } + return str; +} + + +gbString write_expr_to_string(gbString str, AstNode *node) { + if (node == NULL) + return str; + + if (is_ast_node_stmt(node)) { + GB_ASSERT("stmt passed to write_expr_to_string"); + } + + switch (node->kind) { + default: + str = gb_string_appendc(str, "(BadExpr)"); + break; + + case_ast_node(i, Ident, node); + str = string_append_token(str, *i); + case_end; + + case_ast_node(bl, BasicLit, node); + str = string_append_token(str, *bl); + case_end; + + case_ast_node(pl, ProcLit, node); + str = write_expr_to_string(str, pl->type); + case_end; + + case_ast_node(cl, CompoundLit, node); + str = write_expr_to_string(str, cl->type); + str = gb_string_appendc(str, "{"); + for_array(i, cl->elems) { + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, cl->elems.e[i]); + } + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(te, TagExpr, node); + str = gb_string_appendc(str, "#"); + str = string_append_token(str, te->name); + str = write_expr_to_string(str, te->expr); + case_end; + + case_ast_node(ue, UnaryExpr, node); + str = string_append_token(str, ue->op); + str = write_expr_to_string(str, ue->expr); + case_end; + + case_ast_node(de, DerefExpr, node); + str = write_expr_to_string(str, de->expr); + str = gb_string_appendc(str, "^"); + case_end; + + case_ast_node(de, DemaybeExpr, node); + str = write_expr_to_string(str, de->expr); + str = gb_string_appendc(str, "?"); + case_end; + + case_ast_node(be, BinaryExpr, node); + str = write_expr_to_string(str, be->left); + str = gb_string_appendc(str, " "); + str = string_append_token(str, be->op); + str = gb_string_appendc(str, " "); + str = write_expr_to_string(str, be->right); + case_end; + + case_ast_node(pe, ParenExpr, node); + str = gb_string_appendc(str, "("); + str = write_expr_to_string(str, pe->expr); + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(se, SelectorExpr, node); + str = write_expr_to_string(str, se->expr); + str = gb_string_appendc(str, "."); + str = write_expr_to_string(str, se->selector); + case_end; + + case_ast_node(ie, IndexExpr, node); + str = write_expr_to_string(str, ie->expr); + str = gb_string_appendc(str, "["); + str = write_expr_to_string(str, ie->index); + str = gb_string_appendc(str, "]"); + case_end; + + case_ast_node(se, SliceExpr, node); + str = write_expr_to_string(str, se->expr); + str = gb_string_appendc(str, "["); + str = write_expr_to_string(str, se->low); + str = gb_string_appendc(str, ":"); + str = write_expr_to_string(str, se->high); + if (se->triple_indexed) { + str = gb_string_appendc(str, ":"); + str = write_expr_to_string(str, se->max); + } + str = gb_string_appendc(str, "]"); + case_end; + + case_ast_node(e, Ellipsis, node); + str = gb_string_appendc(str, ".."); + case_end; + + case_ast_node(fv, FieldValue, node); + str = write_expr_to_string(str, fv->field); + str = gb_string_appendc(str, " = "); + str = write_expr_to_string(str, fv->value); + case_end; + + case_ast_node(pt, PointerType, node); + str = gb_string_appendc(str, "^"); + str = write_expr_to_string(str, pt->type); + case_end; + + case_ast_node(mt, MaybeType, node); + str = gb_string_appendc(str, "?"); + str = write_expr_to_string(str, mt->type); + case_end; + + case_ast_node(at, ArrayType, node); + str = gb_string_appendc(str, "["); + str = write_expr_to_string(str, at->count); + str = gb_string_appendc(str, "]"); + str = write_expr_to_string(str, at->elem); + case_end; + + case_ast_node(vt, VectorType, node); + str = gb_string_appendc(str, "{"); + str = write_expr_to_string(str, vt->count); + str = gb_string_appendc(str, "}"); + str = write_expr_to_string(str, vt->elem); + case_end; + + case_ast_node(p, Parameter, node); + if (p->is_using) { + str = gb_string_appendc(str, "using "); + } + for_array(i, p->names) { + AstNode *name = p->names.e[i]; + if (i > 0) + str = gb_string_appendc(str, ", "); + str = write_expr_to_string(str, name); + } + + str = gb_string_appendc(str, ": "); + str = write_expr_to_string(str, p->type); + case_end; + + case_ast_node(ce, CallExpr, node); + str = write_expr_to_string(str, ce->proc); + str = gb_string_appendc(str, "("); + + for_array(i, ce->args) { + AstNode *arg = ce->args.e[i]; + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, arg); + } + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(pt, ProcType, node); + str = gb_string_appendc(str, "proc("); + str = write_params_to_string(str, pt->params, ", "); + str = gb_string_appendc(str, ")"); + case_end; + + case_ast_node(st, StructType, node); + str = gb_string_appendc(str, "struct "); + if (st->is_packed) str = gb_string_appendc(str, "#packed "); + if (st->is_ordered) str = gb_string_appendc(str, "#ordered "); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(st, RawUnionType, node); + str = gb_string_appendc(str, "raw_union {"); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(st, UnionType, node); + str = gb_string_appendc(str, "union {"); + for_array(i, st->decls) { + if (i > 0) { + str = gb_string_appendc(str, "; "); + } + str = write_expr_to_string(str, st->decls.e[i]); + } + // str = write_params_to_string(str, st->decl_list, ", "); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(et, EnumType, node); + str = gb_string_appendc(str, "enum "); + if (et->base_type != NULL) { + str = write_expr_to_string(str, et->base_type); + str = gb_string_appendc(str, " "); + } + str = gb_string_appendc(str, "{"); + str = gb_string_appendc(str, "}"); + case_end; + } + + return str; +} + +gbString expr_to_string(AstNode *expression) { + return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression); +} diff --git a/src/checker/stmt.c b/src/checker/stmt.c new file mode 100644 index 000000000..ee56c3cd1 --- /dev/null +++ b/src/checker/stmt.c @@ -0,0 +1,1130 @@ +bool check_is_terminating(AstNode *node); +bool check_has_break (AstNode *stmt, bool implicit); +void check_stmt (Checker *c, AstNode *node, u32 flags); + + +// Statements and Declarations +typedef enum StmtFlag { + Stmt_BreakAllowed = GB_BIT(0), + Stmt_ContinueAllowed = GB_BIT(1), + Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough +} StmtFlag; + + + +void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) { + if (stmts.count == 0) { + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + typedef struct { + Entity *e; + DeclInfo *d; + } Delay; + Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count); + Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, stmts.count); + + for_array(i, stmts) { + AstNode *node = stmts.e[i]; + switch (node->kind) { + case_ast_node(cd, ConstDecl, node); + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + ExactValue v = {ExactValue_Invalid}; + + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); + e->identifier = name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = cd->type; + d->init_expr = value; + + add_entity_and_decl_info(c, name, e, d); + + Delay delay = {e, d}; + array_add(&delayed_const, delay); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(node), "Extra initial expression"); + } + case_end; + + case_ast_node(td, TypeDecl, node); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL); + e->identifier = td->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = td->type; + + add_entity_and_decl_info(c, td->name, e, d); + + Delay delay = {e, d}; + array_add(&delayed_type, delay); + case_end; + } + } + + for_array(i, delayed_type) { + check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL); + } + for_array(i, delayed_const) { + check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL); + } + + bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0; + u32 f = flags & (~Stmt_FallthroughAllowed); + + for_array(i, stmts) { + AstNode *n = stmts.e[i]; + if (n->kind == AstNode_EmptyStmt) { + continue; + } + u32 new_flags = f; + if (ft_ok && i+1 == stmts.count) { + new_flags |= Stmt_FallthroughAllowed; + } + check_stmt(c, n, new_flags); + } + + gb_temp_arena_memory_end(tmp); +} + +bool check_is_terminating_list(AstNodeArray stmts) { + + // Iterate backwards + for (isize n = stmts.count-1; n >= 0; n--) { + AstNode *stmt = stmts.e[n]; + if (stmt->kind != AstNode_EmptyStmt) { + return check_is_terminating(stmt); + } + } + + return false; +} + +bool check_has_break_list(AstNodeArray stmts, bool implicit) { + for_array(i, stmts) { + AstNode *stmt = stmts.e[i]; + if (check_has_break(stmt, implicit)) { + return true; + } + } + return false; +} + + +bool check_has_break(AstNode *stmt, bool implicit) { + switch (stmt->kind) { + case AstNode_BranchStmt: + if (stmt->BranchStmt.token.kind == Token_break) { + return implicit; + } + break; + case AstNode_BlockStmt: + return check_has_break_list(stmt->BlockStmt.stmts, implicit); + + case AstNode_IfStmt: + if (check_has_break(stmt->IfStmt.body, implicit) || + (stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) { + return true; + } + break; + + case AstNode_CaseClause: + return check_has_break_list(stmt->CaseClause.stmts, implicit); + } + + return false; +} + + + +// NOTE(bill): The last expression has to be a `return` statement +// TODO(bill): This is a mild hack and should be probably handled properly +// TODO(bill): Warn/err against code after `return` that it won't be executed +bool check_is_terminating(AstNode *node) { + switch (node->kind) { + case_ast_node(rs, ReturnStmt, node); + return true; + case_end; + + case_ast_node(bs, BlockStmt, node); + return check_is_terminating_list(bs->stmts); + case_end; + + case_ast_node(es, ExprStmt, node); + return check_is_terminating(es->expr); + case_end; + + case_ast_node(is, IfStmt, node); + if (is->else_stmt != NULL) { + if (check_is_terminating(is->body) && + check_is_terminating(is->else_stmt)) { + return true; + } + } + case_end; + + case_ast_node(fs, ForStmt, node); + if (fs->cond == NULL && !check_has_break(fs->body, true)) { + return true; + } + case_end; + + case_ast_node(ms, MatchStmt, node); + bool has_default = false; + for_array(i, ms->body->BlockStmt.stmts) { + AstNode *clause = ms->body->BlockStmt.stmts.e[i]; + ast_node(cc, CaseClause, clause); + if (cc->list.count == 0) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; + + case_ast_node(ms, TypeMatchStmt, node); + bool has_default = false; + for_array(i, ms->body->BlockStmt.stmts) { + AstNode *clause = ms->body->BlockStmt.stmts.e[i]; + ast_node(cc, CaseClause, clause); + if (cc->list.count == 0) { + has_default = true; + } + if (!check_is_terminating_list(cc->stmts) || + check_has_break_list(cc->stmts, true)) { + return false; + } + } + return has_default; + case_end; + + case_ast_node(pa, PushAllocator, node); + return check_is_terminating(pa->body); + case_end; + case_ast_node(pc, PushContext, node); + return check_is_terminating(pc->body); + case_end; + } + + return false; +} + +Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { + if (op_a->mode == Addressing_Invalid || + op_a->type == t_invalid) { + return NULL; + } + + AstNode *node = unparen_expr(lhs); + + // NOTE(bill): Ignore assignments to `_` + if (node->kind == AstNode_Ident && + str_eq(node->Ident.string, str_lit("_"))) { + add_entity_definition(&c->info, node, NULL); + check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier")); + if (op_a->mode == Addressing_Invalid) + return NULL; + return op_a->type; + } + + Entity *e = NULL; + bool used = false; + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + e = scope_lookup_entity(c->context.scope, i->string); + if (e != NULL && e->kind == Entity_Variable) { + used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case + } + } + + + Operand op_b = {Addressing_Invalid}; + check_expr(c, &op_b, lhs); + if (e) { + e->flags |= EntityFlag_Used*used; + } + + if (op_b.mode == Addressing_Invalid || + op_b.type == t_invalid) { + return NULL; + } + + switch (op_b.mode) { + case Addressing_Invalid: + return NULL; + case Addressing_Variable: + break; + default: { + if (op_b.expr->kind == AstNode_SelectorExpr) { + // NOTE(bill): Extra error checks + Operand op_c = {Addressing_Invalid}; + ast_node(se, SelectorExpr, op_b.expr); + check_expr(c, &op_c, se->expr); + } + + gbString str = expr_to_string(op_b.expr); + switch (op_b.mode) { + case Addressing_Value: + error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); + break; + default: + error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); + break; + } + gb_string_free(str); + } break; + } + + check_assignment(c, op_a, op_b.type, str_lit("assignment")); + if (op_a->mode == Addressing_Invalid) { + return NULL; + } + + return op_a->type; +} + +bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { + if (is_type_pointer(type)) { + *is_union_ptr = is_type_union(type_deref(type)); + return *is_union_ptr; + } + if (is_type_any(type)) { + *is_any = true; + return *is_any; + } + return false; +} + +void check_stmt_internal(Checker *c, AstNode *node, u32 flags); +void check_stmt(Checker *c, AstNode *node, u32 flags) { + u32 prev_stmt_state_flags = c->context.stmt_state_flags; + + if (node->stmt_state_flags != 0) { + u32 in = node->stmt_state_flags; + u32 out = c->context.stmt_state_flags; + + if (in & StmtStateFlag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & StmtStateFlag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + + c->context.stmt_state_flags = out; + } + + check_stmt_internal(c, node, flags); + + c->context.stmt_state_flags = prev_stmt_state_flags; +} + +typedef struct TypeAndToken { + Type *type; + Token token; +} TypeAndToken; + +#define MAP_TYPE TypeAndToken +#define MAP_PROC map_type_and_token_ +#define MAP_NAME MapTypeAndToken +#include "../map.c" + +void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { + u32 mod_flags = flags & (~Stmt_FallthroughAllowed); + switch (node->kind) { + case_ast_node(_, EmptyStmt, node); case_end; + case_ast_node(_, BadStmt, node); case_end; + case_ast_node(_, BadDecl, node); case_end; + + case_ast_node(es, ExprStmt, node) + Operand operand = {Addressing_Invalid}; + ExprKind kind = check_expr_base(c, &operand, es->expr, NULL); + switch (operand.mode) { + case Addressing_Type: + error(ast_node_token(node), "Is not an expression"); + break; + case Addressing_NoValue: + return; + default: { + if (kind == Expr_Stmt) { + return; + } + if (operand.expr->kind == AstNode_CallExpr) { + return; + } + gbString expr_str = expr_to_string(operand.expr); + error(ast_node_token(node), "Expression is not used: `%s`", expr_str); + gb_string_free(expr_str); + } break; + } + case_end; + + case_ast_node(ts, TagStmt, node); + // TODO(bill): Tag Statements + error(ast_node_token(node), "Tag statements are not supported yet"); + check_stmt(c, ts->stmt, flags); + case_end; + + case_ast_node(ids, IncDecStmt, node); + Token op = ids->op; + switch (ids->op.kind) { + case Token_Increment: + op.kind = Token_Add; + op.string.len = 1; + break; + case Token_Decrement: + op.kind = Token_Sub; + op.string.len = 1; + break; + default: + error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string)); + return; + } + + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, ids->expr); + if (operand.mode == Addressing_Invalid) + return; + if (!is_type_numeric(operand.type)) { + error(ids->op, "Non numeric type"); + return; + } + + AstNode basic_lit = {AstNode_BasicLit}; + ast_node(bl, BasicLit, &basic_lit); + *bl = ids->op; + bl->kind = Token_Integer; + bl->string = str_lit("1"); + + AstNode binary_expr = {AstNode_BinaryExpr}; + ast_node(be, BinaryExpr, &binary_expr); + be->op = op; + be->left = ids->expr; + be->right = &basic_lit; + check_binary_expr(c, &operand, &binary_expr); + case_end; + + case_ast_node(as, AssignStmt, node); + switch (as->op.kind) { + case Token_Eq: { + // a, b, c = 1, 2, 3; // Multisided + if (as->lhs.count == 0) { + error(as->op, "Missing lhs in assignment statement"); + return; + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be + // an extra allocation + Array(Operand) operands; + array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count); + + for_array(i, as->rhs) { + AstNode *rhs = as->rhs.e[i]; + Operand o = {0}; + check_multi_expr(c, &o, rhs); + if (o.type->kind != Type_Tuple) { + array_add(&operands, o); + } else { + TypeTuple *tuple = &o.type->Tuple; + for (isize j = 0; j < tuple->variable_count; j++) { + o.type = tuple->variables[j]->type; + array_add(&operands, o); + } + } + } + + isize lhs_count = as->lhs.count; + isize rhs_count = operands.count; + + isize operand_count = gb_min(as->lhs.count, operands.count); + for (isize i = 0; i < operand_count; i++) { + AstNode *lhs = as->lhs.e[i]; + check_assignment_variable(c, &operands.e[i], lhs); + } + if (lhs_count != rhs_count) { + error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); + } + + gb_temp_arena_memory_end(tmp); + } break; + + default: { + // a += 1; // Single-sided + Token op = as->op; + if (as->lhs.count != 1 || as->rhs.count != 1) { + error(op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string)); + return; + } + if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) { + error(op, "Unknown Assignment operation `%.*s`", LIT(op.string)); + return; + } + // TODO(bill): Check if valid assignment operator + Operand operand = {Addressing_Invalid}; + AstNode binary_expr = {AstNode_BinaryExpr}; + ast_node(be, BinaryExpr, &binary_expr); + be->op = op; + be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); + // NOTE(bill): Only use the first one will be used + be->left = as->lhs.e[0]; + be->right = as->rhs.e[0]; + + check_binary_expr(c, &operand, &binary_expr); + if (operand.mode == Addressing_Invalid) { + return; + } + // NOTE(bill): Only use the first one will be used + check_assignment_variable(c, &operand, as->lhs.e[0]); + } break; + } + case_end; + + case_ast_node(bs, BlockStmt, node); + check_open_scope(c, node); + check_stmt_list(c, bs->stmts, mod_flags); + check_close_scope(c); + case_end; + + case_ast_node(is, IfStmt, node); + check_open_scope(c, node); + + if (is->init != NULL) { + check_stmt(c, is->init, 0); + } + + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, is->cond); + if (operand.mode != Addressing_Invalid && + !is_type_boolean(operand.type)) { + error(ast_node_token(is->cond), + "Non-boolean condition in `if` statement"); + } + + check_stmt(c, is->body, mod_flags); + + if (is->else_stmt) { + switch (is->else_stmt->kind) { + case AstNode_IfStmt: + case AstNode_BlockStmt: + check_stmt(c, is->else_stmt, mod_flags); + break; + default: + error(ast_node_token(is->else_stmt), + "Invalid `else` statement in `if` statement"); + break; + } + } + + check_close_scope(c); + case_end; + + case_ast_node(rs, ReturnStmt, node); + GB_ASSERT(c->proc_stack.count > 0); + + if (c->in_defer) { + error(rs->token, "You cannot `return` within a defer statement"); + // TODO(bill): Should I break here? + break; + } + + + Type *proc_type = c->proc_stack.e[c->proc_stack.count-1]; + isize result_count = 0; + if (proc_type->Proc.results) { + result_count = proc_type->Proc.results->Tuple.variable_count; + } + + if (result_count > 0) { + Entity **variables = NULL; + if (proc_type->Proc.results != NULL) { + TypeTuple *tuple = &proc_type->Proc.results->Tuple; + variables = tuple->variables; + } + if (rs->results.count == 0) { + error(ast_node_token(node), "Expected %td return values, got 0", result_count); + } else { + check_init_variables(c, variables, result_count, + rs->results, str_lit("return statement")); + } + } else if (rs->results.count > 0) { + error(ast_node_token(rs->results.e[0]), "No return values expected"); + } + case_end; + + case_ast_node(fs, ForStmt, node); + u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; + check_open_scope(c, node); + + if (fs->init != NULL) { + check_stmt(c, fs->init, 0); + } + if (fs->cond) { + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, fs->cond); + if (operand.mode != Addressing_Invalid && + !is_type_boolean(operand.type)) { + error(ast_node_token(fs->cond), + "Non-boolean condition in `for` statement"); + } + } + if (fs->post != NULL) { + check_stmt(c, fs->post, 0); + } + check_stmt(c, fs->body, new_flags); + + check_close_scope(c); + case_end; + + case_ast_node(ms, MatchStmt, node); + Operand x = {0}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + + if (ms->init != NULL) { + check_stmt(c, ms->init, 0); + } + if (ms->tag != NULL) { + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, str_lit("match expression")); + } else { + x.mode = Addressing_Constant; + x.type = t_bool; + x.value = make_exact_value_bool(true); + + Token token = {0}; + token.pos = ast_node_token(ms->body).pos; + token.string = str_lit("true"); + x.expr = make_ident(c->curr_ast_file, token); + } + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(cc, CaseClause, stmt); + if (cc->list.count == 0) { + default_stmt = stmt; + } + } else { + error(ast_node_token(stmt), "Invalid AST - expected case clause"); + } + + if (default_stmt != NULL) { + if (first_default != NULL) { + TokenPos pos = ast_node_token(first_default).pos; + error(ast_node_token(stmt), + "multiple `default` clauses\n" + "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + } else { + first_default = default_stmt; + } + } + } +; + + MapTypeAndToken seen = {0}; // NOTE(bill): Multimap + map_type_and_token_init(&seen, heap_allocator()); + + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + + for_array(j, cc->list) { + AstNode *expr = cc->list.e[j]; + Operand y = {0}; + Operand z = {0}; + Token eq = {Token_CmpEq}; + + check_expr(c, &y, expr); + if (x.mode == Addressing_Invalid || + y.mode == Addressing_Invalid) { + continue; + } + convert_to_typed(c, &y, x.type, 0); + if (y.mode == Addressing_Invalid) { + continue; + } + + z = y; + check_comparison(c, &z, &x, eq); + if (z.mode == Addressing_Invalid) { + continue; + } + if (y.mode != Addressing_Constant) { + continue; + } + + if (y.value.kind != ExactValue_Invalid) { + HashKey key = hash_exact_value(y.value); + TypeAndToken *found = map_type_and_token_get(&seen, key); + if (found != NULL) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + isize count = map_type_and_token_multi_count(&seen, key); + TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); + + map_type_and_token_multi_get_all(&seen, key, taps); + bool continue_outer = false; + + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (are_types_identical(y.type, tap.type)) { + TokenPos pos = tap.token.pos; + gbString expr_str = expr_to_string(y.expr); + error(ast_node_token(y.expr), + "Duplicate case `%s`\n" + "\tprevious case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + continue_outer = true; + break; + } + } + + gb_temp_arena_memory_end(tmp); + + if (continue_outer) { + continue; + } + } + TypeAndToken tap = {y.type, ast_node_token(y.expr)}; + map_type_and_token_multi_insert(&seen, key, tap); + } + } + + check_open_scope(c, stmt); + u32 ft_flags = mod_flags; + if (i+1 < bs->stmts.count) { + ft_flags |= Stmt_FallthroughAllowed; + } + check_stmt_list(c, cc->stmts, ft_flags); + check_close_scope(c); + } + + map_type_and_token_destroy(&seen); + + check_close_scope(c); + case_end; + + case_ast_node(ms, TypeMatchStmt, node); + Operand x = {0}; + + mod_flags |= Stmt_BreakAllowed; + check_open_scope(c, node); + + bool is_union_ptr = false; + bool is_any = false; + + check_expr(c, &x, ms->tag); + check_assignment(c, &x, NULL, str_lit("type match expression")); + if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) { + gbString str = type_to_string(x.type); + error(ast_node_token(x.expr), + "Invalid type for this type match expression, got `%s`", str); + gb_string_free(str); + break; + } + + + // NOTE(bill): Check for multiple defaults + AstNode *first_default = NULL; + ast_node(bs, BlockStmt, ms->body); + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + AstNode *default_stmt = NULL; + if (stmt->kind == AstNode_CaseClause) { + ast_node(cc, CaseClause, stmt); + if (cc->list.count == 0) { + default_stmt = stmt; + } + } else { + error(ast_node_token(stmt), "Invalid AST - expected case clause"); + } + + if (default_stmt != NULL) { + if (first_default != NULL) { + TokenPos pos = ast_node_token(first_default).pos; + error(ast_node_token(stmt), + "multiple `default` clauses\n" + "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + } else { + first_default = default_stmt; + } + } + } + + if (ms->var->kind != AstNode_Ident) { + break; + } + + + MapBool seen = {0}; + map_bool_init(&seen, heap_allocator()); + + for_array(i, bs->stmts) { + AstNode *stmt = bs->stmts.e[i]; + if (stmt->kind != AstNode_CaseClause) { + // NOTE(bill): error handled by above multiple default checker + continue; + } + ast_node(cc, CaseClause, stmt); + + // TODO(bill): Make robust + Type *bt = base_type(type_deref(x.type)); + + + AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL; + Type *case_type = NULL; + if (type_expr != NULL) { // Otherwise it's a default expression + Operand y = {0}; + check_expr_or_type(c, &y, type_expr); + + if (is_union_ptr) { + GB_ASSERT(is_type_union(bt)); + bool tag_type_found = false; + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->Record.fields[i]; + if (are_types_identical(f->type, y.type)) { + tag_type_found = true; + break; + } + } + if (!tag_type_found) { + gbString type_str = type_to_string(y.type); + error(ast_node_token(y.expr), + "Unknown tag type, got `%s`", type_str); + gb_string_free(type_str); + continue; + } + case_type = y.type; + } else if (is_any) { + case_type = y.type; + } else { + GB_PANIC("Unknown type to type match statement"); + } + + HashKey key = hash_pointer(y.type); + bool *found = map_bool_get(&seen, key); + if (found) { + TokenPos pos = cc->token.pos; + gbString expr_str = expr_to_string(y.expr); + error(ast_node_token(y.expr), + "Duplicate type case `%s`\n" + "\tprevious type case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + break; + } + map_bool_set(&seen, key, cast(bool)true); + } + + check_open_scope(c, stmt); + if (case_type != NULL) { + add_type_info_type(c, case_type); + + // NOTE(bill): Dummy type + Type *tt = case_type; + if (is_union_ptr) { + tt = make_type_pointer(c->allocator, case_type); + add_type_info_type(c, tt); + } + Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt); + tag_var->flags |= EntityFlag_Used; + add_entity(c, c->context.scope, ms->var, tag_var); + add_entity_use(c, ms->var, tag_var); + } + check_stmt_list(c, cc->stmts, mod_flags); + check_close_scope(c); + } + map_bool_destroy(&seen); + + check_close_scope(c); + case_end; + + + case_ast_node(ds, DeferStmt, node); + if (is_ast_node_decl(ds->stmt)) { + error(ds->token, "You cannot defer a declaration"); + } else { + bool out_in_defer = c->in_defer; + c->in_defer = true; + check_stmt(c, ds->stmt, 0); + c->in_defer = out_in_defer; + } + case_end; + + case_ast_node(bs, BranchStmt, node); + Token token = bs->token; + switch (token.kind) { + case Token_break: + if ((flags & Stmt_BreakAllowed) == 0) { + error(token, "`break` only allowed in `for` or `match` statements"); + } + break; + case Token_continue: + if ((flags & Stmt_ContinueAllowed) == 0) { + error(token, "`continue` only allowed in `for` statements"); + } + break; + case Token_fallthrough: + if ((flags & Stmt_FallthroughAllowed) == 0) { + error(token, "`fallthrough` statement in illegal position"); + } + break; + default: + error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); + break; + } + case_end; + + case_ast_node(us, UsingStmt, node); + switch (us->node->kind) { + case_ast_node(es, ExprStmt, us->node); + // TODO(bill): Allow for just a LHS expression list rather than this silly code + Entity *e = NULL; + + bool is_selector = false; + AstNode *expr = unparen_expr(es->expr); + if (expr->kind == AstNode_Ident) { + String name = expr->Ident.string; + e = scope_lookup_entity(c->context.scope, name); + } else if (expr->kind == AstNode_SelectorExpr) { + Operand o = {0}; + e = check_selector(c, &o, expr); + is_selector = true; + } + + if (e == NULL) { + error(us->token, "`using` applied to an unknown entity"); + return; + } + + switch (e->kind) { + case Entity_TypeName: { + Type *t = base_type(e->type); + if (is_type_struct(t) || is_type_enum(t)) { + for (isize i = 0; i < t->Record.other_field_count; i++) { + Entity *f = t->Record.other_fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + } else if (is_type_union(t)) { + for (isize i = 0; i < t->Record.field_count; i++) { + Entity *f = t->Record.fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + for (isize i = 0; i < t->Record.other_field_count; i++) { + Entity *f = t->Record.other_fields[i]; + Entity *found = scope_insert_entity(c->context.scope, f); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string)); + gb_string_free(expr_str); + return; + } + f->using_parent = e; + } + } + } break; + + case Entity_ImportName: { + Scope *scope = e->ImportName.scope; + for_array(i, scope->elements.entries) { + Entity *decl = scope->elements.entries.e[i].value; + Entity *found = scope_insert_entity(c->context.scope, decl); + if (found != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, + "Namespace collision while `using` `%s` of: %.*s\n" + "\tat %.*s(%td:%td)\n" + "\tat %.*s(%td:%td)", + expr_str, LIT(found->token.string), + LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column, + LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column + ); + gb_string_free(expr_str); + return; + } + } + } break; + + case Entity_Variable: { + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + if (is_selector) { + uvar->using_expr = expr; + } + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + gbString expr_str = expr_to_string(expr); + error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string)); + gb_string_free(expr_str); + return; + } + } + } + } else { + error(us->token, "`using` can only be applied to variables of type struct or raw_union"); + return; + } + } break; + + case Entity_Constant: + error(us->token, "`using` cannot be applied to a constant"); + break; + + case Entity_Procedure: + case Entity_Builtin: + error(us->token, "`using` cannot be applied to a procedure"); + break; + + case Entity_ImplicitValue: + error(us->token, "`using` cannot be applied to an implicit value"); + break; + + case Entity_Nil: + error(us->token, "`using` cannot be applied to `nil`"); + break; + + case Entity_Invalid: + error(us->token, "`using` cannot be applied to an invalid entity"); + break; + + default: + GB_PANIC("TODO(bill): `using` other expressions?"); + } + case_end; + + case_ast_node(vd, VarDecl, us->node); + if (vd->names.count > 1 && vd->type != NULL) { + error(us->token, "`using` can only be applied to one variable of the same type"); + } + check_var_decl_node(c, us->node); + + for_array(name_index, vd->names) { + AstNode *item = vd->names.e[name_index]; + ast_node(i, Ident, item); + String name = i->string; + Entity *e = scope_lookup_entity(c->context.scope, name); + Type *t = base_type(type_deref(e->type)); + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node)); + GB_ASSERT(found != NULL); + for_array(i, (*found)->elements.entries) { + Entity *f = (*found)->elements.entries.e[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != NULL) { + error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + return; + } + } + } + } else { + error(us->token, "`using` can only be applied to variables of type struct or raw_union"); + return; + } + } + case_end; + + + default: + error(us->token, "Invalid AST: Using Statement"); + break; + } + case_end; + + + + case_ast_node(pa, PushAllocator, node); + Operand op = {0}; + check_expr(c, &op, pa->expr); + check_assignment(c, &op, t_allocator, str_lit("argument to push_allocator")); + check_stmt(c, pa->body, mod_flags); + case_end; + + + case_ast_node(pa, PushContext, node); + Operand op = {0}; + check_expr(c, &op, pa->expr); + check_assignment(c, &op, t_context, str_lit("argument to push_context")); + check_stmt(c, pa->body, mod_flags); + case_end; + + + + + + + case_ast_node(vd, VarDecl, node); + check_var_decl_node(c, node); + case_end; + + case_ast_node(cd, ConstDecl, node); + // NOTE(bill): Handled elsewhere + case_end; + + case_ast_node(td, TypeDecl, node); + // NOTE(bill): Handled elsewhere + case_end; + + case_ast_node(pd, ProcDecl, node); + // NOTE(bill): This must be handled here so it has access to the parent scope stuff + // e.g. using + Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL); + e->identifier = pd->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->proc_decl = node; + + add_entity_and_decl_info(c, pd->name, e, d); + check_entity_decl(c, e, d, NULL, NULL); + case_end; + } +} diff --git a/src/checker/types.c b/src/checker/types.c new file mode 100644 index 000000000..f51cbb660 --- /dev/null +++ b/src/checker/types.c @@ -0,0 +1,1487 @@ +typedef struct Scope Scope; + +typedef enum BasicKind { + Basic_Invalid, + Basic_bool, + Basic_i8, + Basic_u8, + Basic_i16, + Basic_u16, + Basic_i32, + Basic_u32, + Basic_i64, + Basic_u64, + Basic_i128, + Basic_u128, + // Basic_f16, + Basic_f32, + Basic_f64, + // Basic_f128, + Basic_int, + Basic_uint, + Basic_rawptr, + Basic_string, // ^u8 + int + Basic_any, // ^Type_Info + rawptr + + Basic_UntypedBool, + Basic_UntypedInteger, + Basic_UntypedFloat, + Basic_UntypedString, + Basic_UntypedRune, + Basic_UntypedNil, + + Basic_Count, + + Basic_byte = Basic_u8, + Basic_rune = Basic_i32, +} BasicKind; + +typedef enum BasicFlag { + BasicFlag_Boolean = GB_BIT(0), + BasicFlag_Integer = GB_BIT(1), + BasicFlag_Unsigned = GB_BIT(2), + BasicFlag_Float = GB_BIT(3), + BasicFlag_Pointer = GB_BIT(4), + BasicFlag_String = GB_BIT(5), + BasicFlag_Rune = GB_BIT(6), + BasicFlag_Untyped = GB_BIT(7), + + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float, + BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer, + BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune, +} BasicFlag; + +typedef struct BasicType { + BasicKind kind; + u32 flags; + i64 size; // -1 if arch. dep. + String name; +} BasicType; + +typedef enum TypeRecordKind { + TypeRecord_Invalid, + + TypeRecord_Struct, + TypeRecord_Enum, + TypeRecord_RawUnion, + TypeRecord_Union, // Tagged + + TypeRecord_Count, +} TypeRecordKind; + +typedef struct TypeRecord { + TypeRecordKind kind; + + // All record types + // Theses are arrays + Entity **fields; // Entity_Variable (otherwise Entity_TypeName if union) + i32 field_count; // == offset_count is struct + AstNode *node; + + union { // NOTE(bill): Reduce size_of Type + struct { // enum only + Type * enum_base; // Default is `int` + Entity * enum_count; + Entity * min_value; + Entity * max_value; + }; + struct { // struct only + i64 * struct_offsets; + bool struct_are_offsets_set; + bool struct_is_packed; + bool struct_is_ordered; + Entity **fields_in_src_order; // Entity_Variable + }; + }; + + // Entity_Constant or Entity_TypeName + Entity **other_fields; + i32 other_field_count; +} TypeRecord; + +#define TYPE_KINDS \ + TYPE_KIND(Basic, BasicType) \ + TYPE_KIND(Pointer, struct { Type *elem; }) \ + TYPE_KIND(Array, struct { Type *elem; i64 count; }) \ + TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \ + TYPE_KIND(Slice, struct { Type *elem; }) \ + TYPE_KIND(Maybe, struct { Type *elem; }) \ + TYPE_KIND(Record, TypeRecord) \ + TYPE_KIND(Named, struct { \ + String name; \ + Type * base; \ + Entity *type_name; /* Entity_TypeName */ \ + }) \ + TYPE_KIND(Tuple, struct { \ + Entity **variables; /* Entity_Variable */ \ + i32 variable_count; \ + bool are_offsets_set; \ + i64 * offsets; \ + }) \ + TYPE_KIND(Proc, struct { \ + Scope *scope; \ + Type * params; /* Type_Tuple */ \ + Type * results; /* Type_Tuple */ \ + i32 param_count; \ + i32 result_count; \ + bool variadic; \ + }) + +typedef enum TypeKind { + Type_Invalid, +#define TYPE_KIND(k, ...) GB_JOIN2(Type_, k), + TYPE_KINDS +#undef TYPE_KIND + Type_Count, +} TypeKind; + +String const type_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")}, +#define TYPE_KIND(k, ...) {cast(u8 *)#k, gb_size_of(#k)-1}, + TYPE_KINDS +#undef TYPE_KIND +}; + +#define TYPE_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(Type, k); + TYPE_KINDS +#undef TYPE_KIND + +typedef struct Type { + TypeKind kind; + union { +#define TYPE_KIND(k, ...) GB_JOIN2(Type, k) k; + TYPE_KINDS +#undef TYPE_KIND + }; +} Type; + +// NOTE(bill): Internal sizes of certain types +// string: 2*word_size (ptr+len) +// slice: 3*word_size (ptr+len+cap) +// array: count*size_of(elem) aligned + +// NOTE(bill): Alignment of structures and other types are to be compatible with C + +typedef struct BaseTypeSizes { + i64 word_size; + i64 max_align; +} BaseTypeSizes; + +typedef Array(isize) Array_isize; + +typedef struct Selection { + Entity * entity; + Array_isize index; + bool indirect; // Set if there was a pointer deref anywhere down the line +} Selection; +Selection empty_selection = {0}; + +Selection make_selection(Entity *entity, Array_isize index, bool indirect) { + Selection s = {entity, index, indirect}; + return s; +} + +void selection_add_index(Selection *s, isize index) { + // IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form + // of heap allocation + if (s->index.e == NULL) { + array_init(&s->index, heap_allocator()); + } + array_add(&s->index, index); +} + + + +#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1} +gb_global Type basic_types[] = { + {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, + {Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}}, + {Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}}, + {Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}}, + {Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}}, + {Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}}, + {Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}}, + {Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}}, + {Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}}, + {Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}}, + {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, + {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, + // {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}}, + {Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}}, + {Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}}, + // {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}}, + {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, + {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, + {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, + {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, + {Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}}, + {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, + {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, + {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, + {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, + {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, + {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, +}; + +gb_global Type basic_type_aliases[] = { + {Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("byte")}}, + {Type_Basic, {Basic_rune, BasicFlag_Integer, 4, STR_LIT("rune")}}, +}; + +gb_global Type *t_invalid = &basic_types[Basic_Invalid]; +gb_global Type *t_bool = &basic_types[Basic_bool]; +gb_global Type *t_i8 = &basic_types[Basic_i8]; +gb_global Type *t_u8 = &basic_types[Basic_u8]; +gb_global Type *t_i16 = &basic_types[Basic_i16]; +gb_global Type *t_u16 = &basic_types[Basic_u16]; +gb_global Type *t_i32 = &basic_types[Basic_i32]; +gb_global Type *t_u32 = &basic_types[Basic_u32]; +gb_global Type *t_i64 = &basic_types[Basic_i64]; +gb_global Type *t_u64 = &basic_types[Basic_u64]; +gb_global Type *t_i128 = &basic_types[Basic_i128]; +gb_global Type *t_u128 = &basic_types[Basic_u128]; +// gb_global Type *t_f16 = &basic_types[Basic_f16]; +gb_global Type *t_f32 = &basic_types[Basic_f32]; +gb_global Type *t_f64 = &basic_types[Basic_f64]; +// gb_global Type *t_f128 = &basic_types[Basic_f128]; +gb_global Type *t_int = &basic_types[Basic_int]; +gb_global Type *t_uint = &basic_types[Basic_uint]; +gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; +gb_global Type *t_string = &basic_types[Basic_string]; +gb_global Type *t_any = &basic_types[Basic_any]; +gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; +gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; +gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; +gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; +gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; +gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; +gb_global Type *t_byte = &basic_type_aliases[0]; +gb_global Type *t_rune = &basic_type_aliases[1]; + + +gb_global Type *t_u8_ptr = NULL; +gb_global Type *t_int_ptr = NULL; + +gb_global Type *t_type_info = NULL; +gb_global Type *t_type_info_ptr = NULL; +gb_global Type *t_type_info_member = NULL; +gb_global Type *t_type_info_member_ptr = NULL; + +gb_global Type *t_type_info_named = NULL; +gb_global Type *t_type_info_integer = NULL; +gb_global Type *t_type_info_float = NULL; +gb_global Type *t_type_info_any = NULL; +gb_global Type *t_type_info_string = NULL; +gb_global Type *t_type_info_boolean = NULL; +gb_global Type *t_type_info_pointer = NULL; +gb_global Type *t_type_info_maybe = NULL; +gb_global Type *t_type_info_procedure = NULL; +gb_global Type *t_type_info_array = NULL; +gb_global Type *t_type_info_slice = NULL; +gb_global Type *t_type_info_vector = NULL; +gb_global Type *t_type_info_tuple = NULL; +gb_global Type *t_type_info_struct = NULL; +gb_global Type *t_type_info_union = NULL; +gb_global Type *t_type_info_raw_union = NULL; +gb_global Type *t_type_info_enum = NULL; + +gb_global Type *t_allocator = NULL; +gb_global Type *t_allocator_ptr = NULL; +gb_global Type *t_context = NULL; +gb_global Type *t_context_ptr = NULL; + + + + + + +gbString type_to_string(Type *type); + +Type *base_type(Type *t) { + for (;;) { + if (t == NULL || t->kind != Type_Named) { + break; + } + t = t->Named.base; + } + return t; +} + +void set_base_type(Type *t, Type *base) { + if (t && t->kind == Type_Named) { + t->Named.base = base; + } +} + + +Type *alloc_type(gbAllocator a, TypeKind kind) { + Type *t = gb_alloc_item(a, Type); + t->kind = kind; + return t; +} + + +Type *make_type_basic(gbAllocator a, BasicType basic) { + Type *t = alloc_type(a, Type_Basic); + t->Basic = basic; + return t; +} + +Type *make_type_pointer(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Pointer); + t->Pointer.elem = elem; + return t; +} + +Type *make_type_maybe(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Maybe); + t->Maybe.elem = elem; + return t; +} + +Type *make_type_array(gbAllocator a, Type *elem, i64 count) { + Type *t = alloc_type(a, Type_Array); + t->Array.elem = elem; + t->Array.count = count; + return t; +} + +Type *make_type_vector(gbAllocator a, Type *elem, i64 count) { + Type *t = alloc_type(a, Type_Vector); + t->Vector.elem = elem; + t->Vector.count = count; + return t; +} + +Type *make_type_slice(gbAllocator a, Type *elem) { + Type *t = alloc_type(a, Type_Slice); + t->Array.elem = elem; + return t; +} + + +Type *make_type_struct(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Struct; + return t; +} + +Type *make_type_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Union; + return t; +} + +Type *make_type_raw_union(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_RawUnion; + return t; +} + +Type *make_type_enum(gbAllocator a) { + Type *t = alloc_type(a, Type_Record); + t->Record.kind = TypeRecord_Enum; + return t; +} + + + +Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) { + Type *t = alloc_type(a, Type_Named); + t->Named.name = name; + t->Named.base = base; + t->Named.type_name = type_name; + return t; +} + +Type *make_type_tuple(gbAllocator a) { + Type *t = alloc_type(a, Type_Tuple); + return t; +} + +Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, bool variadic) { + Type *t = alloc_type(a, Type_Proc); + + if (variadic) { + if (param_count == 0) { + GB_PANIC("variadic procedure must have at least one parameter"); + } + GB_ASSERT(params != NULL && params->kind == Type_Tuple); + Entity *e = params->Tuple.variables[param_count-1]; + if (base_type(e->type)->kind != Type_Slice) { + // NOTE(bill): For custom calling convention + GB_PANIC("variadic parameter must be of type slice"); + } + } + + t->Proc.scope = scope; + t->Proc.params = params; + t->Proc.param_count = param_count; + t->Proc.results = results; + t->Proc.result_count = result_count; + t->Proc.variadic = variadic; + return t; +} + + +Type *type_deref(Type *t) { + if (t != NULL) { + Type *bt = base_type(t); + if (bt == NULL) + return NULL; + if (bt != NULL && bt->kind == Type_Pointer) + return bt->Pointer.elem; + } + return t; +} + +Type *get_enum_base_type(Type *t) { + Type *bt = base_type(t); + if (bt->kind == Type_Record && bt->Record.kind == TypeRecord_Enum) { + GB_ASSERT(bt->Record.enum_base != NULL); + return bt->Record.enum_base; + } + return t; +} + +bool is_type_named(Type *t) { + if (t->kind == Type_Basic) { + return true; + } + return t->kind == Type_Named; +} +bool is_type_boolean(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Boolean) != 0; + } + return false; +} +bool is_type_integer(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Integer) != 0; + } + return false; +} +bool is_type_unsigned(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Unsigned) != 0; + } + return false; +} +bool is_type_numeric(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Numeric) != 0; + } + if (t->kind == Type_Vector) { + return is_type_numeric(t->Vector.elem); + } + return false; +} +bool is_type_string(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_String) != 0; + } + return false; +} +bool is_type_typed(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Untyped) == 0; + } + return true; +} +bool is_type_untyped(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Untyped) != 0; + } + return false; +} +bool is_type_ordered(Type *t) { + t = base_type(get_enum_base_type(t)); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Ordered) != 0; + } + if (t->kind == Type_Pointer) { + return true; + } + return false; +} +bool is_type_constant_type(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_ConstantType) != 0; + } + if (t->kind == Type_Record) { + return t->Record.kind == TypeRecord_Enum; + } + return false; +} +bool is_type_float(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Float) != 0; + } + return false; +} +bool is_type_f32(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f32; + } + return false; +} +bool is_type_f64(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_f64; + } + return false; +} +bool is_type_pointer(Type *t) { + t = base_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Pointer) != 0; + } + return t->kind == Type_Pointer; +} +bool is_type_maybe(Type *t) { + t = base_type(t); + return t->kind == Type_Maybe; +} +bool is_type_tuple(Type *t) { + t = base_type(t); + return t->kind == Type_Tuple; +} + + +bool is_type_int_or_uint(Type *t) { + if (t->kind == Type_Basic) { + return (t->Basic.kind == Basic_int) || (t->Basic.kind == Basic_uint); + } + return false; +} +bool is_type_rawptr(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_rawptr; + } + return false; +} +bool is_type_u8(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_u8; + } + return false; +} +bool is_type_array(Type *t) { + t = base_type(t); + return t->kind == Type_Array; +} +bool is_type_slice(Type *t) { + t = base_type(t); + return t->kind == Type_Slice; +} +bool is_type_u8_slice(Type *t) { + t = base_type(t); + if (t->kind == Type_Slice) { + return is_type_u8(t->Slice.elem); + } + return false; +} +bool is_type_vector(Type *t) { + t = base_type(t); + return t->kind == Type_Vector; +} +bool is_type_proc(Type *t) { + t = base_type(t); + return t->kind == Type_Proc; +} +Type *base_vector_type(Type *t) { + if (is_type_vector(t)) { + t = base_type(t); + return t->Vector.elem; + } + return t; +} + + +bool is_type_enum(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum); +} +bool is_type_struct(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct); +} +bool is_type_union(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union); +} +bool is_type_raw_union(Type *t) { + t = base_type(t); + return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion); +} + +bool is_type_any(Type *t) { + t = base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_any); +} +bool is_type_untyped_nil(Type *t) { + t = base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil); +} + + + +bool is_type_indexable(Type *t) { + return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t); +} + + +bool type_has_nil(Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Basic: + return is_type_rawptr(t); + + case Type_Tuple: + return false; + + case Type_Record: + switch (t->Record.kind) { + case TypeRecord_Enum: + return false; + } + break; + } + return true; +} + + +bool is_type_comparable(Type *t) { + t = base_type(get_enum_base_type(t)); + switch (t->kind) { + case Type_Basic: + return t->kind != Basic_UntypedNil; + case Type_Pointer: + return true; + case Type_Record: { + if (false && is_type_struct(t)) { + // TODO(bill): Should I even allow this? + for (isize i = 0; i < t->Record.field_count; i++) { + if (!is_type_comparable(t->Record.fields[i]->type)) + return false; + } + } else if (is_type_enum(t)) { + return is_type_comparable(t->Record.enum_base); + } + return false; + } break; + case Type_Array: + return is_type_comparable(t->Array.elem); + case Type_Vector: + return is_type_comparable(t->Vector.elem); + case Type_Proc: + return true; + } + return false; +} + +bool are_types_identical(Type *x, Type *y) { + if (x == y) + return true; + + if ((x == NULL && y != NULL) || + (x != NULL && y == NULL)) { + return false; + } + + switch (x->kind) { + case Type_Basic: + if (y->kind == Type_Basic) { + return x->Basic.kind == y->Basic.kind; + } + break; + + case Type_Array: + if (y->kind == Type_Array) { + return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem); + } + break; + + case Type_Vector: + if (y->kind == Type_Vector) { + return (x->Vector.count == y->Vector.count) && are_types_identical(x->Vector.elem, y->Vector.elem); + } + break; + + case Type_Slice: + if (y->kind == Type_Slice) { + return are_types_identical(x->Slice.elem, y->Slice.elem); + } + break; + + case Type_Record: + if (y->kind == Type_Record) { + if (x->Record.kind == y->Record.kind) { + switch (x->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + case TypeRecord_Union: + if (x->Record.field_count == y->Record.field_count && + x->Record.struct_is_packed == y->Record.struct_is_packed && + x->Record.struct_is_ordered == y->Record.struct_is_ordered) { + for (isize i = 0; i < x->Record.field_count; i++) { + if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) { + return false; + } + if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) { + return false; + } + } + return true; + } + break; + + case TypeRecord_Enum: + // NOTE(bill): Each enum is unique + return x == y; + } + } + } + break; + + case Type_Pointer: + if (y->kind == Type_Pointer) { + return are_types_identical(x->Pointer.elem, y->Pointer.elem); + } + break; + + case Type_Maybe: + if (y->kind == Type_Maybe) { + return are_types_identical(x->Maybe.elem, y->Maybe.elem); + } + break; + + case Type_Named: + if (y->kind == Type_Named) { + return x->Named.base == y->Named.base; + } + break; + + case Type_Tuple: + if (y->kind == Type_Tuple) { + if (x->Tuple.variable_count == y->Tuple.variable_count) { + for (isize i = 0; i < x->Tuple.variable_count; i++) { + if (!are_types_identical(x->Tuple.variables[i]->type, y->Tuple.variables[i]->type)) { + return false; + } + } + return true; + } + } + break; + + case Type_Proc: + if (y->kind == Type_Proc) { + return are_types_identical(x->Proc.params, y->Proc.params) && + are_types_identical(x->Proc.results, y->Proc.results); + } + break; + } + + + return false; +} + + +Type *default_type(Type *type) { + if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_UntypedBool: return t_bool; + case Basic_UntypedInteger: return t_int; + case Basic_UntypedFloat: return t_f64; + case Basic_UntypedString: return t_string; + case Basic_UntypedRune: return t_rune; + } + } + return type; +} + + + + +gb_global Entity *entity__any_type_info = NULL; +gb_global Entity *entity__any_data = NULL; +gb_global Entity *entity__string_data = NULL; +gb_global Entity *entity__string_count = NULL; +gb_global Entity *entity__slice_count = NULL; +gb_global Entity *entity__slice_capacity = NULL; + +Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel); + +Selection lookup_field(gbAllocator a, Type *type_, String field_name, bool is_type) { + return lookup_field_with_selection(a, type_, field_name, is_type, empty_selection); +} + +Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) { + GB_ASSERT(type_ != NULL); + + if (str_eq(field_name, str_lit("_"))) { + return empty_selection; + } + + Type *type = type_deref(type_); + bool is_ptr = type != type_; + sel.indirect = sel.indirect || is_ptr; + + type = base_type(type); + + if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + String type_info_str = str_lit("type_info"); + String data_str = str_lit("data"); + if (entity__any_type_info == NULL) { + entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0); + } + if (entity__any_data == NULL) { + entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1); + } + + if (str_eq(field_name, type_info_str)) { + selection_add_index(&sel, 0); + sel.entity = entity__any_type_info; + return sel; + } else if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 1); + sel.entity = entity__any_data; + return sel; + } + } break; + case Basic_string: { + String data_str = str_lit("data"); + String count_str = str_lit("count"); + if (entity__string_data == NULL) { + entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0); + } + + if (entity__string_count == NULL) { + entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1); + } + + if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 0); + sel.entity = entity__string_data; + return sel; + } else if (str_eq(field_name, count_str)) { + selection_add_index(&sel, 1); + sel.entity = entity__string_count; + return sel; + } + } break; + } + + return sel; + } else if (type->kind == Type_Array) { + String count_str = str_lit("count"); + // NOTE(bill): Underlying memory address cannot be changed + if (str_eq(field_name, count_str)) { + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count)); + return sel; + } + } else if (type->kind == Type_Vector) { + String count_str = str_lit("count"); + // NOTE(bill): Vectors are not addressable + if (str_eq(field_name, count_str)) { + // HACK(bill): Memory leak + sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count)); + return sel; + } + + if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) { + // HACK(bill): Memory leak + switch (type->Vector.count) { + #define _VECTOR_FIELD_CASE(_length, _name) \ + case (_length): \ + if (str_eq(field_name, str_lit(_name))) { \ + selection_add_index(&sel, (_length)-1); \ + sel.entity = make_entity_vector_elem(a, NULL, make_token_ident(str_lit(_name)), type->Vector.elem, (_length)-1); \ + return sel; \ + } \ + /*fallthrough*/ + + _VECTOR_FIELD_CASE(4, "w"); + _VECTOR_FIELD_CASE(3, "z"); + _VECTOR_FIELD_CASE(2, "y"); + _VECTOR_FIELD_CASE(1, "x"); + default: break; + + #undef _VECTOR_FIELD_CASE + } + } + + } else if (type->kind == Type_Slice) { + String data_str = str_lit("data"); + String count_str = str_lit("count"); + String capacity_str = str_lit("capacity"); + + if (str_eq(field_name, data_str)) { + selection_add_index(&sel, 0); + // HACK(bill): Memory leak + sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0); + return sel; + } else if (str_eq(field_name, count_str)) { + selection_add_index(&sel, 1); + if (entity__slice_count == NULL) { + entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1); + } + + sel.entity = entity__slice_count; + return sel; + } else if (str_eq(field_name, capacity_str)) { + selection_add_index(&sel, 2); + if (entity__slice_capacity == NULL) { + entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2); + } + + sel.entity = entity__slice_capacity; + return sel; + } + } + + if (type->kind != Type_Record) { + return sel; + } + if (is_type) { + if (is_type_union(type)) { + // NOTE(bill): The subtype for a union are stored in the fields + // as they are "kind of" like variables but not + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + String str = f->token.string; + + if (str_eq(field_name, str)) { + sel.entity = f; + selection_add_index(&sel, i); + return sel; + } + } + } + + for (isize i = 0; i < type->Record.other_field_count; i++) { + Entity *f = type->Record.other_fields[i]; + GB_ASSERT(f->kind != Entity_Variable); + String str = f->token.string; + + if (str_eq(field_name, str)) { + sel.entity = f; + selection_add_index(&sel, i); + return sel; + } + } + + if (is_type_enum(type)) { + if (str_eq(field_name, str_lit("count"))) { + sel.entity = type->Record.enum_count; + return sel; + } else if (str_eq(field_name, str_lit("min_value"))) { + sel.entity = type->Record.min_value; + return sel; + } else if (str_eq(field_name, str_lit("max_value"))) { + sel.entity = type->Record.max_value; + return sel; + } + } + + } else if (!is_type_enum(type) && !is_type_union(type)) { + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + String str = f->token.string; + if (str_eq(field_name, str)) { + selection_add_index(&sel, i); // HACK(bill): Leaky memory + sel.entity = f; + return sel; + } + + if (f->flags & EntityFlag_Anonymous) { + isize prev_count = sel.index.count; + selection_add_index(&sel, i); // HACK(bill): Leaky memory + + sel = lookup_field_with_selection(a, f->type, field_name, is_type, sel); + + if (sel.entity != NULL) { + if (is_type_pointer(f->type)) { + sel.indirect = true; + } + return sel; + } + sel.index.count = prev_count; + } + } + } + + return sel; +} + + + +i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t); +i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t); +i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index); + +i64 align_formula(i64 size, i64 align) { + if (align > 0) { + i64 result = size + align-1; + return result - result%align; + } + return size; +} + +i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + + switch (t->kind) { + case Type_Array: + return type_align_of(s, allocator, t->Array.elem); + case Type_Vector: { + i64 size = type_size_of(s, allocator, t->Vector.elem); + i64 count = gb_max(prev_pow2(t->Vector.count), 1); + i64 total = size * count; + return gb_clamp(total, 1, s.max_align); + } break; + + case Type_Tuple: { + i64 max = 1; + for (isize i = 0; i < t->Tuple.variable_count; i++) { + i64 align = type_align_of(s, allocator, t->Tuple.variables[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + + case Type_Maybe: + return gb_max(type_align_of(s, allocator, t->Maybe.elem), type_align_of(s, allocator, t_bool)); + + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (t->Record.field_count > 0) { + // TODO(bill): What is this supposed to be? + if (t->Record.struct_is_packed) { + i64 max = s.word_size; + for (isize i = 1; i < t->Record.field_count; i++) { + // NOTE(bill): field zero is null + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } + return type_align_of(s, allocator, t->Record.fields[0]->type); + } + break; + case TypeRecord_Union: { + i64 max = 1; + for (isize i = 1; i < t->Record.field_count; i++) { + // NOTE(bill): field zero is null + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + case TypeRecord_RawUnion: { + i64 max = 1; + for (isize i = 0; i < t->Record.field_count; i++) { + i64 align = type_align_of(s, allocator, t->Record.fields[i]->type); + if (max < align) { + max = align; + } + } + return max; + } break; + case TypeRecord_Enum: + return type_align_of(s, allocator, t->Record.enum_base); + } + } break; + } + + // return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align); + // NOTE(bill): Things that are bigger than s.word_size, are actually comprised of smaller types + // TODO(bill): Is this correct for 128-bit types (integers)? + return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.word_size); +} + +i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count, bool is_packed) { + i64 *offsets = gb_alloc_array(allocator, i64, field_count); + i64 curr_offset = 0; + if (is_packed) { + for (isize i = 0; i < field_count; i++) { + offsets[i] = curr_offset; + curr_offset += type_size_of(s, allocator, fields[i]->type); + } + + } else { + for (isize i = 0; i < field_count; i++) { + i64 align = type_align_of(s, allocator, fields[i]->type); + curr_offset = align_formula(curr_offset, align); + offsets[i] = curr_offset; + curr_offset += type_size_of(s, allocator, fields[i]->type); + } + } + return offsets; +} + +bool type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + if (is_type_struct(t)) { + if (!t->Record.struct_are_offsets_set) { + t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed); + t->Record.struct_are_offsets_set = true; + return true; + } + } else if (is_type_tuple(t)) { + if (!t->Tuple.are_offsets_set) { + t->Tuple.offsets = type_set_offsets_of(s, allocator, t->Tuple.variables, t->Tuple.variable_count, false); + t->Tuple.are_offsets_set = true; + return true; + } + } else { + GB_PANIC("Invalid type for setting offsets"); + } + return false; +} + +i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Basic: { + GB_ASSERT(is_type_typed(t)); + BasicKind kind = t->Basic.kind; + i64 size = t->Basic.size; + if (size > 0) { + return size; + } + switch (kind) { + case Basic_string: return 2*s.word_size; + case Basic_any: return 2*s.word_size; + + case Basic_int: case Basic_uint: case Basic_rawptr: + return s.word_size; + } + } break; + + case Type_Array: { + i64 count = t->Array.count; + if (count == 0) { + return 0; + } + i64 align = type_align_of(s, allocator, t->Array.elem); + i64 size = type_size_of(s, allocator, t->Array.elem); + i64 alignment = align_formula(size, align); + return alignment*(count-1) + size; + } break; + + case Type_Vector: { + i64 count = t->Vector.count; + if (count == 0) { + return 0; + } + // i64 align = type_align_of(s, allocator, t->Vector.elem); + i64 bit_size = 8*type_size_of(s, allocator, t->Vector.elem); + if (is_type_boolean(t->Vector.elem)) { + bit_size = 1; // NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1` + // Silly LLVM spec + } + i64 total_size_in_bits = bit_size * count; + i64 total_size = (total_size_in_bits+7)/8; + return total_size; + } break; + + + case Type_Slice: // ptr + len + cap + return 3 * s.word_size; + + case Type_Maybe: { // value + bool + Type *elem = t->Maybe.elem; + i64 align = type_align_of(s, allocator, elem); + i64 size = align_formula(type_size_of(s, allocator, elem), align); + size += type_size_of(s, allocator, t_bool); + return align_formula(size, align); + } + + case Type_Tuple: { + i64 count = t->Tuple.variable_count; + if (count == 0) { + return 0; + } + type_set_offsets(s, allocator, t); + i64 size = t->Tuple.offsets[count-1] + type_size_of(s, allocator, t->Tuple.variables[count-1]->type); + i64 align = type_align_of(s, allocator, t); + return align_formula(size, align); + } break; + + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + i64 count = t->Record.field_count; + if (count == 0) { + return 0; + } + type_set_offsets(s, allocator, t); + i64 size = t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type); + i64 align = type_align_of(s, allocator, t); + return align_formula(size, align); + } break; + + case TypeRecord_Union: { + i64 count = t->Record.field_count; + i64 max = 0; + // NOTE(bill): Zeroth field is invalid + for (isize i = 1; i < count; i++) { + i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); + if (max < size) { + max = size; + } + } + // NOTE(bill): Align to int + i64 align = type_align_of(s, allocator, t); + isize size = align_formula(max, s.word_size); + size += type_size_of(s, allocator, t_int); + return align_formula(size, align); + } break; + + case TypeRecord_RawUnion: { + i64 count = t->Record.field_count; + i64 max = 0; + for (isize i = 0; i < count; i++) { + i64 size = type_size_of(s, allocator, t->Record.fields[i]->type); + if (max < size) { + max = size; + } + } + // TODO(bill): Is this how it should work? + i64 align = type_align_of(s, allocator, t); + return align_formula(max, align); + } break; + + case TypeRecord_Enum: { + return type_size_of(s, allocator, t->Record.enum_base); + } break; + } + } break; + } + + // Catch all + return s.word_size; +} + +i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) { + t = base_type(t); + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->Record.field_count-1)) { + return t->Record.struct_offsets[index]; + } + } else if (t->kind == Type_Tuple) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->Tuple.variable_count-1)) { + return t->Tuple.offsets[index]; + } + } else if (t->kind == Type_Basic) { + if (t->Basic.kind == Basic_string) { + switch (index) { + case 0: return 0; + case 1: return s.word_size; + } + } else if (t->Basic.kind == Basic_any) { + switch (index) { + case 0: return 0; + case 1: return s.word_size; + } + } + } else if (t->kind == Type_Slice) { + switch (index) { + case 0: return 0; + case 1: return 1*s.word_size; + case 2: return 2*s.word_size; + } + } + return 0; +} + + +i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *type, Selection sel) { + GB_ASSERT(sel.indirect == false); + + Type *t = type; + i64 offset = 0; + for_array(i, sel.index) { + isize index = sel.index.e[i]; + t = base_type(t); + offset += type_offset_of(s, allocator, t, index); + if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { + t = t->Record.fields[index]->type; + } else { + // NOTE(bill): string/any/slices don't have record fields so this case doesn't need to be handled + } + } + return offset; +} + + + +gbString write_type_to_string(gbString str, Type *type) { + if (type == NULL) { + return gb_string_appendc(str, ""); + } + + switch (type->kind) { + case Type_Basic: + str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len); + break; + + case Type_Pointer: + str = gb_string_appendc(str, "^"); + str = write_type_to_string(str, type->Pointer.elem); + break; + + case Type_Maybe: + str = gb_string_appendc(str, "?"); + str = write_type_to_string(str, type->Maybe.elem); + break; + + case Type_Array: + str = gb_string_appendc(str, gb_bprintf("[%td]", type->Array.count)); + str = write_type_to_string(str, type->Array.elem); + break; + + case Type_Vector: + str = gb_string_appendc(str, gb_bprintf("{%td}", type->Vector.count)); + str = write_type_to_string(str, type->Vector.elem); + break; + + case Type_Slice: + str = gb_string_appendc(str, "[]"); + str = write_type_to_string(str, type->Array.elem); + break; + + case Type_Record: { + switch (type->Record.kind) { + case TypeRecord_Struct: + str = gb_string_appendc(str, "struct"); + if (type->Record.struct_is_packed) { + str = gb_string_appendc(str, " #packed"); + } + if (type->Record.struct_is_ordered) { + str = gb_string_appendc(str, " #ordered"); + } + str = gb_string_appendc(str, " {"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, "; "); + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_Union: + str = gb_string_appendc(str, "union{"); + for (isize i = 1; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_TypeName); + if (i > 1) { + str = gb_string_appendc(str, "; "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, base_type(f->type)); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_RawUnion: + str = gb_string_appendc(str, "raw_union{"); + for (isize i = 0; i < type->Record.field_count; i++) { + Entity *f = type->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); + } + str = gb_string_appendc(str, "}"); + break; + + case TypeRecord_Enum: + str = gb_string_appendc(str, "enum "); + str = write_type_to_string(str, type->Record.enum_base); + break; + } + } break; + + + case Type_Named: + if (type->Named.type_name != NULL) { + str = gb_string_append_length(str, type->Named.name.text, type->Named.name.len); + } else { + // NOTE(bill): Just in case + str = gb_string_appendc(str, ""); + } + break; + + case Type_Tuple: + if (type->Tuple.variable_count > 0) { + for (isize i = 0; i < type->Tuple.variable_count; i++) { + Entity *var = type->Tuple.variables[i]; + if (var != NULL) { + GB_ASSERT(var->kind == Entity_Variable); + if (i > 0) + str = gb_string_appendc(str, ", "); + str = write_type_to_string(str, var->type); + } + } + } + break; + + case Type_Proc: + str = gb_string_appendc(str, "proc("); + if (type->Proc.params) + str = write_type_to_string(str, type->Proc.params); + str = gb_string_appendc(str, ")"); + if (type->Proc.results) { + str = gb_string_appendc(str, " -> "); + str = write_type_to_string(str, type->Proc.results); + } + break; + } + + return str; +} + + +gbString type_to_string(Type *type) { + gbString str = gb_string_make(gb_heap_allocator(), ""); + return write_type_to_string(str, type); +} + + diff --git a/src/common.c b/src/common.c new file mode 100644 index 000000000..9b70722d1 --- /dev/null +++ b/src/common.c @@ -0,0 +1,195 @@ +#define GB_NO_DEFER +#define GB_IMPLEMENTATION +#include "gb/gb.h" + +gbAllocator heap_allocator(void) { + return gb_heap_allocator(); +} + +#include "string.c" +#include "array.c" + +gb_global String global_module_path = {0}; +gb_global bool global_module_path_set = false; + + +String get_module_dir() { + if (global_module_path_set) { + return global_module_path; + } + + Array(wchar_t) path_buf; + array_init_count(&path_buf, heap_allocator(), 300); + + isize len = 0; + for (;;) { + len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count); + if (len == 0) { + return make_string(NULL, 0); + } + if (len < path_buf.count) { + break; + } + array_resize(&path_buf, 2*path_buf.count + 300); + } + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1); + + GetModuleFileNameW(NULL, text, len); + String path = string16_to_string(heap_allocator(), make_string16(text, len)); + for (isize i = path.len-1; i >= 0; i--) { + u8 c = path.text[i]; + if (c == '/' || c == '\\') { + break; + } + path.len--; + } + + global_module_path = path; + global_module_path_set = true; + + gb_temp_arena_memory_end(tmp); + + array_free(&path_buf); + + return path; +} + +String path_to_fullpath(gbAllocator a, String s) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + String16 string16 = string_to_string16(string_buffer_allocator, s); + String result = {0}; + + DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL); + if (len != 0) { + wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1); + GetFullPathNameW(string16.text, len, text, NULL); + text[len] = 0; + result = string16_to_string(a, make_string16(text, len)); + } + gb_temp_arena_memory_end(tmp); + return result; +} + +i64 next_pow2(i64 n) { + if (n <= 0) { + return 0; + } + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n++; + return n; +} + +i64 prev_pow2(i64 n) { + if (n <= 0) { + return 0; + } + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + return n - (n >> 1); +} + +i16 f32_to_f16(f32 value) { + union { u32 i; f32 f; } v; + i32 i, s, e, m; + + v.f = value; + i = (i32)v.i; + + s = (i >> 16) & 0x00008000; + e = ((i >> 23) & 0x000000ff) - (127 - 15); + m = i & 0x007fffff; + + + if (e <= 0) { + if (e < -10) return cast(i16)s; + m = (m | 0x00800000) >> (1 - e); + + if (m & 0x00001000) + m += 0x00002000; + + return cast(i16)(s | (m >> 13)); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */ + } else { + /* NOTE(bill): NAN */ + m >>= 13; + return cast(i16)(s | 0x7c00 | m | (m == 0)); + } + } else { + if (m & 0x00001000) { + m += 0x00002000; + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + float volatile f = 1e12f; + int j; + for (j = 0; j < 10; j++) + f *= f; /* NOTE(bill): Cause overflow */ + + return cast(i16)(s | 0x7c00); + } + + return cast(i16)(s | (e << 10) | (m >> 13)); + } +} + + + +#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) + + +// Doubly Linked Lists + +#define DLIST_SET(curr_element, next_element) do { \ + (curr_element)->next = (next_element); \ + (curr_element)->next->prev = (curr_element); \ + (curr_element) = (curr_element)->next; \ +} while (0) + +#define DLIST_APPEND(root_element, curr_element, next_element) do { \ + if ((root_element) == NULL) { \ + (root_element) = (curr_element) = (next_element); \ + } else { \ + DLIST_SET(curr_element, next_element); \ + } \ +} while (0) + +//////////////////////////////////////////////////////////////// +// +// Generic Data Structures +// +//////////////////////////////////////////////////////////////// + + +#define MAP_TYPE String +#define MAP_PROC map_string_ +#define MAP_NAME MapString +#include "map.c" + +#define MAP_TYPE bool +#define MAP_PROC map_bool_ +#define MAP_NAME MapBool +#include "map.c" + +#define MAP_TYPE isize +#define MAP_PROC map_isize_ +#define MAP_NAME MapIsize +#include "map.c" diff --git a/src/exact_value.c b/src/exact_value.c new file mode 100644 index 000000000..313cda694 --- /dev/null +++ b/src/exact_value.c @@ -0,0 +1,400 @@ +#include + +// TODO(bill): Big numbers +// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!! + +typedef struct AstNode AstNode; + +typedef enum ExactValueKind { + ExactValue_Invalid, + + ExactValue_Bool, + ExactValue_String, + ExactValue_Integer, + ExactValue_Float, + ExactValue_Pointer, + ExactValue_Compound, // TODO(bill): Is this good enough? + + ExactValue_Count, +} ExactValueKind; + +typedef struct ExactValue { + ExactValueKind kind; + union { + bool value_bool; + String value_string; + i64 value_integer; // NOTE(bill): This must be an integer and not a pointer + f64 value_float; + i64 value_pointer; + AstNode *value_compound; + }; +} ExactValue; + +HashKey hash_exact_value(ExactValue v) { + return hashing_proc(&v, gb_size_of(ExactValue)); +} + + +ExactValue make_exact_value_compound(AstNode *node) { + ExactValue result = {ExactValue_Compound}; + result.value_compound = node; + return result; +} + +ExactValue make_exact_value_bool(bool b) { + ExactValue result = {ExactValue_Bool}; + result.value_bool = (b != 0); + return result; +} + +ExactValue make_exact_value_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_String}; + result.value_string = string; + return result; +} + +ExactValue make_exact_value_integer_from_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_Integer}; + i32 base = 10; + if (string.text[0] == '0') { + switch (string.text[1]) { + case 'b': base = 2; break; + case 'o': base = 8; break; + case 'd': base = 10; break; + case 'x': base = 16; break; + } + } + + result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base); + + return result; +} + +ExactValue make_exact_value_integer(i64 i) { + ExactValue result = {ExactValue_Integer}; + result.value_integer = i; + return result; +} + +ExactValue make_exact_value_float_from_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + ExactValue result = {ExactValue_Float}; + result.value_float = gb_str_to_f64(cast(char *)string.text, NULL); + return result; +} + +ExactValue make_exact_value_float(f64 f) { + ExactValue result = {ExactValue_Float}; + result.value_float = f; + return result; +} + +ExactValue make_exact_value_pointer(i64 ptr) { + ExactValue result = {ExactValue_Pointer}; + result.value_pointer = ptr; + return result; +} + +ExactValue make_exact_value_from_basic_literal(Token token) { + switch (token.kind) { + case Token_String: return make_exact_value_string(token.string); + case Token_Integer: return make_exact_value_integer_from_string(token.string); + case Token_Float: return make_exact_value_float_from_string(token.string); + case Token_Rune: { + Rune r = GB_RUNE_INVALID; + gb_utf8_decode(token.string.text, token.string.len, &r); + // gb_printf("%.*s rune: %d\n", LIT(token.string), r); + return make_exact_value_integer(r); + } + default: + GB_PANIC("Invalid token for basic literal"); + break; + } + + ExactValue result = {ExactValue_Invalid}; + return result; +} + +ExactValue exact_value_to_integer(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return v; + case ExactValue_Float: { + i64 i = cast(i64)v.value_float; + f64 f = cast(f64)i; + if (f == v.value_float) { + return make_exact_value_integer(i); + } + } break; + + case ExactValue_Pointer: + return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_to_float(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return make_exact_value_float(cast(i64)v.value_integer); + case ExactValue_Float: + return v; + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + + +ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) { + switch (op.kind) { + case Token_Add: { + switch (v.kind) { + case ExactValue_Invalid: + case ExactValue_Integer: + case ExactValue_Float: + return v; + } + } break; + + case Token_Sub: { + switch (v.kind) { + case ExactValue_Invalid: + return v; + case ExactValue_Integer: { + ExactValue i = v; + i.value_integer = -i.value_integer; + return i; + } + case ExactValue_Float: { + ExactValue i = v; + i.value_float = -i.value_float; + return i; + } + } + } break; + + case Token_Xor: { + i64 i = 0; + switch (v.kind) { + case ExactValue_Invalid: + return v; + case ExactValue_Integer: + i = v.value_integer; + i = ~i; + break; + default: + goto failure; + } + + // NOTE(bill): unsigned integers will be negative and will need to be + // limited to the types precision + if (precision > 0) + i &= ~((~0ll)<kind) { + case ExactValue_Invalid: + *y = *x; + return; + + case ExactValue_Bool: + case ExactValue_String: + return; + + case ExactValue_Integer: + switch (y->kind) { + case ExactValue_Integer: + return; + case ExactValue_Float: + // TODO(bill): Is this good enough? + *x = make_exact_value_float(cast(f64)x->value_integer); + return; + } + break; + + case ExactValue_Float: + if (y->kind == ExactValue_Float) + return; + break; + } + + compiler_error("How'd you get here? Invalid ExactValueKind"); +} + +// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough? +ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) { + match_exact_values(&x, &y); + + switch (x.kind) { + case ExactValue_Invalid: + return x; + + case ExactValue_Bool: + switch (op.kind) { + case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool); + case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool); + case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool); + case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool); + default: goto error; + } + break; + + case ExactValue_Integer: { + i64 a = x.value_integer; + i64 b = y.value_integer; + i64 c = 0; + switch (op.kind) { + case Token_Add: c = a + b; break; + case Token_Sub: c = a - b; break; + case Token_Mul: c = a * b; break; + case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b)); + case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division + case Token_Mod: c = a % b; break; + case Token_And: c = a & b; break; + case Token_Or: c = a | b; break; + case Token_Xor: c = a ^ b; break; + case Token_AndNot: c = a&(~b); break; + case Token_Shl: c = a << b; break; + case Token_Shr: c = a >> b; break; + default: goto error; + } + return make_exact_value_integer(c); + } break; + + case ExactValue_Float: { + f64 a = x.value_float; + f64 b = y.value_float; + switch (op.kind) { + case Token_Add: return make_exact_value_float(a + b); + case Token_Sub: return make_exact_value_float(a - b); + case Token_Mul: return make_exact_value_float(a * b); + case Token_Quo: return make_exact_value_float(a / b); + default: goto error; + } + } break; + } + +error: + ExactValue error_value = {0}; + // gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind)); + return error_value; +} + +gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo}; return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); } + + +i32 cmp_f64(f64 a, f64 b) { + return (a > b) - (a < b); +} + +bool compare_exact_values(Token op, ExactValue x, ExactValue y) { + match_exact_values(&x, &y); + + switch (x.kind) { + case ExactValue_Invalid: + return false; + + case ExactValue_Bool: + switch (op.kind) { + case Token_CmpEq: return x.value_bool == y.value_bool; + case Token_NotEq: return x.value_bool != y.value_bool; + } + break; + + case ExactValue_Integer: { + i64 a = x.value_integer; + i64 b = y.value_integer; + switch (op.kind) { + case Token_CmpEq: return a == b; + case Token_NotEq: return a != b; + case Token_Lt: return a < b; + case Token_LtEq: return a <= b; + case Token_Gt: return a > b; + case Token_GtEq: return a >= b; + } + } break; + + case ExactValue_Float: { + f64 a = x.value_float; + f64 b = y.value_float; + switch (op.kind) { + case Token_CmpEq: return cmp_f64(a, b) == 0; + case Token_NotEq: return cmp_f64(a, b) != 0; + case Token_Lt: return cmp_f64(a, b) < 0; + case Token_LtEq: return cmp_f64(a, b) <= 0; + case Token_Gt: return cmp_f64(a, b) > 0; + case Token_GtEq: return cmp_f64(a, b) >= 0; + } + } break; + + case ExactValue_String: { + String a = x.value_string; + String b = y.value_string; + isize len = gb_min(a.len, b.len); + // TODO(bill): gb_memcompare is used because the strings are UTF-8 + switch (op.kind) { + case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0; + case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0; + case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0; + case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0; + case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0; + case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0; + } + } break; + } + + GB_PANIC("Invalid comparison"); + return false; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..cf5d2379b --- /dev/null +++ b/src/main.c @@ -0,0 +1,272 @@ +#if defined(__cplusplus) +extern "C" { +#endif +#define VERSION_STRING "v0.0.3c" + +#include "common.c" +#include "timings.c" +#include "unicode.c" +#include "tokenizer.c" +#include "parser.c" +// #include "printer.c" +#include "checker/checker.c" +#include "ssa.c" +#include "ssa_opt.c" +#include "ssa_print.c" +// #include "vm.c" + +// NOTE(bill): `name` is used in debugging and profiling modes +i32 win32_exec_command_line_app(char *name, char *fmt, ...) { + STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)}; + PROCESS_INFORMATION pi = {0}; + char cmd_line[4096] = {0}; + isize cmd_len; + va_list va; + gbTempArenaMemory tmp; + String16 cmd; + i32 exit_code = 0; + + start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_SHOW; + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + va_start(va, fmt); + cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va); + va_end(va); + // gb_printf("%.*s\n", cast(int)cmd_len, cmd_line); + + tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1)); + + if (CreateProcessW(NULL, cmd.text, + NULL, NULL, true, 0, NULL, NULL, + &start_info, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } else { + // NOTE(bill): failed to create process + gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line); + exit_code = -1; + } + + gb_temp_arena_memory_end(tmp); + return exit_code; +} + +typedef enum ArchKind { + ArchKind_x64, + ArchKind_x86, +} ArchKind; + +typedef struct ArchData { + BaseTypeSizes sizes; + String llc_flags; + String link_flags; +} ArchData; + +ArchData make_arch_data(ArchKind kind) { + ArchData data = {0}; + + switch (kind) { + case ArchKind_x64: + default: + data.sizes.word_size = 8; + data.sizes.max_align = 16; + data.llc_flags = str_lit("-march=x86-64 "); + data.link_flags = str_lit("/machine:x64 "); + break; + + case ArchKind_x86: + data.sizes.word_size = 4; + data.sizes.max_align = 8; + data.llc_flags = str_lit("-march=x86 "); + data.link_flags = str_lit("/machine:x86 "); + break; + } + + return data; +} + +void usage(char *argv0) { + gb_printf_err("%s is a tool for managing Odin source code\n", argv0); + gb_printf_err("Usage:"); + gb_printf_err("\n\t%s command [arguments]\n", argv0); + gb_printf_err("Commands:"); + gb_printf_err("\n\tbuild compile .odin file"); + gb_printf_err("\n\trun compile and run .odin file"); + gb_printf_err("\n\tversion print Odin version"); + gb_printf_err("\n\n"); +} + +int main(int argc, char **argv) { + if (argc < 2) { + usage(argv[0]); + return 1; + } + + Timings timings = {0}; + timings_init(&timings, str_lit("Total Time"), 128); + // defer (timings_destroy(&timings)); + +#if 1 + init_string_buffer_memory(); + init_global_error_collector(); + + String module_dir = get_module_dir(); + + init_universal_scope(); + + char *init_filename = NULL; + bool run_output = false; + String arg1 = make_string_c(argv[1]); + if (str_eq(arg1, str_lit("run"))) { + run_output = true; + init_filename = argv[2]; + } else if (str_eq(arg1, str_lit("build"))) { + init_filename = argv[2]; + } else if (str_eq(arg1, str_lit("version"))) { + gb_printf("%s version %s", argv[0], VERSION_STRING); + return 0; + } else { + usage(argv[0]); + return 1; + } + + // TODO(bill): prevent compiling without a linker + + timings_start_section(&timings, str_lit("parse files")); + + Parser parser = {0}; + if (!init_parser(&parser)) { + return 1; + } + // defer (destroy_parser(&parser)); + + if (parse_files(&parser, init_filename) != ParseFile_None) { + return 1; + } + + +#if 1 + timings_start_section(&timings, str_lit("type check")); + + Checker checker = {0}; + ArchData arch_data = make_arch_data(ArchKind_x64); + + init_checker(&checker, &parser, arch_data.sizes); + // defer (destroy_checker(&checker)); + + check_parsed_files(&checker); + + +#endif +#if 1 + + ssaGen ssa = {0}; + if (!ssa_gen_init(&ssa, &checker)) { + return 1; + } + // defer (ssa_gen_destroy(&ssa)); + + timings_start_section(&timings, str_lit("ssa gen")); + ssa_gen_tree(&ssa); + + timings_start_section(&timings, str_lit("ssa opt")); + ssa_opt_tree(&ssa); + + timings_start_section(&timings, str_lit("ssa print")); + ssa_print_llvm_ir(&ssa); + + // prof_print_all(); + +#if 1 + timings_start_section(&timings, str_lit("llvm-opt")); + + char const *output_name = ssa.output_file.filename; + isize base_name_len = gb_path_extension(output_name)-1 - output_name; + String output = make_string(cast(u8 *)output_name, base_name_len); + + i32 optimization_level = 0; + optimization_level = gb_clamp(optimization_level, 0, 3); + + i32 exit_code = 0; + // For more passes arguments: http://llvm.org/docs/Passes.html + exit_code = win32_exec_command_line_app("llvm-opt", + "%.*sbin/opt %s -o %.*s.bc " + "-mem2reg " + "-memcpyopt " + "-die " + // "-dse " + // "-dce " + // "-S " + "", + LIT(module_dir), + output_name, LIT(output)); + if (exit_code != 0) { + return exit_code; + } + + #if 1 + timings_start_section(&timings, str_lit("llvm-llc")); + // For more arguments: http://llvm.org/docs/CommandGuide/llc.html + exit_code = win32_exec_command_line_app("llvm-llc", + "%.*sbin/llc %.*s.bc -filetype=obj -O%d " + "%.*s " + // "-debug-pass=Arguments " + "", + LIT(module_dir), + LIT(output), + optimization_level, + LIT(arch_data.llc_flags)); + if (exit_code != 0) { + return exit_code; + } + + timings_start_section(&timings, str_lit("msvc-link")); + + gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib"); + // defer (gb_string_free(lib_str)); + char lib_str_buf[1024] = {0}; + for_array(i, parser.foreign_libraries) { + String lib = parser.foreign_libraries.e[i]; + isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), + " %.*s.lib", LIT(lib)); + lib_str = gb_string_appendc(lib_str, lib_str_buf); + } + + exit_code = win32_exec_command_line_app("msvc-link", + "link %.*s.obj -OUT:%.*s.exe %s " + "/defaultlib:libcmt " + "/nologo /incremental:no /opt:ref /subsystem:console " + " %.*s " + "", + LIT(output), LIT(output), + lib_str, LIT(arch_data.link_flags)); + if (exit_code != 0) { + return exit_code; + } + + // timings_print_all(&timings); + + if (run_output) { + win32_exec_command_line_app("odin run", + "%.*s.exe", cast(int)base_name_len, output_name); + } + #endif +#endif +#endif +#endif + + + return 0; +} + +#if defined(__cplusplus) +} +#endif diff --git a/src/old_vm.c b/src/old_vm.c new file mode 100644 index 000000000..071af7ae3 --- /dev/null +++ b/src/old_vm.c @@ -0,0 +1,1305 @@ +// TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER +#include "dyncall/include/dyncall.h" + +struct VirtualMachine; + +struct vmValueProc { + ssaProcedure *proc; // If `NULL`, use `ptr` instead and call external procedure + void * ptr; +}; + + +struct vmValue { + // NOTE(bill): Shouldn't need to store type here as the type checking + // has already been handled in the SSA + union { + f32 val_f32; + f64 val_f64; + void * val_ptr; + i64 val_int; + vmValueProc val_proc; + }; + Array val_comp; // NOTE(bill): Will be freed through "stack" + Type *type; +}; + +vmValue vm_make_value_ptr(Type *type, void *ptr) { + GB_ASSERT(is_type_pointer(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_ptr = ptr; + return v; +} +vmValue vm_make_value_int(Type *type, i64 i) { + GB_ASSERT(is_type_integer(type) || + is_type_boolean(type) || + is_type_enum(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_int = i; + return v; +} +vmValue vm_make_value_f32(Type *type, f32 f) { + GB_ASSERT(is_type_f32(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_f32 = f; + return v; +} +vmValue vm_make_value_f64(Type *type, f64 f) { + GB_ASSERT(is_type_f64(type)); + vmValue v = {0}; + v.type = default_type(type); + v.val_f64 = f; + return v; +} +vmValue vm_make_value_comp(Type *type, gbAllocator allocator, isize count) { + GB_ASSERT(is_type_string(type) || + is_type_any (type) || + is_type_array (type) || + is_type_vector(type) || + is_type_slice (type) || + is_type_maybe (type) || + is_type_struct(type) || + is_type_union(type) || + is_type_raw_union(type) || + is_type_tuple (type) || + is_type_proc (type)); + vmValue v = {0}; + v.type = default_type(type); + array_init_count(&v.val_comp, allocator, count); + return v; +} + + + + + + +struct vmFrame { + VirtualMachine * vm; + vmFrame * caller; + ssaProcedure * curr_proc; + ssaBlock * prev_block; + ssaBlock * curr_block; + i32 instr_index; // For the current block + + Map values; // Key: ssaValue * + gbTempArenaMemory temp_arena_memory; + gbAllocator stack_allocator; + Array locals; // Memory to locals + vmValue result; +}; + +struct VirtualMachine { + ssaModule * module; + gbArena stack_arena; + gbAllocator stack_allocator; + gbAllocator heap_allocator; + Array frame_stack; + Map globals; // Key: ssaValue * + Map const_compound_lits; // Key: ssaValue * + vmValue exit_value; +}; + +void vm_exec_instr (VirtualMachine *vm, ssaValue *value); +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value); +void vm_store (VirtualMachine *vm, void *dst, vmValue val, Type *type); +vmValue vm_load (VirtualMachine *vm, void *ptr, Type *type); +void vm_print_value (vmValue value, Type *type); + +void vm_jump_block(vmFrame *f, ssaBlock *target) { + f->prev_block = f->curr_block; + f->curr_block = target; + f->instr_index = 0; +} + + +vmFrame *vm_back_frame(VirtualMachine *vm) { + if (vm->frame_stack.count > 0) { + return &vm->frame_stack[vm->frame_stack.count-1]; + } + return NULL; +} + +i64 vm_type_size_of(VirtualMachine *vm, Type *type) { + return type_size_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_align_of(VirtualMachine *vm, Type *type) { + return type_align_of(vm->module->sizes, vm->heap_allocator, type); +} +i64 vm_type_offset_of(VirtualMachine *vm, Type *type, i64 index) { + return type_offset_of(vm->module->sizes, vm->heap_allocator, type, index); +} + + +void vm_init(VirtualMachine *vm, ssaModule *module) { + gb_arena_init_from_allocator(&vm->stack_arena, heap_allocator(), gb_megabytes(64)); + + vm->module = module; + vm->stack_allocator = gb_arena_allocator(&vm->stack_arena); + vm->heap_allocator = heap_allocator(); + array_init(&vm->frame_stack, vm->heap_allocator); + map_init(&vm->globals, vm->heap_allocator); + map_init(&vm->const_compound_lits, vm->heap_allocator); + + for_array(i, vm->module->values.entries) { + ssaValue *v = vm->module->values.entries[i].value; + switch (v->kind) { + case ssaValue_Global: { + Type *t = ssa_type(v); + GB_ASSERT(is_type_pointer(t)); + i64 size = vm_type_size_of(vm, t); + i64 align = vm_type_align_of(vm, t); + void *mem = gb_alloc_align(vm->heap_allocator, size, align); + if (v->Global.value != NULL && v->Global.value->kind == ssaValue_Constant) { + vm_store(vm, mem, vm_operand_value(vm, v->Global.value), type_deref(t)); + } + map_set(&vm->globals, hash_pointer(v), vm_make_value_ptr(t, mem)); + } break; + } + } + +} +void vm_destroy(VirtualMachine *vm) { + array_free(&vm->frame_stack); + map_destroy(&vm->globals); + map_destroy(&vm->const_compound_lits); + gb_arena_free(&vm->stack_arena); +} + + + + + + +void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { + if (v != NULL) { + GB_ASSERT(ssa_type(v) != NULL); + map_set(&f->values, hash_pointer(v), val); + } +} + + + +vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) { + vmFrame frame = {0}; + + frame.vm = vm; + frame.curr_proc = proc; + frame.prev_block = proc->blocks[0]; + frame.curr_block = proc->blocks[0]; + frame.instr_index = 0; + frame.caller = vm_back_frame(vm); + frame.stack_allocator = vm->stack_allocator; + frame.temp_arena_memory = gb_temp_arena_memory_begin(&vm->stack_arena); + + map_init(&frame.values, vm->heap_allocator); + array_init(&frame.locals, vm->heap_allocator, proc->local_count); + array_add(&vm->frame_stack, frame); + return vm_back_frame(vm); +} + +void vm_pop_frame(VirtualMachine *vm) { + vmFrame *f = vm_back_frame(vm); + + gb_temp_arena_memory_end(f->temp_arena_memory); + array_free(&f->locals); + map_destroy(&f->values); + + array_pop(&vm->frame_stack); +} + + +vmValue vm_call_proc(VirtualMachine *vm, ssaProcedure *proc, Array values) { + Type *type = base_type(proc->type); + GB_ASSERT_MSG(type->Proc.param_count == values.count, + "Incorrect number of arguments passed into procedure call!\n" + "%.*s -> %td vs %td", + LIT(proc->name), + type->Proc.param_count, values.count); + Type *result_type = type->Proc.results; + if (result_type != NULL && + result_type->Tuple.variable_count == 1) { + result_type = result_type->Tuple.variables[0]->type; + } + + if (proc->body == NULL) { + // GB_PANIC("TODO(bill): external procedure"); + gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name)); + vmValue result = {0}; + result.type = result_type; + return result; + } + + void *result_mem = NULL; + if (result_type != NULL) { + result_mem = gb_alloc_align(vm->stack_allocator, + vm_type_size_of(vm, result_type), + vm_type_align_of(vm, result_type)); + } + + gb_printf("call: %.*s\n", LIT(proc->name)); + + vmFrame *f = vm_push_frame(vm, proc); + for_array(i, proc->params) { + vm_set_value(f, proc->params[i], values[i]); + } + + while (f->curr_block != NULL) { + ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++]; + vm_exec_instr(vm, curr_instr); + } + + + + + if (type->Proc.result_count > 0) { + vmValue r = f->result; + + gb_printf("%.*s -> ", LIT(proc->name)); + vm_print_value(r, result_type); + gb_printf("\n"); + + vm_store(vm, result_mem, r, result_type); + } + + vm_pop_frame(vm); + if (result_mem != NULL) { + return vm_load(vm, result_mem, result_type); + } + + vmValue void_result = {0}; + return void_result; +} + + +ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { + ssaValue *v = ssa_lookup_member(vm->module, name); + GB_ASSERT(v->kind == ssaValue_Proc); + return &v->Proc; +} + +vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array args) { + return vm_call_proc(vm, vm_lookup_procedure(vm, name), args); +} + +vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) { + Type *original_type = t; + t = base_type(get_enum_base_type(t)); + // i64 size = vm_type_size_of(vm, t); + if (is_type_boolean(t)) { + return vm_make_value_int(original_type, value.value_bool); + } else if (is_type_integer(t)) { + return vm_make_value_int(original_type, value.value_integer); + } else if (is_type_float(t)) { + if (t->Basic.kind == Basic_f32) { + return vm_make_value_f32(original_type, cast(f32)value.value_float); + } else if (t->Basic.kind == Basic_f64) { + return vm_make_value_f64(original_type, cast(f64)value.value_float); + } + } else if (is_type_pointer(t)) { + return vm_make_value_ptr(original_type, cast(void *)cast(intptr)value.value_pointer); + } else if (is_type_string(t)) { + vmValue result = vm_make_value_comp(original_type, vm->stack_allocator, 2); + + String str = value.value_string; + i64 len = str.len; + u8 *text = gb_alloc_array(vm->heap_allocator, u8, len); + gb_memcopy(text, str.text, len); + + result.val_comp[0] = vm_make_value_ptr(t_u8_ptr, text); + result.val_comp[1] = vm_make_value_int(t_int, len); + + return result; + } else if (value.kind == ExactValue_Compound) { + if (ptr != NULL) { + vmValue *found = map_get(&vm->const_compound_lits, hash_pointer(ptr)); + if (found != NULL) { + return *found; + } + } + + ast_node(cl, CompoundLit, value.value_compound); + + if (is_type_array(t)) { + vmValue result = {0}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_vector(t)) { + vmValue result = {0}; + + isize elem_count = cl->elems.count; + if (elem_count == 0) { + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + return result; + } + + Type *type = base_type(t); + result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count); + for (isize i = 0; i < elem_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); + result.val_comp[i] = elem; + } + + if (ptr != NULL) { + map_set(&vm->const_compound_lits, hash_pointer(ptr), result); + } + + return result; + } else if (is_type_struct(t)) { + ast_node(cl, CompoundLit, value.value_compound); + + isize value_count = t->Record.field_count; + vmValue result = vm_make_value_comp(t, vm->heap_allocator, value_count); + + if (cl->elems.count == 0) { + return result; + } + + if (cl->elems[0]->kind == AstNode_FieldValue) { + isize elem_count = cl->elems.count; + for (isize i = 0; i < elem_count; i++) { + ast_node(fv, FieldValue, cl->elems[i]); + String name = fv->field->Ident.string; + + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, fv->value); + GB_ASSERT(tav != NULL); + + Selection sel = lookup_field(vm->heap_allocator, t, name, false); + Entity *f = t->Record.fields[sel.index[0]]; + + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } else { + for (isize i = 0; i < value_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); + GB_ASSERT(tav != NULL); + Entity *f = t->Record.fields_in_src_order[i]; + result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type); + } + } + + return result; + } else { + GB_PANIC("TODO(bill): Other compound types\n"); + } + + } else if (value.kind == ExactValue_Invalid) { + vmValue zero_result = {0}; + zero_result.type = t; + return zero_result; + } else { + gb_printf_err("TODO(bill): Other constant types: %s\n", type_to_string(original_type)); + } + + GB_ASSERT_MSG(t == NULL, "%s - %d", type_to_string(t), value.kind); + vmValue void_result = {0}; + return void_result; +} + + +vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) { + vmFrame *f = vm_back_frame(vm); + vmValue v = {0}; + switch (value->kind) { + case ssaValue_Constant: { + v = vm_exact_value(vm, value, value->Constant.value, value->Constant.type); + } break; + case ssaValue_ConstantSlice: { + ssaValueConstant *cs = &value->ConstantSlice; + v = vm_make_value_comp(ssa_type(value), vm->stack_allocator, 3); + v.val_comp[0] = vm_operand_value(vm, cs->backing_array); + v.val_comp[1] = vm_make_value_int(t_int, cs->count); + v.val_comp[2] = vm_make_value_int(t_int, cs->count); + } break; + case ssaValue_Nil: + GB_PANIC("TODO(bill): ssaValue_Nil"); + break; + case ssaValue_TypeName: + GB_PANIC("ssaValue_TypeName has no operand value"); + break; + case ssaValue_Global: + v = *map_get(&vm->globals, hash_pointer(value)); + break; + case ssaValue_Param: + v = *map_get(&f->values, hash_pointer(value)); + break; + case ssaValue_Proc: { + v.type = ssa_type(value); + v.val_proc.proc = &value->Proc; + // GB_PANIC("TODO(bill): ssaValue_Proc"); + } break; + case ssaValue_Block: + GB_PANIC("ssaValue_Block has no operand value"); + break; + case ssaValue_Instr: { + vmValue *found = map_get(&f->values, hash_pointer(value)); + if (found) { + v = *found; + } else { + GB_PANIC("Invalid instruction"); + } + } break; + } + + return v; +} + +void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + Type *type = val.type; + GB_ASSERT_MSG(is_type_integer(type) || is_type_boolean(type), + "\nExpected integer/boolean, got %s (%s)", + type_to_string(type), + type_to_string(base_type(type))); + i64 size = vm_type_size_of(vm, type); + gb_memcopy(dst, &val.val_int, size); +} + +void vm_store_pointer(VirtualMachine *vm, void *dst, vmValue val) { + // TODO(bill): I assume little endian here + GB_ASSERT(dst != NULL); + GB_ASSERT(is_type_pointer(val.type)); + gb_memcopy(dst, &val.val_ptr, vm_type_size_of(vm, t_rawptr)); +} + + +void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + // NOTE(bill): enums are pretty much integers + type = base_type(get_enum_base_type(type)); + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + vm_store_integer(vm, dst, val); + break; + case Basic_f32: + *cast(f32 *)dst = val.val_f32; + break; + case Basic_f64: + *cast(f64 *)dst = val.val_f64; + break; + case Basic_rawptr: + vm_store_pointer(vm, dst, val); // NOTE(bill): A pointer can be treated as an integer + break; + case Basic_string: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + } break; + case Basic_any: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_pointer(vm, mem+1*word_size, val.val_comp[1]); + } break; + default: + gb_printf_err("TODO(bill): other basic types for `vm_store` %s\n", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + vm_store_pointer(vm, dst, val); + break; + + case Type_Record: { + u8 *mem = cast(u8 *)dst; + gb_zero_size(mem, size); + + if (is_type_struct(type)) { + GB_ASSERT_MSG(type->Record.field_count >= val.val_comp.count, + "%td vs %td", + type->Record.field_count, val.val_comp.count); + + isize field_count = gb_min(val.val_comp.count, type->Record.field_count); + + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + vm_store(vm, mem+offset, val.val_comp[i], f->type); + } + } else if (is_type_union(type)) { + GB_ASSERT(val.val_comp.count == 2); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + vm_store_integer(vm, mem + size_of_union, val.val_comp[1]); + + } else if (is_type_raw_union(type)) { + GB_ASSERT(val.val_comp.count == 1); + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + for (isize i = 0; i < size_of_union; i++) { + mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + } + } else { + GB_PANIC("Unknown record type: %s", type_to_string(type)); + } + } break; + + case Type_Tuple: { + u8 *mem = cast(u8 *)dst; + + GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count, + "%td vs %td", + type->Tuple.variable_count, val.val_comp.count); + + isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count); + + for (isize i = 0; i < variable_count; i++) { + Entity *f = type->Tuple.variables[i]; + void *ptr = mem + vm_type_offset_of(vm, type, i); + vm_store(vm, ptr, val.val_comp[i], f->type); + } + } break; + + case Type_Array: { + Type *elem_type = type->Array.elem; + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Vector: { + Type *elem_type = type->Array.elem; + GB_ASSERT_MSG(!is_type_boolean(elem_type), "TODO(bill): Booleans of vectors"); + u8 *mem = cast(u8 *)dst; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 elem_count = gb_min(val.val_comp.count, type->Array.count); + + for (i64 i = 0; i < elem_count; i++) { + vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type); + } + } break; + + case Type_Slice: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]); + vm_store_integer(vm, mem+1*word_size, val.val_comp[1]); + vm_store_integer(vm, mem+2*word_size, val.val_comp[2]); + } break; + + default: + gb_printf_err("TODO(bill): other types for `vm_store` %s\n", type_to_string(type)); + break; + } +} + +vmValue vm_load_integer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {0}; + v.type = type; + GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + +vmValue vm_load_pointer(VirtualMachine *vm, void *ptr, Type *type) { + // TODO(bill): I assume little endian here + vmValue v = {0}; + v.type = type; + GB_ASSERT(is_type_pointer(type)); + // NOTE(bill): Only load the needed amount + gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type)); + return v; +} + + +vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { + i64 size = vm_type_size_of(vm, type); + Type *original_type = type; + type = base_type(get_enum_base_type(type)); + + switch (type->kind) { + case Type_Basic: + switch (type->Basic.kind) { + case Basic_bool: + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_int: + case Basic_uint: + return vm_load_integer(vm, ptr, original_type); + case Basic_f32: + return vm_make_value_f32(original_type, *cast(f32 *)ptr); + case Basic_f64: + return vm_make_value_f64(original_type, *cast(f64 *)ptr); + case Basic_rawptr: + return vm_load_pointer(vm, ptr, original_type); + + + case Basic_string: { + u8 *mem = cast(u8 *)ptr; + i64 word_size = vm_type_size_of(vm, t_int); + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load_pointer(vm, mem+0*word_size, t_u8_ptr); + result.val_comp[1] = vm_load_integer(vm, mem+1*word_size, t_int); + return result; + } break; + + default: + GB_PANIC("TODO(bill): other basic types for `vm_load` %s", type_to_string(type)); + break; + } + break; + + case Type_Pointer: + return vm_load_pointer(vm, ptr, original_type); + + case Type_Array: { + i64 count = type->Array.count; + Type *elem_type = type->Array.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + i64 offset = elem_size*i; + vmValue val = vm_load(vm, mem+offset, elem_type); + result.val_comp[i] = val; + } + + return result; + } break; + + case Type_Slice: { + Type *elem_type = type->Slice.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 word_size = vm_type_size_of(vm, t_int); + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 3); + + u8 *mem = cast(u8 *)ptr; + result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data + result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int); // count + result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int); // capacity + return result; + } break; + + case Type_Record: { + if (is_type_struct(type)) { + isize field_count = type->Record.field_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, field_count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < field_count; i++) { + Entity *f = type->Record.fields[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + + return result; + } else if (is_type_union(type)) { + i64 word_size = vm_type_size_of(vm, t_int); + i64 size_of_union = vm_type_size_of(vm, type) - word_size; + u8 *mem = cast(u8 *)ptr; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2); + result.val_comp[0] = vm_load(vm, mem, make_type_array(vm->stack_allocator, t_u8, size_of_union)); + result.val_comp[1] = vm_load(vm, mem+size_of_union, t_int); + return result; + } else if (is_type_raw_union(type)) { + gb_printf_err("TODO(bill): load raw_union\n"); + } else { + gb_printf_err("TODO(bill): load other records\n"); + } + } break; + + case Type_Tuple: { + isize count = type->Tuple.variable_count; + + vmValue result = vm_make_value_comp(type, vm->stack_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + Entity *f = type->Tuple.variables[i]; + i64 offset = vm_type_offset_of(vm, type, i); + result.val_comp[i] = vm_load(vm, mem+offset, f->type); + } + return result; + } break; + + default: + GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type)); + break; + } + + GB_ASSERT(type == NULL); + vmValue void_result = {0}; + return void_result; +} + +vmValue vm_exec_binary_op(VirtualMachine *vm, Type *type, vmValue lhs, vmValue rhs, TokenKind op) { + vmValue result = {0}; + + type = base_type(type); + if (is_type_vector(type)) { + Type *elem = type->Vector.elem; + i64 count = type->Vector.count; + + result = vm_make_value_comp(type, vm->stack_allocator, count); + + for (i64 i = 0; i < count; i++) { + result.val_comp[i] = vm_exec_binary_op(vm, elem, lhs.val_comp[i], rhs.val_comp[i], op); + } + + return result; + } + + if (gb_is_between(op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_integer(type) || is_type_boolean(type)) { + // TODO(bill): Do I need to take into account the size of the integer? + switch (op) { + case Token_CmpEq: result.val_int = lhs.val_int == rhs.val_int; break; + case Token_NotEq: result.val_int = lhs.val_int != rhs.val_int; break; + case Token_Lt: result.val_int = lhs.val_int < rhs.val_int; break; + case Token_Gt: result.val_int = lhs.val_int > rhs.val_int; break; + case Token_LtEq: result.val_int = lhs.val_int <= rhs.val_int; break; + case Token_GtEq: result.val_int = lhs.val_int >= rhs.val_int; break; + } + } else if (type == t_f32) { + switch (op) { + case Token_CmpEq: result.val_f32 = lhs.val_f32 == rhs.val_f32; break; + case Token_NotEq: result.val_f32 = lhs.val_f32 != rhs.val_f32; break; + case Token_Lt: result.val_f32 = lhs.val_f32 < rhs.val_f32; break; + case Token_Gt: result.val_f32 = lhs.val_f32 > rhs.val_f32; break; + case Token_LtEq: result.val_f32 = lhs.val_f32 <= rhs.val_f32; break; + case Token_GtEq: result.val_f32 = lhs.val_f32 >= rhs.val_f32; break; + } + } else if (type == t_f64) { + switch (op) { + case Token_CmpEq: result.val_f64 = lhs.val_f64 == rhs.val_f64; break; + case Token_NotEq: result.val_f64 = lhs.val_f64 != rhs.val_f64; break; + case Token_Lt: result.val_f64 = lhs.val_f64 < rhs.val_f64; break; + case Token_Gt: result.val_f64 = lhs.val_f64 > rhs.val_f64; break; + case Token_LtEq: result.val_f64 = lhs.val_f64 <= rhs.val_f64; break; + case Token_GtEq: result.val_f64 = lhs.val_f64 >= rhs.val_f64; break; + } + } else if (is_type_string(type)) { + Array args = {0}; + array_init_count(&args, vm->stack_allocator, 2); + args[0] = lhs; + args[1] = rhs; + switch (op) { + case Token_CmpEq: result = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break; + case Token_NotEq: result = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break; + case Token_Lt: result = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break; + case Token_Gt: result = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break; + case Token_LtEq: result = vm_call_proc_by_name(vm, make_string("__string_le"), args); break; + case Token_GtEq: result = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break; + } + } else { + GB_PANIC("TODO(bill): Vector BinaryOp"); + } + } else { + if (is_type_integer(type) || is_type_boolean(type)) { + switch (op) { + case Token_Add: result.val_int = lhs.val_int + rhs.val_int; break; + case Token_Sub: result.val_int = lhs.val_int - rhs.val_int; break; + case Token_And: result.val_int = lhs.val_int & rhs.val_int; break; + case Token_Or: result.val_int = lhs.val_int | rhs.val_int; break; + case Token_Xor: result.val_int = lhs.val_int ^ rhs.val_int; break; + case Token_Shl: result.val_int = lhs.val_int << rhs.val_int; break; + case Token_Shr: result.val_int = lhs.val_int >> rhs.val_int; break; + case Token_Mul: result.val_int = lhs.val_int * rhs.val_int; break; + case Token_Not: result.val_int = lhs.val_int ^ rhs.val_int; break; + + case Token_AndNot: result.val_int = lhs.val_int & (~rhs.val_int); break; + + // TODO(bill): Take into account size of integer and signedness + case Token_Quo: GB_PANIC("TODO(bill): BinaryOp Integer Token_Quo"); break; + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp Integer Token_Mod"); break; + + } + } else if (is_type_float(type)) { + if (type == t_f32) { + switch (op) { + case Token_Add: result.val_f32 = lhs.val_f32 + rhs.val_f32; break; + case Token_Sub: result.val_f32 = lhs.val_f32 - rhs.val_f32; break; + case Token_Mul: result.val_f32 = lhs.val_f32 * rhs.val_f32; break; + case Token_Quo: result.val_f32 = lhs.val_f32 / rhs.val_f32; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f32 Token_Mod"); break; + } + } else if (type == t_f64) { + switch (op) { + case Token_Add: result.val_f64 = lhs.val_f64 + rhs.val_f64; break; + case Token_Sub: result.val_f64 = lhs.val_f64 - rhs.val_f64; break; + case Token_Mul: result.val_f64 = lhs.val_f64 * rhs.val_f64; break; + case Token_Quo: result.val_f64 = lhs.val_f64 / rhs.val_f64; break; + + case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f64 Token_Mod"); break; + } + } + } else { + GB_PANIC("Invalid binary op type"); + } + } + + return result; +} + +void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { + GB_ASSERT(value != NULL); + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + vmFrame *f = vm_back_frame(vm); + +#if 0 + if (instr->kind != ssaInstr_Comment) { + gb_printf("exec_instr: %.*s\n", LIT(ssa_instr_strings[instr->kind])); + } +#endif + + switch (instr->kind) { + case ssaInstr_StartupRuntime: { +#if 1 + Array args = {0}; // Empty + vm_call_proc_by_name(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME), args); // NOTE(bill): No return value +#endif + } break; + + case ssaInstr_Comment: + break; + + case ssaInstr_Local: { + Type *type = ssa_type(value); + GB_ASSERT(is_type_pointer(type)); + isize size = gb_max(1, vm_type_size_of(vm, type)); + isize align = gb_max(1, vm_type_align_of(vm, type)); + void *memory = gb_alloc_align(vm->stack_allocator, size, align); + GB_ASSERT(memory != NULL); + vm_set_value(f, value, vm_make_value_ptr(type, memory)); + array_add(&f->locals, memory); + } break; + + case ssaInstr_ZeroInit: { + Type *t = type_deref(ssa_type(instr->ZeroInit.address)); + vmValue addr = vm_operand_value(vm, instr->ZeroInit.address); + void *data = addr.val_ptr; + i64 size = vm_type_size_of(vm, t); + gb_zero_size(data, size); + } break; + + case ssaInstr_Store: { + vmValue addr = vm_operand_value(vm, instr->Store.address); + vmValue val = vm_operand_value(vm, instr->Store.value); + GB_ASSERT(val.type != NULL); + Type *t = type_deref(ssa_type(instr->Store.address)); + vm_store(vm, addr.val_ptr, val, t); + } break; + + case ssaInstr_Load: { + vmValue addr = vm_operand_value(vm, instr->Load.address); + Type *t = ssa_type(value); + vmValue v = vm_load(vm, addr.val_ptr, t); + vm_set_value(f, value, v); + } break; + + case ssaInstr_ArrayElementPtr: { + vmValue address = vm_operand_value(vm, instr->ArrayElementPtr.address); + vmValue elem_index = vm_operand_value(vm, instr->ArrayElementPtr.elem_index); + + Type *t = ssa_type(instr->ArrayElementPtr.address); + GB_ASSERT(is_type_pointer(t)); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + void *ptr = cast(u8 *)address.val_ptr + elem_index.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_StructElementPtr: { + vmValue address = vm_operand_value(vm, instr->StructElementPtr.address); + i32 elem_index = instr->StructElementPtr.elem_index; + + Type *t = ssa_type(instr->StructElementPtr.address); + GB_ASSERT(is_type_pointer(t)); + i64 offset = vm_type_offset_of(vm, type_deref(t), elem_index); + void *ptr = cast(u8 *)address.val_ptr + offset; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_PtrOffset: { + Type *t = ssa_type(instr->PtrOffset.address); + GB_ASSERT(is_type_pointer(t)); + i64 elem_size = vm_type_size_of(vm, type_deref(t)); + vmValue address = vm_operand_value(vm, instr->PtrOffset.address); + vmValue offset = vm_operand_value(vm, instr->PtrOffset.offset); + + void *ptr = cast(u8 *)address.val_ptr + offset.val_int*elem_size; + vm_set_value(f, value, vm_make_value_ptr(t, ptr)); + } break; + + case ssaInstr_Phi: { + for_array(i, f->curr_block->preds) { + ssaBlock *pred = f->curr_block->preds[i]; + if (f->prev_block == pred) { + vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]); + vm_set_value(f, value, edge); + break; + } + } + } break; + + case ssaInstr_ArrayExtractValue: { + vmValue s = vm_operand_value(vm, instr->ArrayExtractValue.address); + vmValue v = s.val_comp[instr->ArrayExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_StructExtractValue: { + vmValue s = vm_operand_value(vm, instr->StructExtractValue.address); + vmValue v = s.val_comp[instr->StructExtractValue.index]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_Jump: { + vm_jump_block(f, instr->Jump.block); + } break; + + case ssaInstr_If: { + vmValue cond = vm_operand_value(vm, instr->If.cond); + if (cond.val_int != 0) { + vm_jump_block(f, instr->If.true_block); + } else { + vm_jump_block(f, instr->If.false_block); + } + } break; + + case ssaInstr_Return: { + Type *return_type = NULL; + vmValue result = {0}; + + if (instr->Return.value != NULL) { + return_type = ssa_type(instr->Return.value); + result = vm_operand_value(vm, instr->Return.value); + } + + f->result = result; + f->curr_block = NULL; + f->instr_index = 0; + return; + } break; + + case ssaInstr_Conv: { + // TODO(bill): Assuming little endian + vmValue dst = {0}; + vmValue src = vm_operand_value(vm, instr->Conv.value); + i64 from_size = vm_type_size_of(vm, instr->Conv.from); + i64 to_size = vm_type_size_of(vm, instr->Conv.to); + switch (instr->Conv.kind) { + case ssaConv_trunc: + gb_memcopy(&dst, &src, to_size); + break; + case ssaConv_zext: + gb_memcopy(&dst, &src, from_size); + break; + case ssaConv_fptrunc: { + GB_ASSERT(from_size > to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f64); + GB_ASSERT(base_type(instr->Conv.to) == t_f32); + dst.val_f32 = cast(f32)src.val_f64; + } break; + case ssaConv_fpext: { + GB_ASSERT(from_size < to_size); + GB_ASSERT(base_type(instr->Conv.from) == t_f32); + GB_ASSERT(base_type(instr->Conv.to) == t_f64); + dst.val_f64 = cast(f64)src.val_f32; + } break; + case ssaConv_fptoui: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + u64 u = cast(u64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } else { + u64 u = cast(u64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u)); + } + } break; + case ssaConv_fptosi: { + Type *from = base_type(instr->Conv.from); + if (from == t_f64) { + i64 i = cast(i64)src.val_f64; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } else { + i64 i = cast(i64)src.val_f32; + vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i)); + } + } break; + case ssaConv_uitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(u64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(u64)src.val_int); + } + } break; + case ssaConv_sitofp: { + Type *to = base_type(instr->Conv.to); + if (to == t_f64) { + dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(i64)src.val_int); + } else { + dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(i64)src.val_int); + } + } break; + + case ssaConv_ptrtoint: + dst = vm_make_value_int(instr->Conv.to, cast(i64)src.val_ptr); + break; + case ssaConv_inttoptr: + dst = vm_make_value_ptr(instr->Conv.to, cast(void *)src.val_int); + break; + case ssaConv_bitcast: + dst = src; + dst.type = instr->Conv.to; + break; + } + + vm_set_value(f, value, dst); + } break; + + case ssaInstr_Unreachable: { + GB_PANIC("Unreachable"); + } break; + + case ssaInstr_BinaryOp: { + ssaInstrBinaryOp *bo = &instr->BinaryOp; + Type *type = ssa_type(bo->left); + vmValue lhs = vm_operand_value(vm, bo->left); + vmValue rhs = vm_operand_value(vm, bo->right); + vmValue v = vm_exec_binary_op(vm, type, lhs, rhs, bo->op); + vm_set_value(f, value, v); + } break; + + case ssaInstr_Call: { + Array args = {0}; + array_init(&args, f->stack_allocator, instr->Call.arg_count); + for (isize i = 0; i < instr->Call.arg_count; i++) { + array_add(&args, vm_operand_value(vm, instr->Call.args[i])); + } + vmValue proc = vm_operand_value(vm, instr->Call.value); + if (proc.val_proc.proc != NULL) { + vmValue result = vm_call_proc(vm, proc.val_proc.proc, args); + vm_set_value(f, value, result); + } else { + GB_PANIC("TODO(bill): external procedure calls"); + } + + } break; + + case ssaInstr_Select: { + vmValue v = {0}; + vmValue cond = vm_operand_value(vm, instr->Select.cond); + if (cond.val_int != 0) { + v = vm_operand_value(vm, instr->Select.true_value); + } else { + v = vm_operand_value(vm, instr->Select.false_value); + } + + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorExtractElement: { + vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector); + vmValue index = vm_operand_value(vm, instr->VectorExtractElement.index); + vmValue v = vector.val_comp[index.val_int]; + vm_set_value(f, value, v); + } break; + + case ssaInstr_VectorInsertElement: { + vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector); + vmValue elem = vm_operand_value(vm, instr->VectorInsertElement.elem); + vmValue index = vm_operand_value(vm, instr->VectorInsertElement.index); + vector.val_comp[index.val_int] = elem; + } break; + + case ssaInstr_VectorShuffle: { + ssaValueVectorShuffle *vs = &instr->VectorShuffle; + vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector); + vmValue new_vector = vm_make_value_comp(ssa_type(value), vm->stack_allocator, vs->index_count); + + for (i32 i = 0; i < vs->index_count; i++) { + new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]]; + } + + vm_set_value(f, value, new_vector); + } break; + + case ssaInstr_BoundsCheck: { + ssaInstrBoundsCheck *bc = &instr->BoundsCheck; + Array args = {0}; + array_init(&args, vm->stack_allocator, 5); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->index)); + array_add(&args, vm_operand_value(vm, bc->len)); + + vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args); + } break; + + case ssaInstr_SliceBoundsCheck: { + ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; + Array args = {0}; + + array_init(&args, vm->stack_allocator, 7); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int)); + array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int)); + array_add(&args, vm_operand_value(vm, bc->low)); + array_add(&args, vm_operand_value(vm, bc->high)); + if (!bc->is_substring) { + array_add(&args, vm_operand_value(vm, bc->max)); + vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args); + } else { + vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args); + } + } break; + + default: { + GB_PANIC(" %d\n", instr->kind); + } break; + } +} + + + +void vm_print_value(vmValue value, Type *type) { + type = base_type(type); + if (is_type_string(type)) { + vmValue data = value.val_comp[0]; + vmValue count = value.val_comp[1]; + gb_printf("`%.*s`", cast(int)count.val_int, cast(u8 *)data.val_ptr); + } else if (is_type_boolean(type)) { + if (value.val_int != 0) { + gb_printf("true"); + } else { + gb_printf("false"); + } + } else if (is_type_integer(type)) { + gb_printf("%lld", cast(i64)value.val_int); + } else if (type == t_f32) { + gb_printf("%f", value.val_f32); + } else if (type == t_f64) { + gb_printf("%f", value.val_f64); + } else if (is_type_pointer(type)) { + gb_printf("0x%08x", value.val_ptr); + } else if (is_type_array(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Array.elem); + } + gb_printf("]"); + } else if (is_type_vector(type)) { + gb_printf("<"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Vector.elem); + } + gb_printf(">"); + } else if (is_type_slice(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Slice.elem); + } + gb_printf("]"); + } else if (is_type_maybe(type)) { + if (value.val_comp[1].val_int != 0) { + gb_printf("?"); + vm_print_value(value.val_comp[0], type->Maybe.elem); + } else { + gb_printf("nil"); + } + } else if (is_type_struct(type)) { + if (value.val_comp.count == 0) { + gb_printf("nil"); + } else { + gb_printf("{"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Record.fields[i]->type); + } + gb_printf("}"); + } + } else if (is_type_tuple(type)) { + if (value.val_comp.count != 1) { + gb_printf("("); + } + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type); + } + if (value.val_comp.count != 1) { + gb_printf(")"); + } + } +} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 000000000..1ea8ba430 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,3250 @@ +typedef struct AstNode AstNode; +typedef struct Scope Scope; +typedef struct DeclInfo DeclInfo; + +typedef enum ParseFileError { + ParseFile_None, + + ParseFile_WrongExtension, + ParseFile_InvalidFile, + ParseFile_EmptyFile, + ParseFile_Permission, + ParseFile_NotFound, + ParseFile_InvalidToken, + + ParseFile_Count, +} ParseFileError; + +typedef Array(AstNode *) AstNodeArray; + +typedef struct AstFile { + i32 id; + gbArena arena; + Tokenizer tokenizer; + Array(Token) tokens; + isize curr_token_index; + Token curr_token; + Token prev_token; // previous non-comment + + // >= 0: In Expression + // < 0: In Control Clause + // NOTE(bill): Used to prevent type literals in control clauses + isize expr_level; + + AstNodeArray decls; + bool is_global_scope; + + AstNode * curr_proc; + isize scope_level; + Scope * scope; // NOTE(bill): Created in checker + DeclInfo * decl_info; // NOTE(bill): Created in checker + + // TODO(bill): Error recovery +#define PARSER_MAX_FIX_COUNT 6 + isize fix_count; + TokenPos fix_prev_pos; +} AstFile; + +typedef struct ImportedFile { + String path; + String rel_path; + TokenPos pos; // #import +} ImportedFile; + +typedef struct Parser { + String init_fullpath; + Array(AstFile) files; + Array(ImportedFile) imports; + gbAtomic32 import_index; + Array(String) foreign_libraries; + isize total_token_count; + gbMutex mutex; +} Parser; + +typedef enum ProcTag { + ProcTag_bounds_check = GB_BIT(0), + ProcTag_no_bounds_check = GB_BIT(1), + + ProcTag_foreign = GB_BIT(10), + ProcTag_link_name = GB_BIT(11), + ProcTag_inline = GB_BIT(12), + ProcTag_no_inline = GB_BIT(13), + ProcTag_dll_import = GB_BIT(14), + ProcTag_dll_export = GB_BIT(15), + + ProcTag_stdcall = GB_BIT(16), + ProcTag_fastcall = GB_BIT(17), + // ProcTag_cdecl = GB_BIT(18), +} ProcTag; + +typedef enum VarDeclTag { + VarDeclTag_thread_local = GB_BIT(0), +} VarDeclTag; + +typedef enum StmtStateFlag { + StmtStateFlag_bounds_check = GB_BIT(0), + StmtStateFlag_no_bounds_check = GB_BIT(1), +} StmtStateFlag; + + +typedef enum CallExprKind { + CallExpr_Prefix, // call(...) + CallExpr_Postfix, // a'call + CallExpr_Infix, // a ''call b +} CallExprKind; + +AstNodeArray make_ast_node_array(AstFile *f) { + AstNodeArray a; + array_init(&a, gb_arena_allocator(&f->arena)); + return a; +} + + +#define AST_NODE_KINDS \ + AST_NODE_KIND(BasicLit, "basic literal", Token) \ + AST_NODE_KIND(Ident, "identifier", Token) \ + AST_NODE_KIND(Ellipsis, "ellipsis", struct { \ + Token token; \ + AstNode *expr; \ + }) \ + AST_NODE_KIND(ProcLit, "procedure literal", struct { \ + AstNode *type; \ + AstNode *body; \ + u64 tags; \ + }) \ + AST_NODE_KIND(CompoundLit, "compound literal", struct { \ + AstNode *type; \ + AstNodeArray elems; \ + Token open, close; \ + }) \ +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; }) \ + AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \ + AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \ + AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \ + AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ + AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ + AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(DemaybeExpr, "demaybe expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(CallExpr, "call expression", struct { \ + AstNode *proc; \ + AstNodeArray args; \ + Token open, close; \ + Token ellipsis; \ + CallExprKind kind; \ + }) \ + AST_NODE_KIND(SliceExpr, "slice expression", struct { \ + AstNode *expr; \ + Token open, close; \ + AstNode *low, *high, *max; \ + bool triple_indexed; \ + }) \ + AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ +AST_NODE_KIND(_ExprEnd, "", i32) \ +AST_NODE_KIND(_StmtBegin, "", i32) \ + AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ + AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \ + AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \ + AST_NODE_KIND(IncDecStmt, "increment/decrement statement", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(TagStmt, "tag statement", struct { \ + Token token; \ + Token name; \ + AstNode *stmt; \ + }) \ + AST_NODE_KIND(AssignStmt, "assign statement", struct { \ + Token op; \ + AstNodeArray lhs, rhs; \ + }) \ +AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ + AST_NODE_KIND(BlockStmt, "block statement", struct { \ + AstNodeArray stmts; \ + Token open, close; \ + }) \ + AST_NODE_KIND(IfStmt, "if statement", struct { \ + Token token; \ + AstNode *init; \ + AstNode *cond; \ + AstNode *body; \ + AstNode *else_stmt; \ + }) \ + AST_NODE_KIND(ReturnStmt, "return statement", struct { \ + Token token; \ + AstNodeArray results; \ + }) \ + AST_NODE_KIND(ForStmt, "for statement", struct { \ + Token token; \ + AstNode *init, *cond, *post; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(CaseClause, "case clause", struct { \ + Token token; \ + AstNodeArray list, stmts; \ + }) \ + AST_NODE_KIND(MatchStmt, "match statement", struct { \ + Token token; \ + AstNode *init, *tag; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \ + Token token; \ + AstNode *tag, *var; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ + AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ + AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \ + AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ + Token string; \ + AstNode *operand; \ + }) \ + AST_NODE_KIND(AsmStmt, "assembly statement", struct { \ + Token token; \ + bool is_volatile; \ + Token open, close; \ + Token code_string; \ + AstNode *output_list; \ + AstNode *input_list; \ + AstNode *clobber_list; \ + isize output_count, input_count, clobber_count; \ + }) \ + AST_NODE_KIND(PushAllocator, "push_allocator statement", struct { \ + Token token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ + AST_NODE_KIND(PushContext, "push_context statement", struct { \ + Token token; \ + AstNode *expr; \ + AstNode *body; \ + }) \ +\ +AST_NODE_KIND(_ComplexStmtEnd, "", i32) \ +AST_NODE_KIND(_StmtEnd, "", i32) \ +AST_NODE_KIND(_DeclBegin, "", i32) \ + AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \ + AST_NODE_KIND(VarDecl, "variable declaration", struct { \ + u64 tags; \ + bool is_using; \ + AstNodeArray names; \ + AstNode * type; \ + AstNodeArray values; \ + AstNode * note; \ + }) \ + AST_NODE_KIND(ConstDecl, "constant declaration", struct { \ + u64 tags; \ + AstNodeArray names; \ + AstNode * type; \ + AstNodeArray values; \ + AstNode * note; \ + }) \ + AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \ + AstNode *name; \ + AstNode *type; \ + AstNode *body; \ + u64 tags; \ + String foreign_name; \ + String link_name; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(TypeDecl, "type declaration", struct { \ + Token token; \ + AstNode *name, *type; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(ImportDecl, "import declaration", struct { \ + Token token, relpath; \ + String fullpath; \ + Token import_name; \ + bool is_load; \ + AstNode *note; \ + }) \ + AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \ + Token token, filepath; \ + bool is_system; \ + }) \ +AST_NODE_KIND(_DeclEnd, "", i32) \ +AST_NODE_KIND(_TypeBegin, "", i32) \ + AST_NODE_KIND(Parameter, "parameter", struct { \ + AstNodeArray names; \ + AstNode *type; \ + bool is_using; \ + }) \ + AST_NODE_KIND(ProcType, "procedure type", struct { \ + Token token; \ + AstNodeArray params; \ + AstNodeArray results; \ + }) \ + AST_NODE_KIND(PointerType, "pointer type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(MaybeType, "maybe type", struct { \ + Token token; \ + AstNode *type; \ + }) \ + AST_NODE_KIND(ArrayType, "array type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(VectorType, "vector type", struct { \ + Token token; \ + AstNode *count; \ + AstNode *elem; \ + }) \ + AST_NODE_KIND(StructType, "struct type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + bool is_packed; \ + bool is_ordered; \ + }) \ + AST_NODE_KIND(UnionType, "union type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + }) \ + AST_NODE_KIND(RawUnionType, "raw union type", struct { \ + Token token; \ + AstNodeArray decls; \ + isize decl_count; \ + }) \ + AST_NODE_KIND(EnumType, "enum type", struct { \ + Token token; \ + AstNode *base_type; \ + AstNodeArray fields; \ + }) \ +AST_NODE_KIND(_TypeEnd, "", i32) + +typedef enum AstNodeKind { + AstNode_Invalid, +#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_), + AST_NODE_KINDS +#undef AST_NODE_KIND + AstNode_Count, +} AstNodeKind; + +String const ast_node_strings[] = { + {cast(u8 *)"invalid node", gb_size_of("invalid node")}, +#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1}, + AST_NODE_KINDS +#undef AST_NODE_KIND +}; + +#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_); + AST_NODE_KINDS +#undef AST_NODE_KIND + +typedef struct AstNode { + AstNodeKind kind; + // AstNode *prev, *next; // NOTE(bill): allow for Linked list + u32 stmt_state_flags; + union { +#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; + AST_NODE_KINDS +#undef AST_NODE_KIND + }; +} AstNode; + + +#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_)) +#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_); +#define case_end } break; + + + + +gb_inline bool is_ast_node_expr(AstNode *node) { + return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); +} +gb_inline bool is_ast_node_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1); +} +gb_inline bool is_ast_node_complex_stmt(AstNode *node) { + return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1); +} +gb_inline bool is_ast_node_decl(AstNode *node) { + return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1); +} +gb_inline bool is_ast_node_type(AstNode *node) { + return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1); +} + + +Token ast_node_token(AstNode *node) { + switch (node->kind) { + case AstNode_BasicLit: + return node->BasicLit; + case AstNode_Ident: + return node->Ident; + case AstNode_ProcLit: + return ast_node_token(node->ProcLit.type); + case AstNode_CompoundLit: + if (node->CompoundLit.type != NULL) { + return ast_node_token(node->CompoundLit.type); + } + return node->CompoundLit.open; + case AstNode_TagExpr: + return node->TagExpr.token; + case AstNode_RunExpr: + return node->RunExpr.token; + case AstNode_BadExpr: + return node->BadExpr.begin; + case AstNode_UnaryExpr: + return node->UnaryExpr.op; + case AstNode_BinaryExpr: + return ast_node_token(node->BinaryExpr.left); + case AstNode_ParenExpr: + return node->ParenExpr.open; + case AstNode_CallExpr: + return ast_node_token(node->CallExpr.proc); + case AstNode_SelectorExpr: + return ast_node_token(node->SelectorExpr.selector); + case AstNode_IndexExpr: + return node->IndexExpr.open; + case AstNode_SliceExpr: + return node->SliceExpr.open; + case AstNode_Ellipsis: + return node->Ellipsis.token; + case AstNode_FieldValue: + return node->FieldValue.eq; + case AstNode_DerefExpr: + return node->DerefExpr.op; + case AstNode_DemaybeExpr: + return node->DemaybeExpr.op; + case AstNode_BadStmt: + return node->BadStmt.begin; + case AstNode_EmptyStmt: + return node->EmptyStmt.token; + case AstNode_ExprStmt: + return ast_node_token(node->ExprStmt.expr); + case AstNode_TagStmt: + return node->TagStmt.token; + case AstNode_IncDecStmt: + return node->IncDecStmt.op; + case AstNode_AssignStmt: + return node->AssignStmt.op; + case AstNode_BlockStmt: + return node->BlockStmt.open; + case AstNode_IfStmt: + return node->IfStmt.token; + case AstNode_ReturnStmt: + return node->ReturnStmt.token; + case AstNode_ForStmt: + return node->ForStmt.token; + case AstNode_MatchStmt: + return node->MatchStmt.token; + case AstNode_CaseClause: + return node->CaseClause.token; + case AstNode_DeferStmt: + return node->DeferStmt.token; + case AstNode_BranchStmt: + return node->BranchStmt.token; + case AstNode_UsingStmt: + return node->UsingStmt.token; + case AstNode_AsmStmt: + return node->AsmStmt.token; + case AstNode_PushAllocator: + return node->PushAllocator.token; + case AstNode_PushContext: + return node->PushContext.token; + case AstNode_BadDecl: + return node->BadDecl.begin; + case AstNode_VarDecl: + return ast_node_token(node->VarDecl.names.e[0]); + case AstNode_ConstDecl: + return ast_node_token(node->ConstDecl.names.e[0]); + case AstNode_ProcDecl: + return node->ProcDecl.name->Ident; + case AstNode_TypeDecl: + return node->TypeDecl.token; + case AstNode_ImportDecl: + return node->ImportDecl.token; + case AstNode_ForeignLibrary: + return node->ForeignLibrary.token; + case AstNode_Parameter: { + if (node->Parameter.names.count > 0) { + return ast_node_token(node->Parameter.names.e[0]); + } else { + return ast_node_token(node->Parameter.type); + } + } + case AstNode_ProcType: + return node->ProcType.token; + case AstNode_PointerType: + return node->PointerType.token; + case AstNode_MaybeType: + return node->MaybeType.token; + case AstNode_ArrayType: + return node->ArrayType.token; + case AstNode_VectorType: + return node->VectorType.token; + case AstNode_StructType: + return node->StructType.token; + case AstNode_UnionType: + return node->UnionType.token; + case AstNode_RawUnionType: + return node->RawUnionType.token; + case AstNode_EnumType: + return node->EnumType.token; + } + + return empty_token; +} + +// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ +AstNode *make_node(AstFile *f, AstNodeKind kind) { + gbArena *arena = &f->arena; + if (gb_arena_size_remaining(arena, GB_DEFAULT_MEMORY_ALIGNMENT) <= gb_size_of(AstNode)) { + // NOTE(bill): If a syntax error is so bad, just quit! + gb_exit(1); + } + AstNode *node = gb_alloc_item(gb_arena_allocator(arena), AstNode); + node->kind = kind; + return node; +} + +AstNode *make_bad_expr(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadExpr); + result->BadExpr.begin = begin; + result->BadExpr.end = end; + return result; +} + +AstNode *make_tag_expr(AstFile *f, Token token, Token name, AstNode *expr) { + AstNode *result = make_node(f, AstNode_TagExpr); + result->TagExpr.token = token; + result->TagExpr.name = name; + result->TagExpr.expr = expr; + return result; +} + +AstNode *make_run_expr(AstFile *f, Token token, Token name, AstNode *expr) { + AstNode *result = make_node(f, AstNode_RunExpr); + result->RunExpr.token = token; + result->RunExpr.name = name; + result->RunExpr.expr = expr; + return result; +} + + +AstNode *make_tag_stmt(AstFile *f, Token token, Token name, AstNode *stmt) { + AstNode *result = make_node(f, AstNode_TagStmt); + result->TagStmt.token = token; + result->TagStmt.name = name; + result->TagStmt.stmt = stmt; + return result; +} + +AstNode *make_unary_expr(AstFile *f, Token op, AstNode *expr) { + AstNode *result = make_node(f, AstNode_UnaryExpr); + result->UnaryExpr.op = op; + result->UnaryExpr.expr = expr; + return result; +} + +AstNode *make_binary_expr(AstFile *f, Token op, AstNode *left, AstNode *right) { + AstNode *result = make_node(f, AstNode_BinaryExpr); + + if (left == NULL) { + syntax_error(op, "No lhs expression for binary expression `%.*s`", LIT(op.string)); + left = make_bad_expr(f, op, op); + } + if (right == NULL) { + syntax_error(op, "No rhs expression for binary expression `%.*s`", LIT(op.string)); + right = make_bad_expr(f, op, op); + } + + result->BinaryExpr.op = op; + result->BinaryExpr.left = left; + result->BinaryExpr.right = right; + + return result; +} + +AstNode *make_paren_expr(AstFile *f, AstNode *expr, Token open, Token close) { + AstNode *result = make_node(f, AstNode_ParenExpr); + result->ParenExpr.expr = expr; + result->ParenExpr.open = open; + result->ParenExpr.close = close; + return result; +} + +AstNode *make_call_expr(AstFile *f, AstNode *proc, AstNodeArray args, Token open, Token close, Token ellipsis) { + AstNode *result = make_node(f, AstNode_CallExpr); + result->CallExpr.proc = proc; + result->CallExpr.args = args; + result->CallExpr.open = open; + result->CallExpr.close = close; + result->CallExpr.ellipsis = ellipsis; + return result; +} + +AstNode *make_selector_expr(AstFile *f, Token token, AstNode *expr, AstNode *selector) { + AstNode *result = make_node(f, AstNode_SelectorExpr); + result->SelectorExpr.expr = expr; + result->SelectorExpr.selector = selector; + return result; +} + +AstNode *make_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, Token close) { + AstNode *result = make_node(f, AstNode_IndexExpr); + result->IndexExpr.expr = expr; + result->IndexExpr.index = index; + result->IndexExpr.open = open; + result->IndexExpr.close = close; + return result; +} + + +AstNode *make_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, AstNode *low, AstNode *high, AstNode *max, bool triple_indexed) { + AstNode *result = make_node(f, AstNode_SliceExpr); + result->SliceExpr.expr = expr; + result->SliceExpr.open = open; + result->SliceExpr.close = close; + result->SliceExpr.low = low; + result->SliceExpr.high = high; + result->SliceExpr.max = max; + result->SliceExpr.triple_indexed = triple_indexed; + return result; +} + +AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) { + AstNode *result = make_node(f, AstNode_DerefExpr); + result->DerefExpr.expr = expr; + result->DerefExpr.op = op; + return result; +} + +AstNode *make_demaybe_expr(AstFile *f, AstNode *expr, Token op) { + AstNode *result = make_node(f, AstNode_DemaybeExpr); + result->DemaybeExpr.expr = expr; + result->DemaybeExpr.op = op; + return result; +} + + +AstNode *make_basic_lit(AstFile *f, Token basic_lit) { + AstNode *result = make_node(f, AstNode_BasicLit); + result->BasicLit = basic_lit; + return result; +} + +AstNode *make_ident(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_Ident); + result->Ident = token; + return result; +} + +AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) { + AstNode *result = make_node(f, AstNode_Ellipsis); + result->Ellipsis.token = token; + result->Ellipsis.expr = expr; + return result; +} + + +AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) { + AstNode *result = make_node(f, AstNode_ProcLit); + result->ProcLit.type = type; + result->ProcLit.body = body; + result->ProcLit.tags = tags; + return result; +} + +AstNode *make_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) { + AstNode *result = make_node(f, AstNode_FieldValue); + result->FieldValue.field = field; + result->FieldValue.value = value; + result->FieldValue.eq = eq; + return result; +} + +AstNode *make_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) { + AstNode *result = make_node(f, AstNode_CompoundLit); + result->CompoundLit.type = type; + result->CompoundLit.elems = elems; + result->CompoundLit.open = open; + result->CompoundLit.close = close; + return result; +} + +AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadStmt); + result->BadStmt.begin = begin; + result->BadStmt.end = end; + return result; +} + +AstNode *make_empty_stmt(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_EmptyStmt); + result->EmptyStmt.token = token; + return result; +} + +AstNode *make_expr_stmt(AstFile *f, AstNode *expr) { + AstNode *result = make_node(f, AstNode_ExprStmt); + result->ExprStmt.expr = expr; + return result; +} + +AstNode *make_inc_dec_stmt(AstFile *f, Token op, AstNode *expr) { + AstNode *result = make_node(f, AstNode_IncDecStmt); + result->IncDecStmt.op = op; + result->IncDecStmt.expr = expr; + return result; +} + +AstNode *make_assign_stmt(AstFile *f, Token op, AstNodeArray lhs, AstNodeArray rhs) { + AstNode *result = make_node(f, AstNode_AssignStmt); + result->AssignStmt.op = op; + result->AssignStmt.lhs = lhs; + result->AssignStmt.rhs = rhs; + return result; +} + +AstNode *make_block_stmt(AstFile *f, AstNodeArray stmts, Token open, Token close) { + AstNode *result = make_node(f, AstNode_BlockStmt); + result->BlockStmt.stmts = stmts; + result->BlockStmt.open = open; + result->BlockStmt.close = close; + return result; +} + +AstNode *make_if_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_stmt) { + AstNode *result = make_node(f, AstNode_IfStmt); + result->IfStmt.token = token; + result->IfStmt.init = init; + result->IfStmt.cond = cond; + result->IfStmt.body = body; + result->IfStmt.else_stmt = else_stmt; + return result; +} + +AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) { + AstNode *result = make_node(f, AstNode_ReturnStmt); + result->ReturnStmt.token = token; + result->ReturnStmt.results = results; + return result; +} + +AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { + AstNode *result = make_node(f, AstNode_ForStmt); + result->ForStmt.token = token; + result->ForStmt.init = init; + result->ForStmt.cond = cond; + result->ForStmt.post = post; + result->ForStmt.body = body; + return result; +} + + +AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) { + AstNode *result = make_node(f, AstNode_MatchStmt); + result->MatchStmt.token = token; + result->MatchStmt.init = init; + result->MatchStmt.tag = tag; + result->MatchStmt.body = body; + return result; +} + + +AstNode *make_type_match_stmt(AstFile *f, Token token, AstNode *tag, AstNode *var, AstNode *body) { + AstNode *result = make_node(f, AstNode_TypeMatchStmt); + result->TypeMatchStmt.token = token; + result->TypeMatchStmt.tag = tag; + result->TypeMatchStmt.var = var; + result->TypeMatchStmt.body = body; + return result; +} + +AstNode *make_case_clause(AstFile *f, Token token, AstNodeArray list, AstNodeArray stmts) { + AstNode *result = make_node(f, AstNode_CaseClause); + result->CaseClause.token = token; + result->CaseClause.list = list; + result->CaseClause.stmts = stmts; + return result; +} + + +AstNode *make_defer_stmt(AstFile *f, Token token, AstNode *stmt) { + AstNode *result = make_node(f, AstNode_DeferStmt); + result->DeferStmt.token = token; + result->DeferStmt.stmt = stmt; + return result; +} + +AstNode *make_branch_stmt(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_BranchStmt); + result->BranchStmt.token = token; + return result; +} + +AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) { + AstNode *result = make_node(f, AstNode_UsingStmt); + result->UsingStmt.token = token; + result->UsingStmt.node = node; + return result; +} + +AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) { + AstNode *result = make_node(f, AstNode_AsmOperand); + result->AsmOperand.string = string; + result->AsmOperand.operand = operand; + return result; + +} + +AstNode *make_asm_stmt(AstFile *f, Token token, bool is_volatile, Token open, Token close, Token code_string, + AstNode *output_list, AstNode *input_list, AstNode *clobber_list, + isize output_count, isize input_count, isize clobber_count) { + AstNode *result = make_node(f, AstNode_AsmStmt); + result->AsmStmt.token = token; + result->AsmStmt.is_volatile = is_volatile; + result->AsmStmt.open = open; + result->AsmStmt.close = close; + result->AsmStmt.code_string = code_string; + result->AsmStmt.output_list = output_list; + result->AsmStmt.input_list = input_list; + result->AsmStmt.clobber_list = clobber_list; + result->AsmStmt.output_count = output_count; + result->AsmStmt.input_count = input_count; + result->AsmStmt.clobber_count = clobber_count; + return result; +} + +AstNode *make_push_allocator(AstFile *f, Token token, AstNode *expr, AstNode *body) { + AstNode *result = make_node(f, AstNode_PushAllocator); + result->PushAllocator.token = token; + result->PushAllocator.expr = expr; + result->PushAllocator.body = body; + return result; +} + +AstNode *make_push_context(AstFile *f, Token token, AstNode *expr, AstNode *body) { + AstNode *result = make_node(f, AstNode_PushContext); + result->PushContext.token = token; + result->PushContext.expr = expr; + result->PushContext.body = body; + return result; +} + + + + +AstNode *make_bad_decl(AstFile *f, Token begin, Token end) { + AstNode *result = make_node(f, AstNode_BadDecl); + result->BadDecl.begin = begin; + result->BadDecl.end = end; + return result; +} + +AstNode *make_var_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) { + AstNode *result = make_node(f, AstNode_VarDecl); + result->VarDecl.names = names; + result->VarDecl.type = type; + result->VarDecl.values = values; + return result; +} + +AstNode *make_const_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) { + AstNode *result = make_node(f, AstNode_ConstDecl); + result->ConstDecl.names = names; + result->ConstDecl.type = type; + result->ConstDecl.values = values; + return result; +} + +AstNode *make_parameter(AstFile *f, AstNodeArray names, AstNode *type, bool is_using) { + AstNode *result = make_node(f, AstNode_Parameter); + result->Parameter.names = names; + result->Parameter.type = type; + result->Parameter.is_using = is_using; + return result; +} + +AstNode *make_proc_type(AstFile *f, Token token, AstNodeArray params, AstNodeArray results) { + AstNode *result = make_node(f, AstNode_ProcType); + result->ProcType.token = token; + result->ProcType.params = params; + result->ProcType.results = results; + return result; +} + +AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String link_name) { + AstNode *result = make_node(f, AstNode_ProcDecl); + result->ProcDecl.name = name; + result->ProcDecl.type = proc_type; + result->ProcDecl.body = body; + result->ProcDecl.tags = tags; + result->ProcDecl.foreign_name = foreign_name; + result->ProcDecl.link_name = link_name; + return result; +} + +AstNode *make_pointer_type(AstFile *f, Token token, AstNode *type) { + AstNode *result = make_node(f, AstNode_PointerType); + result->PointerType.token = token; + result->PointerType.type = type; + return result; +} + +AstNode *make_maybe_type(AstFile *f, Token token, AstNode *type) { + AstNode *result = make_node(f, AstNode_MaybeType); + result->MaybeType.token = token; + result->MaybeType.type = type; + return result; +} + +AstNode *make_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { + AstNode *result = make_node(f, AstNode_ArrayType); + result->ArrayType.token = token; + result->ArrayType.count = count; + result->ArrayType.elem = elem; + return result; +} + +AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { + AstNode *result = make_node(f, AstNode_VectorType); + result->VectorType.token = token; + result->VectorType.count = count; + result->VectorType.elem = elem; + return result; +} + +AstNode *make_struct_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count, bool is_packed, bool is_ordered) { + AstNode *result = make_node(f, AstNode_StructType); + result->StructType.token = token; + result->StructType.decls = decls; + result->StructType.decl_count = decl_count; + result->StructType.is_packed = is_packed; + result->StructType.is_ordered = is_ordered; + return result; +} + + +AstNode *make_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) { + AstNode *result = make_node(f, AstNode_UnionType); + result->UnionType.token = token; + result->UnionType.decls = decls; + result->UnionType.decl_count = decl_count; + return result; +} + +AstNode *make_raw_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) { + AstNode *result = make_node(f, AstNode_RawUnionType); + result->RawUnionType.token = token; + result->RawUnionType.decls = decls; + result->RawUnionType.decl_count = decl_count; + return result; +} + + +AstNode *make_enum_type(AstFile *f, Token token, AstNode *base_type, AstNodeArray fields) { + AstNode *result = make_node(f, AstNode_EnumType); + result->EnumType.token = token; + result->EnumType.base_type = base_type; + result->EnumType.fields = fields; + return result; +} + +AstNode *make_type_decl(AstFile *f, Token token, AstNode *name, AstNode *type) { + AstNode *result = make_node(f, AstNode_TypeDecl); + result->TypeDecl.token = token; + result->TypeDecl.name = name; + result->TypeDecl.type = type; + return result; +} + +AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name, bool is_load) { + AstNode *result = make_node(f, AstNode_ImportDecl); + result->ImportDecl.token = token; + result->ImportDecl.relpath = relpath; + result->ImportDecl.import_name = import_name; + result->ImportDecl.is_load = is_load; + return result; +} + +AstNode *make_foreign_library(AstFile *f, Token token, Token filepath, bool is_system) { + AstNode *result = make_node(f, AstNode_ForeignLibrary); + result->ForeignLibrary.token = token; + result->ForeignLibrary.filepath = filepath; + result->ForeignLibrary.is_system = is_system; + return result; +} + +bool next_token(AstFile *f) { + if (f->curr_token_index+1 < f->tokens.count) { + if (f->curr_token.kind != Token_Comment) { + f->prev_token = f->curr_token; + } + + f->curr_token_index++; + f->curr_token = f->tokens.e[f->curr_token_index]; + if (f->curr_token.kind == Token_Comment) { + return next_token(f); + } + return true; + } + syntax_error(f->curr_token, "Token is EOF"); + return false; +} + +Token expect_token(AstFile *f, TokenKind kind) { + Token prev = f->curr_token; + if (prev.kind != kind) { + syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`", + LIT(token_strings[kind]), + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +Token expect_token_after(AstFile *f, TokenKind kind, char *msg) { + Token prev = f->curr_token; + if (prev.kind != kind) { + syntax_error(f->curr_token, "Expected `%.*s` after %s, got `%.*s`", + LIT(token_strings[kind]), + msg, + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + + +Token expect_operator(AstFile *f) { + Token prev = f->curr_token; + if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { + syntax_error(f->curr_token, "Expected an operator, got `%.*s`", + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +Token expect_keyword(AstFile *f) { + Token prev = f->curr_token; + if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) { + syntax_error(f->curr_token, "Expected a keyword, got `%.*s`", + LIT(token_strings[prev.kind])); + } + next_token(f); + return prev; +} + +bool allow_token(AstFile *f, TokenKind kind) { + Token prev = f->curr_token; + if (prev.kind == kind) { + next_token(f); + return true; + } + return false; +} + + +bool is_blank_ident(String str) { + if (str.len == 1) { + return str.text[0] == '_'; + } + return false; +} + + +// NOTE(bill): Go to next statement to prevent numerous error messages popping up +void fix_advance_to_next_stmt(AstFile *f) { + // TODO(bill): fix_advance_to_next_stmt +#if 1 + for (;;) { + Token t = f->curr_token; + switch (t.kind) { + case Token_EOF: + case Token_Semicolon: + return; + + case Token_if: + case Token_return: + case Token_for: + case Token_match: + case Token_defer: + case Token_asm: + case Token_using: + + case Token_break: + case Token_continue: + case Token_fallthrough: + + case Token_push_allocator: + case Token_push_context: + + case Token_Hash: + { + if (token_pos_are_equal(t.pos, f->fix_prev_pos) && + f->fix_count < PARSER_MAX_FIX_COUNT) { + f->fix_count++; + return; + } + if (token_pos_cmp(f->fix_prev_pos, t.pos) < 0) { + f->fix_prev_pos = t.pos; + f->fix_count = 0; // NOTE(bill): Reset + return; + } + // NOTE(bill): Reaching here means there is a parsing bug + } break; + } + next_token(f); + } +#endif +} + +bool expect_semicolon_after_stmt(AstFile *f, AstNode *s) { + if (allow_token(f, Token_Semicolon)) { + return true; + } + + if (f->curr_token.pos.line != f->prev_token.pos.line) { + return true; + } + + switch (f->curr_token.kind) { + case Token_EOF: + case Token_CloseBrace: + return true; + } + + syntax_error(f->curr_token, + "Expected `;` after %.*s, got `%.*s`", + LIT(ast_node_strings[s->kind]), LIT(token_strings[f->curr_token.kind])); + fix_advance_to_next_stmt(f); + return false; +} + + +AstNode * parse_expr(AstFile *f, bool lhs); +AstNode * parse_proc_type(AstFile *f); +AstNodeArray parse_stmt_list(AstFile *f); +AstNode * parse_stmt(AstFile *f); +AstNode * parse_body(AstFile *f); + +AstNode *parse_identifier(AstFile *f) { + Token token = f->curr_token; + if (token.kind == Token_Identifier) { + next_token(f); + } else { + token.string = str_lit("_"); + expect_token(f, Token_Identifier); + } + return make_ident(f, token); +} + +AstNode *parse_tag_expr(AstFile *f, AstNode *expression) { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + return make_tag_expr(f, token, name, expression); +} + +AstNode *parse_tag_stmt(AstFile *f, AstNode *statement) { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + return make_tag_stmt(f, token, name, statement); +} + +AstNode *unparen_expr(AstNode *node) { + for (;;) { + if (node->kind != AstNode_ParenExpr) + return node; + node = node->ParenExpr.expr; + } +} + +AstNode *parse_value(AstFile *f); + +AstNodeArray parse_element_list(AstFile *f) { + AstNodeArray elems = make_ast_node_array(f); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *elem = parse_value(f); + if (f->curr_token.kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + AstNode *value = parse_value(f); + elem = make_field_value(f, elem, value, eq); + } + + array_add(&elems, elem); + + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + return elems; +} + +AstNode *parse_literal_value(AstFile *f, AstNode *type) { + AstNodeArray elems = {0}; + Token open = expect_token(f, Token_OpenBrace); + f->expr_level++; + if (f->curr_token.kind != Token_CloseBrace) { + elems = parse_element_list(f); + } + f->expr_level--; + Token close = expect_token(f, Token_CloseBrace); + + return make_compound_lit(f, type, elems, open, close); +} + +AstNode *parse_value(AstFile *f) { + if (f->curr_token.kind == Token_OpenBrace) + return parse_literal_value(f, NULL); + + AstNode *value = parse_expr(f, false); + return value; +} + +AstNode *parse_identifier_or_type(AstFile *f, u32 flags); + + +void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) { + if (*tags & tag) { + syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name)); + } + *tags |= tag; +} + +bool is_foreign_name_valid(String name) { + // TODO(bill): is_foreign_name_valid + if (name.len == 0) + return false; + isize offset = 0; + while (offset < name.len) { + Rune rune; + isize remaining = name.len - offset; + isize width = gb_utf8_decode(name.text+offset, remaining, &rune); + if (rune == GB_RUNE_INVALID && width == 1) { + return false; + } else if (rune == GB_RUNE_BOM && remaining > 0) { + return false; + } + + if (offset == 0) { + switch (rune) { + case '-': + case '$': + case '.': + case '_': + break; + default: + if (!gb_char_is_alpha(cast(char)rune)) + return false; + break; + } + } else { + switch (rune) { + case '-': + case '$': + case '.': + case '_': + break; + default: + if (!gb_char_is_alphanumeric(cast(char)rune)) { + return false; + } + break; + } + } + + offset += width; + } + + return true; +} + +void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_name) { + // TODO(bill): Add this to procedure literals too + GB_ASSERT(foreign_name != NULL); + GB_ASSERT(link_name != NULL); + + while (f->curr_token.kind == Token_Hash) { + AstNode *tag_expr = parse_tag_expr(f, NULL); + ast_node(te, TagExpr, tag_expr); + String tag_name = te->name.string; + + #define ELSE_IF_ADD_TAG(name) \ + else if (str_eq(tag_name, str_lit(#name))) { \ + check_proc_add_tag(f, tag_expr, tags, ProcTag_##name, tag_name); \ + } + + if (str_eq(tag_name, str_lit("foreign"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_foreign, tag_name); + if (f->curr_token.kind == Token_String) { + *foreign_name = f->curr_token.string; + // TODO(bill): Check if valid string + if (!is_foreign_name_valid(*foreign_name)) { + syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name)); + } + + next_token(f); + } + } else if (str_eq(tag_name, str_lit("link_name"))) { + check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name); + if (f->curr_token.kind == Token_String) { + *link_name = f->curr_token.string; + // TODO(bill): Check if valid string + if (!is_foreign_name_valid(*link_name)) { + syntax_error(ast_node_token(tag_expr), "Invalid alternative link procedure name `%.*s`", LIT(*link_name)); + } + + next_token(f); + } else { + expect_token(f, Token_String); + } + } + ELSE_IF_ADD_TAG(bounds_check) + ELSE_IF_ADD_TAG(no_bounds_check) + ELSE_IF_ADD_TAG(inline) + ELSE_IF_ADD_TAG(no_inline) + ELSE_IF_ADD_TAG(dll_import) + ELSE_IF_ADD_TAG(dll_export) + ELSE_IF_ADD_TAG(stdcall) + ELSE_IF_ADD_TAG(fastcall) + // ELSE_IF_ADD_TAG(cdecl) + else { + syntax_error(ast_node_token(tag_expr), "Unknown procedure tag"); + } + + #undef ELSE_IF_ADD_TAG + } + + if ((*tags & ProcTag_foreign) && (*tags & ProcTag_link_name)) { + syntax_error(f->curr_token, "You cannot apply both #foreign and #link_name to a procedure"); + } + + if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) { + syntax_error(f->curr_token, "You cannot apply both #inline and #no_inline to a procedure"); + } + + if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { + syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); + } + + if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) { + syntax_error(f->curr_token, "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body"); + } + + if ((*tags & ProcTag_stdcall) && (*tags & ProcTag_fastcall)) { + syntax_error(f->curr_token, "You cannot apply one calling convention to a procedure"); + } +} + +AstNode *parse_operand(AstFile *f, bool lhs) { + AstNode *operand = NULL; // Operand + switch (f->curr_token.kind) { + case Token_Identifier: + operand = parse_identifier(f); + if (!lhs) { + // TODO(bill): Handle? + } + return operand; + + case Token_Integer: + case Token_Float: + case Token_String: + case Token_Rune: + operand = make_basic_lit(f, f->curr_token); + next_token(f); + return operand; + + case Token_OpenParen: { + Token open, close; + // NOTE(bill): Skip the Paren Expression + open = expect_token(f, Token_OpenParen); + f->expr_level++; + operand = parse_expr(f, false); + f->expr_level--; + close = expect_token(f, Token_CloseParen); + return make_paren_expr(f, operand, open, close); + } + + case Token_Hash: { + Token token = expect_token(f, Token_Hash); + Token name = expect_token(f, Token_Identifier); + if (str_eq(name.string, str_lit("rune"))) { + if (f->curr_token.kind == Token_String) { + Token *s = &f->curr_token; + + if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) { + syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string)); + } + s->kind = Token_Rune; // NOTE(bill): Change it + } else { + expect_token(f, Token_String); + } + operand = parse_operand(f, lhs); + } else if (str_eq(name.string, str_lit("file"))) { + Token token = name; + token.kind = Token_String; + token.string = token.pos.file; + return make_basic_lit(f, token); + } else if (str_eq(name.string, str_lit("line"))) { + Token token = name; + token.kind = Token_Integer; + char *str = gb_alloc_array(gb_arena_allocator(&f->arena), char, 20); + gb_i64_to_str(token.pos.line, str, 10); + token.string = make_string_c(str); + return make_basic_lit(f, token); + } else if (str_eq(name.string, str_lit("run"))) { + AstNode *expr = parse_expr(f, false); + operand = make_run_expr(f, token, name, expr); + if (unparen_expr(expr)->kind != AstNode_CallExpr) { + error(ast_node_token(expr), "#run can only be applied to procedure calls"); + operand = make_bad_expr(f, token, f->curr_token); + } + warning(token, "#run is not yet implemented"); + } else { + operand = make_tag_expr(f, token, name, parse_expr(f, false)); + } + return operand; + } + + // Parse Procedure Type or Literal + case Token_proc: { + AstNode *curr_proc = f->curr_proc; + AstNode *type = parse_proc_type(f); + f->curr_proc = type; + + u64 tags = 0; + String foreign_name = {0}; + String link_name = {0}; + parse_proc_tags(f, &tags, &foreign_name, &link_name); + if (tags & ProcTag_foreign) { + syntax_error(f->curr_token, "#foreign cannot be applied to procedure literals"); + } + if (tags & ProcTag_link_name) { + syntax_error(f->curr_token, "#link_name cannot be applied to procedure literals"); + } + + if (f->curr_token.kind == Token_OpenBrace) { + AstNode *body; + + f->expr_level++; + body = parse_body(f); + f->expr_level--; + + type = make_proc_lit(f, type, body, tags); + } + f->curr_proc = curr_proc; + return type; + } + + default: { + AstNode *type = parse_identifier_or_type(f, 0); + if (type != NULL) { + // NOTE(bill): Sanity check as identifiers should be handled already + GB_ASSERT_MSG(type->kind != AstNode_Ident, "Type Cannot be identifier"); + return type; + } + } + } + + Token begin = f->curr_token; + syntax_error(begin, "Expected an operand"); + fix_advance_to_next_stmt(f); + return make_bad_expr(f, begin, f->curr_token); +} + +bool is_literal_type(AstNode *node) { + switch (node->kind) { + case AstNode_BadExpr: + case AstNode_Ident: + case AstNode_SelectorExpr: + case AstNode_ArrayType: + case AstNode_VectorType: + case AstNode_StructType: + return true; + } + return false; +} + +AstNode *parse_call_expr(AstFile *f, AstNode *operand) { + AstNodeArray args = make_ast_node_array(f); + Token open_paren, close_paren; + Token ellipsis = {0}; + + f->expr_level++; + open_paren = expect_token(f, Token_OpenParen); + + while (f->curr_token.kind != Token_CloseParen && + f->curr_token.kind != Token_EOF && + ellipsis.pos.line == 0) { + if (f->curr_token.kind == Token_Comma) + syntax_error(f->curr_token, "Expected an expression not a ,"); + + if (f->curr_token.kind == Token_Ellipsis) { + ellipsis = f->curr_token; + next_token(f); + } + + AstNode *arg = parse_expr(f, false); + array_add(&args, arg); + + if (f->curr_token.kind != Token_Comma) { + if (f->curr_token.kind == Token_CloseParen) + break; + } + + next_token(f); + } + + f->expr_level--; + close_paren = expect_token(f, Token_CloseParen); + + return make_call_expr(f, operand, args, open_paren, close_paren, ellipsis); +} + +AstNode *parse_atom_expr(AstFile *f, bool lhs) { + AstNode *operand = parse_operand(f, lhs); + + bool loop = true; + while (loop) { + switch (f->curr_token.kind) { + + case Token_Prime: { + Token op = expect_token(f, Token_Prime); + if (lhs) { + // TODO(bill): Handle this + } + AstNode *proc = parse_identifier(f); + AstNodeArray args; + array_init_reserve(&args, gb_arena_allocator(&f->arena), 1); + array_add(&args, operand); + operand = make_call_expr(f, proc, args, ast_node_token(operand), op, empty_token); + } break; + + case Token_OpenParen: { + if (lhs) { + // TODO(bill): Handle this shit! Is this even allowed in this language?! + } + operand = parse_call_expr(f, operand); + } break; + + case Token_Period: { + Token token = f->curr_token; + next_token(f); + if (lhs) { + // TODO(bill): handle this + } + switch (f->curr_token.kind) { + case Token_Identifier: + operand = make_selector_expr(f, token, operand, parse_identifier(f)); + break; + default: { + syntax_error(f->curr_token, "Expected a selector"); + next_token(f); + operand = make_selector_expr(f, f->curr_token, operand, NULL); + } break; + } + } break; + + case Token_OpenBracket: { + if (lhs) { + // TODO(bill): Handle this + } + Token open, close; + AstNode *indices[3] = {0}; + + f->expr_level++; + open = expect_token(f, Token_OpenBracket); + + if (f->curr_token.kind != Token_Colon) + indices[0] = parse_expr(f, false); + isize colon_count = 0; + Token colons[2] = {0}; + + while (f->curr_token.kind == Token_Colon && colon_count < 2) { + colons[colon_count++] = f->curr_token; + next_token(f); + if (f->curr_token.kind != Token_Colon && + f->curr_token.kind != Token_CloseBracket && + f->curr_token.kind != Token_EOF) { + indices[colon_count] = parse_expr(f, false); + } + } + + f->expr_level--; + close = expect_token(f, Token_CloseBracket); + + if (colon_count == 0) { + operand = make_index_expr(f, operand, indices[0], open, close); + } else { + bool triple_indexed = false; + if (colon_count == 2) { + triple_indexed = true; + if (indices[1] == NULL) { + syntax_error(colons[0], "Second index is required in a triple indexed slice"); + indices[1] = make_bad_expr(f, colons[0], colons[1]); + } + if (indices[2] == NULL) { + syntax_error(colons[1], "Third index is required in a triple indexed slice"); + indices[2] = make_bad_expr(f, colons[1], close); + } + } + operand = make_slice_expr(f, operand, open, close, indices[0], indices[1], indices[2], triple_indexed); + } + } break; + + case Token_Pointer: // Deference + operand = make_deref_expr(f, operand, expect_token(f, Token_Pointer)); + break; + + case Token_Maybe: // Demaybe + operand = make_demaybe_expr(f, operand, expect_token(f, Token_Maybe)); + break; + + case Token_OpenBrace: { + if (!lhs && is_literal_type(operand) && f->expr_level >= 0) { + if (f->curr_token.pos.line == f->prev_token.pos.line) { + // TODO(bill): This is a hack due to optional semicolons + // TODO(bill): It's probably much better to solve this by changing + // the syntax for struct literals and array literals + operand = parse_literal_value(f, operand); + } else { + loop = false; + } + } else { + loop = false; + } + } break; + + default: + loop = false; + break; + } + + lhs = false; // NOTE(bill): 'tis not lhs anymore + } + + return operand; +} + +AstNode *parse_type(AstFile *f); + +AstNode *parse_unary_expr(AstFile *f, bool lhs) { + switch (f->curr_token.kind) { + case Token_Pointer: + case Token_Maybe: + case Token_Add: + case Token_Sub: + case Token_Not: + case Token_Xor: { + AstNode *operand; + Token op = f->curr_token; + next_token(f); + operand = parse_unary_expr(f, lhs); + return make_unary_expr(f, op, operand); + } break; + } + + return parse_atom_expr(f, lhs); +} + +// NOTE(bill): result == priority +i32 token_precedence(Token t) { + switch (t.kind) { + case Token_CmpOr: + return 1; + case Token_CmpAnd: + return 2; + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_Gt: + case Token_LtEq: + case Token_GtEq: + return 3; + case Token_Add: + case Token_Sub: + case Token_Or: + case Token_Xor: + return 4; + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_AndNot: + case Token_Shl: + case Token_Shr: + return 5; + case Token_DoublePrime: + return 6; + case Token_as: + case Token_transmute: + case Token_down_cast: + case Token_union_cast: + return 7; + } + + return 0; +} + +AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { + AstNode *expression = parse_unary_expr(f, lhs); + for (i32 prec = token_precedence(f->curr_token); prec >= prec_in; prec--) { + for (;;) { + AstNode *right; + Token op = f->curr_token; + i32 op_prec = token_precedence(op); + if (op_prec != prec) + break; + expect_operator(f); // NOTE(bill): error checks too + if (lhs) { + // TODO(bill): error checking + lhs = false; + } + + switch (op.kind) { + case Token_DoublePrime: { + // TODO(bill): Properly define semantic for in-fix and post-fix calls + AstNode *proc = parse_identifier(f); + /* if (f->curr_token.kind == Token_OpenParen) { + AstNode *call = parse_call_expr(f, proc); + array_add(&call->CallExpr.args, expression); + for (isize i = gb_array_count(call->CallExpr.args)-1; i > 0; i--) { + gb_swap(AstNode *, call->CallExpr.args[i], call->CallExpr.args[i-1]); + } + + expression = call; + } else */{ + right = parse_binary_expr(f, false, prec+1); + AstNodeArray args = {0}; + array_init_reserve(&args, gb_arena_allocator(&f->arena), 2); + array_add(&args, expression); + array_add(&args, right); + expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token); + } + continue; + } break; + + case Token_as: + case Token_transmute: + case Token_down_cast: + case Token_union_cast: + right = parse_type(f); + break; + + default: + right = parse_binary_expr(f, false, prec+1); + if (!right) { + syntax_error(op, "Expected expression on the right hand side of the binary operator"); + } + break; + } + expression = make_binary_expr(f, op, expression, right); + } + } + return expression; +} + +AstNode *parse_expr(AstFile *f, bool lhs) { + return parse_binary_expr(f, lhs, 0+1); +} + + +AstNodeArray parse_expr_list(AstFile *f, bool lhs) { + AstNodeArray list = make_ast_node_array(f); + do { + AstNode *e = parse_expr(f, lhs); + array_add(&list, e); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } while (true); + + return list; +} + +AstNodeArray parse_lhs_expr_list(AstFile *f) { + return parse_expr_list(f, true); +} + +AstNodeArray parse_rhs_expr_list(AstFile *f) { + return parse_expr_list(f, false); +} + +AstNode *parse_decl(AstFile *f, AstNodeArray names); + +AstNode *parse_simple_stmt(AstFile *f) { + isize lhs_count = 0, rhs_count = 0; + AstNodeArray lhs = parse_lhs_expr_list(f); + + + AstNode *statement = NULL; + Token token = f->curr_token; + switch (token.kind) { + case Token_Eq: + case Token_AddEq: + case Token_SubEq: + case Token_MulEq: + case Token_QuoEq: + case Token_ModEq: + case Token_AndEq: + case Token_OrEq: + case Token_XorEq: + case Token_ShlEq: + case Token_ShrEq: + case Token_AndNotEq: + case Token_CmpAndEq: + case Token_CmpOrEq: + { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a simple statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + next_token(f); + AstNodeArray rhs = parse_rhs_expr_list(f); + if (rhs.count == 0) { + syntax_error(token, "No right-hand side in assignment statement."); + return make_bad_stmt(f, token, f->curr_token); + } + return make_assign_stmt(f, token, lhs, rhs); + } break; + + case Token_Colon: // Declare + return parse_decl(f, lhs); + } + + if (lhs_count > 1) { + syntax_error(token, "Expected 1 expression"); + return make_bad_stmt(f, token, f->curr_token); + } + + token = f->curr_token; + switch (token.kind) { + case Token_Increment: + case Token_Decrement: + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a simple statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + statement = make_inc_dec_stmt(f, token, lhs.e[0]); + next_token(f); + return statement; + } + + return make_expr_stmt(f, lhs.e[0]); +} + + + +AstNode *parse_block_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a block statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + AstNode *block_stmt = parse_body(f); + return block_stmt; +} + +AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { + if (statement == NULL) + return NULL; + + if (statement->kind == AstNode_ExprStmt) + return statement->ExprStmt.expr; + + syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind)); + return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); +} + +AstNodeArray parse_identfier_list(AstFile *f) { + AstNodeArray list = make_ast_node_array(f); + + do { + array_add(&list, parse_identifier(f)); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } while (true); + + return list; +} + + + +AstNode *parse_type_attempt(AstFile *f) { + AstNode *type = parse_identifier_or_type(f, 0); + if (type != NULL) { + // TODO(bill): Handle? + } + return type; +} + +AstNode *parse_type(AstFile *f) { + AstNode *type = parse_type_attempt(f); + if (type == NULL) { + Token token = f->curr_token; + syntax_error(token, "Expected a type"); + next_token(f); + return make_bad_expr(f, token, f->curr_token); + } + return type; +} + + +Token parse_procedure_signature(AstFile *f, + AstNodeArray *params, AstNodeArray *results); + +AstNode *parse_proc_type(AstFile *f) { + AstNodeArray params = {0}; + AstNodeArray results = {0}; + + Token proc_token = parse_procedure_signature(f, ¶ms, &results); + + return make_proc_type(f, proc_token, params, results); +} + + +AstNodeArray parse_parameter_list(AstFile *f) { + AstNodeArray params = make_ast_node_array(f); + + while (f->curr_token.kind == Token_Identifier || + f->curr_token.kind == Token_using) { + bool is_using = false; + if (allow_token(f, Token_using)) { + is_using = true; + } + + AstNodeArray names = parse_lhs_expr_list(f); + if (names.count == 0) { + syntax_error(f->curr_token, "Empty parameter declaration"); + } + + if (names.count > 1 && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); + is_using = false; + } + + expect_token_after(f, Token_Colon, "parameter list"); + + AstNode *type = NULL; + if (f->curr_token.kind == Token_Ellipsis) { + Token ellipsis = f->curr_token; + next_token(f); + type = parse_type_attempt(f); + if (type == NULL) { + syntax_error(f->curr_token, "variadic parameter is missing a type after `..`"); + type = make_bad_expr(f, ellipsis, f->curr_token); + } else { + if (names.count > 1) { + syntax_error(f->curr_token, "mutliple variadic parameters, only `..`"); + } else { + type = make_ellipsis(f, ellipsis, type); + } + } + } else { + type = parse_type_attempt(f); + } + + + if (type == NULL) { + syntax_error(f->curr_token, "Expected a type for this parameter declaration"); + } + + array_add(¶ms, make_parameter(f, names, type, is_using)); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + return params; +} + + +AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, bool using_allowed) { + AstNodeArray decls = make_ast_node_array(f); + isize decl_count = 0; + + while (f->curr_token.kind == Token_Identifier || + f->curr_token.kind == Token_using) { + bool is_using = false; + if (allow_token(f, Token_using)) { + is_using = true; + } + AstNodeArray names = parse_lhs_expr_list(f); + if (names.count == 0) { + syntax_error(f->curr_token, "Empty field declaration"); + } + + if (!using_allowed && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to members of a union"); + is_using = false; + } + if (names.count > 1 && is_using) { + syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); + } + + AstNode *decl = NULL; + + if (f->curr_token.kind == Token_Colon) { + decl = parse_decl(f, names); + + if (decl->kind == AstNode_ProcDecl) { + syntax_error(f->curr_token, "Procedure declarations are not allowed within a structure"); + decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); + } + } else { + syntax_error(f->curr_token, "Illegal structure field"); + decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); + } + + expect_semicolon_after_stmt(f, decl); + + if (is_ast_node_decl(decl)) { + array_add(&decls, decl); + if (decl->kind == AstNode_VarDecl) { + decl->VarDecl.is_using = is_using && using_allowed; + if (decl->VarDecl.values.count > 0) { + syntax_error(f->curr_token, "Default variable assignments within a structure will be ignored (at the moment)"); + } + } else { + decl_count += 1; + } + } + } + + if (decl_count_) *decl_count_ = decl_count; + + return decls; +} + +AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { + switch (f->curr_token.kind) { + case Token_Identifier: { + AstNode *e = parse_identifier(f); + while (f->curr_token.kind == Token_Period) { + Token token = f->curr_token; + next_token(f); + AstNode *sel = parse_identifier(f); + e = make_selector_expr(f, token, e, sel); + } + if (f->curr_token.kind == Token_OpenParen) { + // HACK NOTE(bill): For type_of_val(expr) + e = parse_call_expr(f, e); + } + return e; + } + + case Token_Pointer: { + Token token = expect_token(f, Token_Pointer); + AstNode *elem = parse_type(f); + return make_pointer_type(f, token, elem); + } + + case Token_Maybe: { + Token token = expect_token(f, Token_Maybe); + AstNode *elem = parse_type(f); + return make_maybe_type(f, token, elem); + } + + case Token_OpenBracket: { + f->expr_level++; + Token token = expect_token(f, Token_OpenBracket); + AstNode *count_expr = NULL; + + if (f->curr_token.kind == Token_Ellipsis) { + count_expr = make_ellipsis(f, f->curr_token, NULL); + next_token(f); + } else if (f->curr_token.kind != Token_CloseBracket) { + count_expr = parse_expr(f, false); + } + expect_token(f, Token_CloseBracket); + f->expr_level--; + AstNode *e = make_array_type(f, token, count_expr, parse_type(f)); + return e; + } + + case Token_OpenBrace: { + f->expr_level++; + Token token = expect_token(f, Token_OpenBrace); + AstNode *count_expr = parse_expr(f, false); + expect_token(f, Token_CloseBrace); + f->expr_level--; + return make_vector_type(f, token, count_expr, parse_type(f)); + } + + case Token_struct: { + Token token = expect_token(f, Token_struct); + bool is_packed = false; + bool is_ordered = false; + while (allow_token(f, Token_Hash)) { + Token tag = expect_token_after(f, Token_Identifier, "`#`"); + if (str_eq(tag.string, str_lit("packed"))) { + if (is_packed) { + syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string)); + } + is_packed = true; + } else if (str_eq(tag.string, str_lit("ordered"))) { + if (is_ordered) { + syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string)); + } + is_ordered = true; + } else { + syntax_error(tag, "Invalid struct tag `#%.*s`", LIT(tag.string)); + } + } + + if (is_packed && is_ordered) { + syntax_error(token, "`#ordered` is not needed with `#packed` which implies ordering"); + } + + Token open = expect_token_after(f, Token_OpenBrace, "`struct`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, true); + Token close = expect_token(f, Token_CloseBrace); + + return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered); + } break; + + case Token_union: { + Token token = expect_token(f, Token_union); + Token open = expect_token_after(f, Token_OpenBrace, "`union`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, false); + Token close = expect_token(f, Token_CloseBrace); + + return make_union_type(f, token, decls, decl_count); + } + + case Token_raw_union: { + Token token = expect_token(f, Token_raw_union); + Token open = expect_token_after(f, Token_OpenBrace, "`raw_union`"); + isize decl_count = 0; + AstNodeArray decls = parse_struct_params(f, &decl_count, true); + Token close = expect_token(f, Token_CloseBrace); + + return make_raw_union_type(f, token, decls, decl_count); + } + + case Token_enum: { + Token token = expect_token(f, Token_enum); + AstNode *base_type = NULL; + Token open, close; + + if (f->curr_token.kind != Token_OpenBrace) { + base_type = parse_type(f); + } + + AstNodeArray fields = make_ast_node_array(f); + + open = expect_token_after(f, Token_OpenBrace, "`enum`"); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *name = parse_identifier(f); + AstNode *value = NULL; + Token eq = empty_token; + if (f->curr_token.kind == Token_Eq) { + eq = expect_token(f, Token_Eq); + value = parse_value(f); + } + AstNode *field = make_field_value(f, name, value, eq); + array_add(&fields, field); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + + close = expect_token(f, Token_CloseBrace); + + return make_enum_type(f, token, base_type, fields); + } + + case Token_proc: + return parse_proc_type(f); + + case Token_OpenParen: { + // NOTE(bill): Skip the paren expression + AstNode *type; + Token open, close; + open = expect_token(f, Token_OpenParen); + type = parse_type(f); + close = expect_token(f, Token_CloseParen); + return type; + // return make_paren_expr(f, type, open, close); + } + + // TODO(bill): Why is this even allowed? Is this a parsing error? + case Token_Colon: + break; + + case Token_Eq: + if (f->prev_token.kind == Token_Colon) + break; + // fallthrough + default: + syntax_error(f->curr_token, + "Expected a type or identifier after `%.*s`, got `%.*s`", LIT(f->prev_token.string), LIT(f->curr_token.string)); + break; + } + + return NULL; +} + + +AstNodeArray parse_results(AstFile *f) { + AstNodeArray results = make_ast_node_array(f); + if (allow_token(f, Token_ArrowRight)) { + if (f->curr_token.kind == Token_OpenParen) { + expect_token(f, Token_OpenParen); + while (f->curr_token.kind != Token_CloseParen && + f->curr_token.kind != Token_EOF) { + array_add(&results, parse_type(f)); + if (f->curr_token.kind != Token_Comma) { + break; + } + next_token(f); + } + expect_token(f, Token_CloseParen); + + return results; + } + + array_add(&results, parse_type(f)); + return results; + } + return results; +} + +Token parse_procedure_signature(AstFile *f, + AstNodeArray *params, + AstNodeArray *results) { + Token proc_token = expect_token(f, Token_proc); + expect_token(f, Token_OpenParen); + *params = parse_parameter_list(f); + expect_token_after(f, Token_CloseParen, "parameter list"); + *results = parse_results(f); + return proc_token; +} + +AstNode *parse_body(AstFile *f) { + AstNodeArray stmts = {0}; + Token open, close; + open = expect_token(f, Token_OpenBrace); + stmts = parse_stmt_list(f); + close = expect_token(f, Token_CloseBrace); + + return make_block_stmt(f, stmts, open, close); +} + + + +AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) { + AstNodeArray params = {0}; + AstNodeArray results = {0}; + + parse_procedure_signature(f, ¶ms, &results); + AstNode *proc_type = make_proc_type(f, proc_token, params, results); + + AstNode *body = NULL; + u64 tags = 0; + String foreign_name = {0}; + String link_name = {0}; + + parse_proc_tags(f, &tags, &foreign_name, &link_name); + + AstNode *curr_proc = f->curr_proc; + f->curr_proc = proc_type; + + if (f->curr_token.kind == Token_OpenBrace) { + if ((tags & ProcTag_foreign) != 0) { + syntax_error(f->curr_token, "A procedure tagged as `#foreign` cannot have a body"); + } + body = parse_body(f); + } + + f->curr_proc = curr_proc; + return make_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name); +} + +AstNode *parse_decl(AstFile *f, AstNodeArray names) { + AstNodeArray values = {0}; + AstNode *type = NULL; + + for_array(i, names) { + AstNode *name = names.e[i]; + if (name->kind == AstNode_Ident) { + String n = name->Ident.string; + // NOTE(bill): Check for reserved identifiers + if (str_eq(n, str_lit("context"))) { + syntax_error(ast_node_token(name), "`context` is a reserved identifier"); + break; + } + } + } + + if (allow_token(f, Token_Colon)) { + if (!allow_token(f, Token_type)) { + type = parse_identifier_or_type(f, 0); + } + } else if (f->curr_token.kind != Token_Eq && f->curr_token.kind != Token_Semicolon) { + syntax_error(f->curr_token, "Expected type separator `:` or `=`"); + } + + bool is_mutable = true; + + if (f->curr_token.kind == Token_Eq || + f->curr_token.kind == Token_Colon) { + if (f->curr_token.kind == Token_Colon) { + is_mutable = false; + } + next_token(f); + + if (f->curr_token.kind == Token_type || + f->curr_token.kind == Token_struct || + f->curr_token.kind == Token_enum || + f->curr_token.kind == Token_union || + f->curr_token.kind == Token_raw_union) { + Token token = f->curr_token; + if (token.kind == Token_type) { + next_token(f); + } + if (names.count != 1) { + syntax_error(ast_node_token(names.e[0]), "You can only declare one type at a time"); + return make_bad_decl(f, names.e[0]->Ident, token); + } + + if (type != NULL) { + syntax_error(f->prev_token, "Expected either `type` or nothing between : and :"); + // NOTE(bill): Do not fail though + } + + return make_type_decl(f, token, names.e[0], parse_type(f)); + } else if (f->curr_token.kind == Token_proc && + is_mutable == false) { + // NOTE(bill): Procedure declarations + Token proc_token = f->curr_token; + AstNode *name = names.e[0]; + if (names.count != 1) { + syntax_error(proc_token, "You can only declare one procedure at a time"); + return make_bad_decl(f, name->Ident, proc_token); + } + + return parse_proc_decl(f, proc_token, name); + + } else { + values = parse_rhs_expr_list(f); + if (values.count > names.count) { + syntax_error(f->curr_token, "Too many values on the right hand side of the declaration"); + } else if (values.count < names.count && !is_mutable) { + syntax_error(f->curr_token, "All constant declarations must be defined"); + } else if (values.count == 0) { + syntax_error(f->curr_token, "Expected an expression for this declaration"); + } + } + } + + if (is_mutable) { + if (type == NULL && values.count == 0) { + syntax_error(f->curr_token, "Missing variable type or initialization"); + return make_bad_decl(f, f->curr_token, f->curr_token); + } + } else { + if (type == NULL && values.count == 0 && names.count > 0) { + syntax_error(f->curr_token, "Missing constant value"); + return make_bad_decl(f, f->curr_token, f->curr_token); + } + } + + if (values.e == NULL) { + values = make_ast_node_array(f); + } + + if (is_mutable) { + return make_var_decl(f, names, type, values); + } + return make_const_decl(f, names, type, values); +} + + +AstNode *parse_if_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use an if statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_if); + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *body = NULL; + AstNode *else_stmt = NULL; + + isize prev_level = f->expr_level; + f->expr_level = -1; + + + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + init = parse_simple_stmt(f); + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); + init = NULL; + } + } + + f->expr_level = prev_level; + + if (cond == NULL) { + syntax_error(f->curr_token, "Expected condition for if statement"); + } + + body = parse_block_stmt(f); + + if (allow_token(f, Token_else)) { + switch (f->curr_token.kind) { + case Token_if: + else_stmt = parse_if_stmt(f); + break; + case Token_OpenBrace: + else_stmt = parse_block_stmt(f); + break; + default: + syntax_error(f->curr_token, "Expected if statement block statement"); + else_stmt = make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); + break; + } + } + + return make_if_stmt(f, token, init, cond, body, else_stmt); +} + +AstNode *parse_return_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a return statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_return); + AstNodeArray results = make_ast_node_array(f); + + if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace && + f->curr_token.pos.line == token.pos.line) { + results = parse_rhs_expr_list(f); + } + if (f->curr_token.kind != Token_CloseBrace) { + expect_semicolon_after_stmt(f, results.e[0]); + } + + return make_return_stmt(f, token, results); +} + +AstNode *parse_for_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a for statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_for); + + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *end = NULL; + AstNode *body = NULL; + + if (f->curr_token.kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f); + if (is_ast_node_complex_stmt(cond)) { + syntax_error(f->curr_token, + "You are not allowed that type of statement in a for statement, it is too complex!"); + } + } + + if (allow_token(f, Token_Semicolon)) { + init = cond; + cond = NULL; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f); + } + expect_token(f, Token_Semicolon); + if (f->curr_token.kind != Token_OpenBrace) { + end = parse_simple_stmt(f); + } + } + f->expr_level = prev_level; + } + body = parse_block_stmt(f); + + cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); + + return make_for_stmt(f, token, init, cond, end, body); +} + +AstNode *parse_case_clause(AstFile *f) { + Token token = f->curr_token; + AstNodeArray list = make_ast_node_array(f); + if (allow_token(f, Token_case)) { + list = parse_rhs_expr_list(f); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? + AstNodeArray stmts = parse_stmt_list(f); + + return make_case_clause(f, token, list, stmts); +} + + +AstNode *parse_type_case_clause(AstFile *f) { + Token token = f->curr_token; + AstNodeArray clause = make_ast_node_array(f); + if (allow_token(f, Token_case)) { + array_add(&clause, parse_type(f)); + } else { + expect_token(f, Token_default); + } + expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? + AstNodeArray stmts = parse_stmt_list(f); + + return make_case_clause(f, token, clause, stmts); +} + + +AstNode *parse_match_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a match statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_match); + AstNode *init = NULL; + AstNode *tag = NULL; + AstNode *body = NULL; + Token open, close; + + if (allow_token(f, Token_type)) { + isize prev_level = f->expr_level; + f->expr_level = -1; + + AstNode *var = parse_identifier(f); + expect_token(f, Token_Colon); + tag = parse_simple_stmt(f); + + f->expr_level = prev_level; + + open = expect_token(f, Token_OpenBrace); + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind == Token_case || + f->curr_token.kind == Token_default) { + array_add(&list, parse_type_case_clause(f)); + } + + close = expect_token(f, Token_CloseBrace); + body = make_block_stmt(f, list, open, close); + + tag = convert_stmt_to_expr(f, tag, str_lit("type match expression")); + return make_type_match_stmt(f, token, tag, var, body); + } else { + if (f->curr_token.kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->curr_token.kind != Token_Semicolon) { + tag = parse_simple_stmt(f); + } + if (allow_token(f, Token_Semicolon)) { + init = tag; + tag = NULL; + if (f->curr_token.kind != Token_OpenBrace) { + tag = parse_simple_stmt(f); + } + } + + f->expr_level = prev_level; + } + + open = expect_token(f, Token_OpenBrace); + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind == Token_case || + f->curr_token.kind == Token_default) { + array_add(&list, parse_case_clause(f)); + } + + close = expect_token(f, Token_CloseBrace); + + body = make_block_stmt(f, list, open, close); + + tag = convert_stmt_to_expr(f, tag, str_lit("match expression")); + return make_match_stmt(f, token, init, tag, body); + } +} + + +AstNode *parse_defer_stmt(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use a defer statement in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_defer); + AstNode *statement = parse_stmt(f); + switch (statement->kind) { + case AstNode_EmptyStmt: + syntax_error(token, "Empty statement after defer (e.g. `;`)"); + break; + case AstNode_DeferStmt: + syntax_error(token, "You cannot defer a defer statement"); + break; + case AstNode_ReturnStmt: + syntax_error(token, "You cannot a return statement"); + break; + } + + return make_defer_stmt(f, token, statement); +} + +AstNode *parse_asm_stmt(AstFile *f) { + Token token = expect_token(f, Token_asm); + bool is_volatile = false; + if (allow_token(f, Token_volatile)) { + is_volatile = true; + } + Token open, close, code_string; + open = expect_token(f, Token_OpenBrace); + code_string = expect_token(f, Token_String); + AstNode *output_list = NULL; + AstNode *input_list = NULL; + AstNode *clobber_list = NULL; + isize output_count = 0; + isize input_count = 0; + isize clobber_count = 0; + + // TODO(bill): Finish asm statement and determine syntax + + // if (f->curr_token.kind != Token_CloseBrace) { + // expect_token(f, Token_Colon); + // } + + close = expect_token(f, Token_CloseBrace); + + return make_asm_stmt(f, token, is_volatile, open, close, code_string, + output_list, input_list, clobber_list, + output_count, input_count, clobber_count); + +} + + + +AstNode *parse_stmt(AstFile *f) { + AstNode *s = NULL; + Token token = f->curr_token; + switch (token.kind) { + case Token_Comment: + next_token(f); + return parse_stmt(f); + + // Operands + case Token_Identifier: + case Token_Integer: + case Token_Float: + case Token_Rune: + case Token_String: + case Token_OpenParen: + case Token_proc: + // Unary Operators + case Token_Add: + case Token_Sub: + case Token_Xor: + case Token_Not: + s = parse_simple_stmt(f); + expect_semicolon_after_stmt(f, s); + return s; + + // TODO(bill): other keywords + case Token_if: return parse_if_stmt(f); + case Token_return: return parse_return_stmt(f); + case Token_for: return parse_for_stmt(f); + case Token_match: return parse_match_stmt(f); + case Token_defer: return parse_defer_stmt(f); + case Token_asm: return parse_asm_stmt(f); + + case Token_break: + case Token_continue: + case Token_fallthrough: + next_token(f); + s = make_branch_stmt(f, token); + expect_semicolon_after_stmt(f, s); + return s; + + + case Token_using: { + AstNode *node = NULL; + + next_token(f); + node = parse_stmt(f); + + bool valid = false; + + switch (node->kind) { + case AstNode_ExprStmt: { + AstNode *e = unparen_expr(node->ExprStmt.expr); + while (e->kind == AstNode_SelectorExpr) { + e = unparen_expr(e->SelectorExpr.selector); + } + if (e->kind == AstNode_Ident) { + valid = true; + } + } break; + case AstNode_VarDecl: + valid = true; + break; + } + + if (!valid) { + syntax_error(token, "Illegal use of `using` statement."); + return make_bad_stmt(f, token, f->curr_token); + } + + + return make_using_stmt(f, token, node); + } break; + + case Token_push_allocator: { + next_token(f); + isize prev_level = f->expr_level; + f->expr_level = -1; + AstNode *expr = parse_expr(f, false); + f->expr_level = prev_level; + + AstNode *body = parse_block_stmt(f); + return make_push_allocator(f, token, expr, body); + } break; + + case Token_push_context: { + next_token(f); + isize prev_level = f->expr_level; + f->expr_level = -1; + AstNode *expr = parse_expr(f, false); + f->expr_level = prev_level; + + AstNode *body = parse_block_stmt(f); + return make_push_context(f, token, expr, body); + } break; + + case Token_Hash: { + s = parse_tag_stmt(f, NULL); + String tag = s->TagStmt.name.string; + if (str_eq(tag, str_lit("shared_global_scope"))) { + if (f->curr_proc == NULL) { + f->is_global_scope = true; + return make_empty_stmt(f, f->curr_token); + } + syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, f->curr_token); + } else if (str_eq(tag, str_lit("import"))) { + // TODO(bill): better error messages + Token import_name = {0}; + Token file_path = expect_token_after(f, Token_String, "#import"); + if (allow_token(f, Token_as)) { + // NOTE(bill): Custom import name + if (f->curr_token.kind == Token_Period) { + import_name = f->curr_token; + import_name.kind = Token_Identifier; + next_token(f); + } else { + import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration"); + } + + if (str_eq(import_name.string, str_lit("_"))) { + syntax_error(token, "Illegal import name: `_`"); + return make_bad_decl(f, token, f->curr_token); + } + } + + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path, import_name, false); + } + syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("load"))) { + // TODO(bill): better error messages + Token file_path = expect_token(f, Token_String); + Token import_name = file_path; + import_name.string = str_lit("."); + + if (f->curr_proc == NULL) { + return make_import_decl(f, s->TagStmt.token, file_path, import_name, true); + } + syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("foreign_system_library"))) { + Token file_path = expect_token(f, Token_String); + if (f->curr_proc == NULL) { + return make_foreign_library(f, s->TagStmt.token, file_path, true); + } + syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("foreign_library"))) { + Token file_path = expect_token(f, Token_String); + if (f->curr_proc == NULL) { + return make_foreign_library(f, s->TagStmt.token, file_path, false); + } + syntax_error(token, "You cannot use #foreign_library within a procedure. This must be done at the file scope"); + return make_bad_decl(f, token, file_path); + } else if (str_eq(tag, str_lit("thread_local"))) { + AstNode *var_decl = parse_simple_stmt(f); + if (var_decl->kind != AstNode_VarDecl) { + syntax_error(token, "#thread_local may only be applied to variable declarations"); + return make_bad_decl(f, token, ast_node_token(var_decl)); + } + if (f->curr_proc != NULL) { + syntax_error(token, "#thread_local is only allowed at the file scope"); + return make_bad_decl(f, token, ast_node_token(var_decl)); + } + var_decl->VarDecl.tags |= VarDeclTag_thread_local; + return var_decl; + } else if (str_eq(tag, str_lit("bounds_check"))) { + s = parse_stmt(f); + s->stmt_state_flags |= StmtStateFlag_bounds_check; + if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); + } + return s; + } else if (str_eq(tag, str_lit("no_bounds_check"))) { + s = parse_stmt(f); + s->stmt_state_flags |= StmtStateFlag_no_bounds_check; + if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) { + syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); + } + return s; + } + + s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument + return s; + } break; + + case Token_OpenBrace: + return parse_block_stmt(f); + + case Token_Semicolon: + s = make_empty_stmt(f, token); + next_token(f); + return s; + } + + syntax_error(token, + "Expected a statement, got `%.*s`", + LIT(token_strings[token.kind])); + fix_advance_to_next_stmt(f); + return make_bad_stmt(f, token, f->curr_token); +} + +AstNodeArray parse_stmt_list(AstFile *f) { + AstNodeArray list = make_ast_node_array(f); + + while (f->curr_token.kind != Token_case && + f->curr_token.kind != Token_default && + f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *stmt = parse_stmt(f); + if (stmt && stmt->kind != AstNode_EmptyStmt) { + array_add(&list, stmt); + } + } + + return list; +} + + +ParseFileError init_ast_file(AstFile *f, String fullpath) { + if (!string_has_extension(fullpath, str_lit("odin"))) { + return ParseFile_WrongExtension; + } + TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath); + if (err == TokenizerInit_None) { + array_init(&f->tokens, heap_allocator()); + { + for (;;) { + Token token = tokenizer_get_token(&f->tokenizer); + if (token.kind == Token_Invalid) { + return ParseFile_InvalidToken; + } + if (token.kind == Token_Comment) { + continue; + } + array_add(&f->tokens, token); + + if (token.kind == Token_EOF) { + break; + } + } + } + + f->curr_token_index = 0; + f->prev_token = f->tokens.e[f->curr_token_index]; + f->curr_token = f->tokens.e[f->curr_token_index]; + + // NOTE(bill): Is this big enough or too small? + isize arena_size = gb_size_of(AstNode); + arena_size *= 2*f->tokens.count; + gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size); + + f->curr_proc = NULL; + + return ParseFile_None; + } + + switch (err) { + case TokenizerInit_NotExists: + return ParseFile_NotFound; + case TokenizerInit_Permission: + return ParseFile_Permission; + case TokenizerInit_Empty: + return ParseFile_EmptyFile; + } + + return ParseFile_InvalidFile; +} + +void destroy_ast_file(AstFile *f) { + gb_arena_free(&f->arena); + array_free(&f->tokens); + gb_free(heap_allocator(), f->tokenizer.fullpath.text); + destroy_tokenizer(&f->tokenizer); +} + +bool init_parser(Parser *p) { + array_init(&p->files, heap_allocator()); + array_init(&p->imports, heap_allocator()); + array_init(&p->foreign_libraries, heap_allocator()); + gb_mutex_init(&p->mutex); + return true; +} + +void destroy_parser(Parser *p) { + // TODO(bill): Fix memory leak + for_array(i, p->files) { + destroy_ast_file(&p->files.e[i]); + } +#if 1 + for_array(i, p->imports) { + // gb_free(heap_allocator(), p->imports[i].text); + } +#endif + array_free(&p->files); + array_free(&p->imports); + array_free(&p->foreign_libraries); + gb_mutex_destroy(&p->mutex); +} + +// NOTE(bill): Returns true if it's added +bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) { + gb_mutex_lock(&p->mutex); + + for_array(i, p->imports) { + String import = p->imports.e[i].path; + if (str_eq(import, path)) { + return false; + } + } + + ImportedFile item; + item.path = path; + item.rel_path = rel_path; + item.pos = pos; + array_add(&p->imports, item); + + gb_mutex_unlock(&p->mutex); + + return true; +} + +String get_fullpath_relative(gbAllocator a, String base_dir, String path) { + String res = {0}; + isize str_len = base_dir.len+path.len; + + u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); + + isize i = 0; + gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len; + gb_memmove(str+i, path.text, path.len); + str[str_len] = '\0'; + res = path_to_fullpath(a, make_string(str, str_len)); + gb_free(heap_allocator(), str); + return res; +} + +String get_fullpath_core(gbAllocator a, String path) { + String module_dir = get_module_dir(); + String res = {0}; + + char core[] = "core/"; + isize core_len = gb_size_of(core)-1; + + isize str_len = module_dir.len + core_len + path.len; + u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1); + + gb_memmove(str, module_dir.text, module_dir.len); + gb_memmove(str+module_dir.len, core, core_len); + gb_memmove(str+module_dir.len+core_len, path.text, path.len); + str[str_len] = '\0'; + + res = path_to_fullpath(a, make_string(str, str_len)); + gb_free(heap_allocator(), str); + return res; +} + +// NOTE(bill): Returns true if it's added +bool try_add_foreign_library_path(Parser *p, String import_file) { + gb_mutex_lock(&p->mutex); + + for_array(i, p->foreign_libraries) { + String import = p->foreign_libraries.e[i]; + if (str_eq(import, import_file)) { + return false; + } + } + array_add(&p->foreign_libraries, import_file); + gb_mutex_unlock(&p->mutex); + return true; +} + +gb_global Rune illegal_import_runes[] = { + '"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f', + '\\', // NOTE(bill): Disallow windows style filepaths + '!', '$', '%', '^', '&', '*', '(', ')', '=', '+', + '[', ']', '{', '}', + ';', ':', '#', + '|', ',', '<', '>', '?', +}; + +bool is_import_path_valid(String path) { + if (path.len > 0) { + u8 *start = path.text; + u8 *end = path.text + path.len; + u8 *curr = start; + Rune r = -1; + while (curr < end) { + isize width = 1; + r = curr[0]; + if (r >= 0x80) { + width = gb_utf8_decode(curr, end-curr, &r); + if (r == GB_RUNE_INVALID && width == 1) + return false; + else if (r == GB_RUNE_BOM && curr-start > 0) + return false; + } + + for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) { + if (r == illegal_import_runes[i]) + return false; + } + + curr += width; + } + + return true; + } + return false; +} + +String get_filepath_extension(String path) { + isize dot = 0; + bool seen_slash = false; + for (isize i = path.len-1; i >= 0; i--) { + u8 c = path.text[i]; + if (c == '/' || c == '\\') { + seen_slash = true; + } + + if (c == '.') { + if (seen_slash) { + return str_lit(""); + } + + dot = i; + break; + } + } + return make_string(path.text, dot); +} + +void parse_file(Parser *p, AstFile *f) { + String filepath = f->tokenizer.fullpath; + String base_dir = filepath; + for (isize i = filepath.len-1; i >= 0; i--) { + if (base_dir.text[i] == '\\' || + base_dir.text[i] == '/') { + break; + } + base_dir.len--; + } + + + f->decls = parse_stmt_list(f); + + for_array(i, f->decls) { + AstNode *node = f->decls.e[i]; + if (!is_ast_node_decl(node) && + node->kind != AstNode_BadStmt && + node->kind != AstNode_EmptyStmt) { + // NOTE(bill): Sanity check + syntax_error(ast_node_token(node), "Only declarations are allowed at file scope"); + } else { + if (node->kind == AstNode_ImportDecl) { + AstNodeImportDecl *id = &node->ImportDecl; + String file_str = id->relpath.string; + + if (!is_import_path_valid(file_str)) { + if (id->is_load) { + syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str)); + } else { + syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str)); + } + // NOTE(bill): It's a naughty name + f->decls.e[i] = make_bad_decl(f, id->token, id->token); + continue; + } + + gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator + + String rel_path = get_fullpath_relative(allocator, base_dir, file_str); + String import_file = rel_path; + if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated + String abs_path = get_fullpath_core(allocator, file_str); + if (gb_file_exists(cast(char *)abs_path.text)) { + import_file = abs_path; + } + } + + id->fullpath = import_file; + try_add_import_path(p, import_file, file_str, ast_node_token(node).pos); + + } else if (node->kind == AstNode_ForeignLibrary) { + AstNodeForeignLibrary *id = &node->ForeignLibrary; + String file_str = id->filepath.string; + + if (!is_import_path_valid(file_str)) { + if (id->is_system) { + syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path"); + } else { + syntax_error(ast_node_token(node), "Invalid `foreign_library` path"); + } + // NOTE(bill): It's a naughty name + f->decls.e[i] = make_bad_decl(f, id->token, id->token); + continue; + } + + if (!id->is_system) { + gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator + + String rel_path = get_fullpath_relative(allocator, base_dir, file_str); + String import_file = rel_path; + if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated + String abs_path = get_fullpath_core(allocator, file_str); + if (gb_file_exists(cast(char *)abs_path.text)) { + import_file = abs_path; + } + } + file_str = import_file; + } + + try_add_foreign_library_path(p, file_str); + } + } + } +} + + + +ParseFileError parse_files(Parser *p, char *init_filename) { + char *fullpath_str = gb_path_get_full_name(heap_allocator(), init_filename); + String init_fullpath = make_string_c(fullpath_str); + TokenPos init_pos = {0}; + ImportedFile init_imported_file = {init_fullpath, init_fullpath, init_pos}; + array_add(&p->imports, init_imported_file); + p->init_fullpath = init_fullpath; + + { + String s = get_fullpath_core(heap_allocator(), str_lit("_preload.odin")); + ImportedFile runtime_file = {s, s, init_pos}; + array_add(&p->imports, runtime_file); + } + { + String s = get_fullpath_core(heap_allocator(), str_lit("_soft_numbers.odin")); + ImportedFile runtime_file = {s, s, init_pos}; + array_add(&p->imports, runtime_file); + } + + for_array(i, p->imports) { + ImportedFile imported_file = p->imports.e[i]; + String import_path = imported_file.path; + String import_rel_path = imported_file.rel_path; + TokenPos pos = imported_file.pos; + AstFile file = {0}; + ParseFileError err = init_ast_file(&file, import_path); + + if (err != ParseFile_None) { + if (pos.line != 0) { + gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); + } + gb_printf_err("Failed to parse file: %.*s\n\t", LIT(import_rel_path)); + switch (err) { + case ParseFile_WrongExtension: + gb_printf_err("Invalid file extension: File must have the extension `.odin`"); + break; + case ParseFile_InvalidFile: + gb_printf_err("Invalid file"); + break; + case ParseFile_EmptyFile: + gb_printf_err("File is empty"); + break; + case ParseFile_Permission: + gb_printf_err("File permissions problem"); + break; + case ParseFile_NotFound: + gb_printf_err("File cannot be found"); + break; + case ParseFile_InvalidToken: + gb_printf_err("Invalid token found in file"); + break; + } + gb_printf_err("\n"); + return err; + } + parse_file(p, &file); + + { + gb_mutex_lock(&p->mutex); + file.id = p->files.count; + array_add(&p->files, file); + gb_mutex_unlock(&p->mutex); + } + } + + for_array(i, p->files) { + p->total_token_count += p->files.e[i].tokens.count; + } + + + return ParseFile_None; +} + + diff --git a/src/printer.c b/src/printer.c new file mode 100644 index 000000000..4d7184631 --- /dev/null +++ b/src/printer.c @@ -0,0 +1,221 @@ + + +gb_inline void print_indent(isize indent) { + while (indent --> 0) + gb_printf(" "); +} + +void print_ast(AstNode *node, isize indent) { + if (node == NULL) + return; + + switch (node->kind) { + case AstNode_BasicLit: + print_indent(indent); + print_token(node->BasicLit); + break; + case AstNode_Ident: + print_indent(indent); + print_token(node->Ident); + break; + case AstNode_ProcLit: + print_indent(indent); + gb_printf("(proc lit)\n"); + print_ast(node->ProcLit.type, indent+1); + print_ast(node->ProcLit.body, indent+1); + break; + + case AstNode_CompoundLit: + print_indent(indent); + gb_printf("(compound lit)\n"); + print_ast(node->CompoundLit.type, indent+1); + for_array(i, node->CompoundLit.elems) { + print_ast(node->CompoundLit.elems[i], indent+1); + } + break; + + + case AstNode_TagExpr: + print_indent(indent); + gb_printf("(tag)\n"); + print_indent(indent+1); + print_token(node->TagExpr.name); + print_ast(node->TagExpr.expr, indent+1); + break; + + case AstNode_UnaryExpr: + print_indent(indent); + print_token(node->UnaryExpr.op); + print_ast(node->UnaryExpr.expr, indent+1); + break; + case AstNode_BinaryExpr: + print_indent(indent); + print_token(node->BinaryExpr.op); + print_ast(node->BinaryExpr.left, indent+1); + print_ast(node->BinaryExpr.right, indent+1); + break; + case AstNode_CallExpr: + print_indent(indent); + gb_printf("(call)\n"); + print_ast(node->CallExpr.proc, indent+1); + for_array(i, node->CallExpr.args) { + print_ast(node->CallExpr.args[i], indent+1); + } + break; + case AstNode_SelectorExpr: + print_indent(indent); + gb_printf(".\n"); + print_ast(node->SelectorExpr.expr, indent+1); + print_ast(node->SelectorExpr.selector, indent+1); + break; + case AstNode_IndexExpr: + print_indent(indent); + gb_printf("([])\n"); + print_ast(node->IndexExpr.expr, indent+1); + print_ast(node->IndexExpr.index, indent+1); + break; + case AstNode_DerefExpr: + print_indent(indent); + gb_printf("(deref)\n"); + print_ast(node->DerefExpr.expr, indent+1); + break; + + + case AstNode_ExprStmt: + print_ast(node->ExprStmt.expr, indent); + break; + case AstNode_IncDecStmt: + print_indent(indent); + print_token(node->IncDecStmt.op); + print_ast(node->IncDecStmt.expr, indent+1); + break; + case AstNode_AssignStmt: + print_indent(indent); + print_token(node->AssignStmt.op); + for_array(i, node->AssignStmt.lhs) { + print_ast(node->AssignStmt.lhs[i], indent+1); + } + for_array(i, node->AssignStmt.rhs) { + print_ast(node->AssignStmt.rhs[i], indent+1); + } + break; + case AstNode_BlockStmt: + print_indent(indent); + gb_printf("(block)\n"); + for_array(i, node->BlockStmt.stmts) { + print_ast(node->BlockStmt.stmts[i], indent+1); + } + break; + + case AstNode_IfStmt: + print_indent(indent); + gb_printf("(if)\n"); + print_ast(node->IfStmt.cond, indent+1); + print_ast(node->IfStmt.body, indent+1); + if (node->IfStmt.else_stmt) { + print_indent(indent); + gb_printf("(else)\n"); + print_ast(node->IfStmt.else_stmt, indent+1); + } + break; + case AstNode_ReturnStmt: + print_indent(indent); + gb_printf("(return)\n"); + for_array(i, node->ReturnStmt.results) { + print_ast(node->ReturnStmt.results[i], indent+1); + } + break; + case AstNode_ForStmt: + print_indent(indent); + gb_printf("(for)\n"); + print_ast(node->ForStmt.init, indent+1); + print_ast(node->ForStmt.cond, indent+1); + print_ast(node->ForStmt.post, indent+1); + print_ast(node->ForStmt.body, indent+1); + break; + case AstNode_DeferStmt: + print_indent(indent); + gb_printf("(defer)\n"); + print_ast(node->DeferStmt.stmt, indent+1); + break; + + + case AstNode_VarDecl: + print_indent(indent); + gb_printf("(decl:var)\n"); + for_array(i, node->VarDecl.names) { + print_ast(node->VarDecl.names[i], indent+1); + } + print_ast(node->VarDecl.type, indent+1); + for_array(i, node->VarDecl.values) { + print_ast(node->VarDecl.values[i], indent+1); + } + break; + case AstNode_ConstDecl: + print_indent(indent); + gb_printf("(decl:const)\n"); + for_array(i, node->VarDecl.names) { + print_ast(node->VarDecl.names[i], indent+1); + } + print_ast(node->VarDecl.type, indent+1); + for_array(i, node->VarDecl.values) { + print_ast(node->VarDecl.values[i], indent+1); + } + break; + case AstNode_ProcDecl: + print_indent(indent); + gb_printf("(decl:proc)\n"); + print_ast(node->ProcDecl.type, indent+1); + print_ast(node->ProcDecl.body, indent+1); + break; + + case AstNode_TypeDecl: + print_indent(indent); + gb_printf("(type)\n"); + print_ast(node->TypeDecl.name, indent+1); + print_ast(node->TypeDecl.type, indent+1); + break; + + case AstNode_ProcType: + print_indent(indent); + gb_printf("(type:proc)(%td -> %td)\n", node->ProcType.params.count, node->ProcType.results.count); + for_array(i, node->ProcType.params) { + print_ast(node->ProcType.params[i], indent+1); + } + if (node->ProcType.results.count > 0) { + print_indent(indent+1); + gb_printf("->\n"); + for_array(i, node->ProcType.results) { + print_ast(node->ProcType.results[i], indent+1); + } + } + break; + case AstNode_Parameter: + for_array(i, node->Parameter.names) { + print_ast(node->Parameter.names[i], indent+1); + } + print_ast(node->Parameter.type, indent); + break; + case AstNode_PointerType: + print_indent(indent); + print_token(node->PointerType.token); + print_ast(node->PointerType.type, indent+1); + break; + case AstNode_ArrayType: + print_indent(indent); + gb_printf("[]\n"); + print_ast(node->ArrayType.count, indent+1); + print_ast(node->ArrayType.elem, indent+1); + break; + case AstNode_StructType: + print_indent(indent); + gb_printf("(struct)\n"); + for_array(i, node->StructType.decls) { + print_ast(node->StructType.decls[i], indent+1); + } + break; + } + + // if (node->next) + // print_ast(node->next, indent); +} diff --git a/src/ssa.c b/src/ssa.c new file mode 100644 index 000000000..b34bc7c51 --- /dev/null +++ b/src/ssa.c @@ -0,0 +1,5419 @@ +typedef struct ssaProcedure ssaProcedure; +typedef struct ssaBlock ssaBlock; +typedef struct ssaValue ssaValue; +typedef struct ssaDebugInfo ssaDebugInfo; + +typedef Array(ssaValue *) ssaValueArray; + +#define MAP_TYPE ssaValue * +#define MAP_PROC map_ssa_value_ +#define MAP_NAME MapSsaValue +#include "map.c" + +#define MAP_TYPE ssaDebugInfo * +#define MAP_PROC map_ssa_debug_info_ +#define MAP_NAME MapSsaDebugInfo +#include "map.c" + +typedef struct ssaModule { + CheckerInfo * info; + BaseTypeSizes sizes; + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; + bool generate_debug_info; + + u32 stmt_state_flags; + + // String source_filename; + String layout; + // String triple; + + + MapEntity min_dep_map; // Key: Entity * + MapSsaValue values; // Key: Entity * + MapSsaValue members; // Key: String + MapString type_names; // Key: Type * + MapSsaDebugInfo debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice + + Array(ssaProcedure *) procs; // NOTE(bill): All procedures with bodies + ssaValueArray procs_to_generate; // NOTE(bill): Procedures to generate +} ssaModule; + +// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) +typedef struct ssaDomNode { + ssaBlock * idom; // Parent (Immediate Dominator) + Array(ssaBlock *) children; + i32 pre, post; // Ordering in tree +} ssaDomNode; + + +typedef struct ssaBlock { + i32 index; + String label; + ssaProcedure *parent; + AstNode * node; // Can be NULL + Scope * scope; + isize scope_index; + ssaDomNode dom; + i32 gaps; + + ssaValueArray instrs; + ssaValueArray locals; + + Array(ssaBlock *) preds; + Array(ssaBlock *) succs; +} ssaBlock; + +typedef struct ssaTargetList ssaTargetList; +struct ssaTargetList { + ssaTargetList *prev; + ssaBlock * break_; + ssaBlock * continue_; + ssaBlock * fallthrough_; +}; + +typedef enum ssaDeferExitKind { + ssaDeferExit_Default, + ssaDeferExit_Return, + ssaDeferExit_Branch, +} ssaDeferExitKind; +typedef enum ssaDeferKind { + ssaDefer_Node, + ssaDefer_Instr, +} ssaDeferKind; + +typedef struct ssaDefer { + ssaDeferKind kind; + isize scope_index; + ssaBlock * block; + union { + AstNode *stmt; + // NOTE(bill): `instr` will be copied every time to create a new one + ssaValue *instr; + }; +} ssaDefer; + +typedef struct ssaProcedure ssaProcedure; +struct ssaProcedure { + ssaProcedure * parent; + Array(ssaProcedure *) children; + + Entity * entity; + ssaModule * module; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; + + ssaValueArray params; + Array(ssaDefer) defer_stmts; + Array(ssaBlock *) blocks; + i32 scope_index; + ssaBlock * decl_block; + ssaBlock * entry_block; + ssaBlock * curr_block; + ssaTargetList * target_list; + ssaValueArray referrers; + + i32 local_count; + i32 instr_count; + i32 block_count; +}; + +#define SSA_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" +#define SSA_TYPE_INFO_DATA_NAME "__$type_info_data" +#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" + + +#define SSA_INSTR_KINDS \ + SSA_INSTR_KIND(Comment, struct { String text; }) \ + SSA_INSTR_KIND(Local, struct { \ + Entity * entity; \ + Type * type; \ + bool zero_initialized; \ + ssaValueArray referrers; \ + }) \ + SSA_INSTR_KIND(ZeroInit, struct { ssaValue *address; }) \ + SSA_INSTR_KIND(Store, struct { ssaValue *address, *value; }) \ + SSA_INSTR_KIND(Load, struct { Type *type; ssaValue *address; }) \ + SSA_INSTR_KIND(PtrOffset, struct { \ + ssaValue *address; \ + ssaValue *offset; \ + }) \ + SSA_INSTR_KIND(ArrayElementPtr, struct { \ + ssaValue *address; \ + Type * result_type; \ + ssaValue *elem_index; \ + }) \ + SSA_INSTR_KIND(StructElementPtr, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 elem_index; \ + }) \ + SSA_INSTR_KIND(ArrayExtractValue, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + SSA_INSTR_KIND(StructExtractValue, struct { \ + ssaValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + SSA_INSTR_KIND(UnionTagPtr, struct { \ + ssaValue *address; \ + Type *type; /* ^int */ \ + }) \ + SSA_INSTR_KIND(UnionTagValue, struct { \ + ssaValue *address; \ + Type *type; /* int */ \ + }) \ + SSA_INSTR_KIND(Conv, struct { \ + ssaConvKind kind; \ + ssaValue *value; \ + Type *from, *to; \ + }) \ + SSA_INSTR_KIND(Jump, struct { ssaBlock *block; }) \ + SSA_INSTR_KIND(If, struct { \ + ssaValue *cond; \ + ssaBlock *true_block; \ + ssaBlock *false_block; \ + }) \ + SSA_INSTR_KIND(Return, struct { ssaValue *value; }) \ + SSA_INSTR_KIND(Select, struct { \ + ssaValue *cond; \ + ssaValue *true_value; \ + ssaValue *false_value; \ + }) \ + SSA_INSTR_KIND(Phi, struct { ssaValueArray edges; Type *type; }) \ + SSA_INSTR_KIND(Unreachable, i32) \ + SSA_INSTR_KIND(BinaryOp, struct { \ + Type * type; \ + TokenKind op; \ + ssaValue *left, *right; \ + }) \ + SSA_INSTR_KIND(Call, struct { \ + Type * type; /* return type */ \ + ssaValue *value; \ + ssaValue **args; \ + isize arg_count; \ + }) \ + SSA_INSTR_KIND(VectorExtractElement, struct { \ + ssaValue *vector; \ + ssaValue *index; \ + }) \ + SSA_INSTR_KIND(VectorInsertElement, struct { \ + ssaValue *vector; \ + ssaValue *elem; \ + ssaValue *index; \ + }) \ + SSA_INSTR_KIND(VectorShuffle, struct { \ + ssaValue *vector; \ + i32 * indices; \ + i32 index_count; \ + Type * type; \ + }) \ + SSA_INSTR_KIND(StartupRuntime, i32) \ + SSA_INSTR_KIND(BoundsCheck, struct { \ + TokenPos pos; \ + ssaValue *index; \ + ssaValue *len; \ + }) \ + SSA_INSTR_KIND(SliceBoundsCheck, struct { \ + TokenPos pos; \ + ssaValue *low; \ + ssaValue *high; \ + ssaValue *max; \ + bool is_substring; \ + }) + +#define SSA_CONV_KINDS \ + SSA_CONV_KIND(trunc) \ + SSA_CONV_KIND(zext) \ + SSA_CONV_KIND(fptrunc) \ + SSA_CONV_KIND(fpext) \ + SSA_CONV_KIND(fptoui) \ + SSA_CONV_KIND(fptosi) \ + SSA_CONV_KIND(uitofp) \ + SSA_CONV_KIND(sitofp) \ + SSA_CONV_KIND(ptrtoint) \ + SSA_CONV_KIND(inttoptr) \ + SSA_CONV_KIND(bitcast) + +typedef enum ssaInstrKind { + ssaInstr_Invalid, +#define SSA_INSTR_KIND(x, ...) GB_JOIN2(ssaInstr_, x), + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +} ssaInstrKind; + +String const ssa_instr_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define SSA_INSTR_KIND(x, ...) {cast(u8 *)#x, gb_size_of(#x)-1}, + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +}; + +typedef enum ssaConvKind { + ssaConv_Invalid, +#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x), + SSA_CONV_KINDS +#undef SSA_CONV_KIND +} ssaConvKind; + +String const ssa_conv_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}, + SSA_CONV_KINDS +#undef SSA_CONV_KIND +}; + +#define SSA_INSTR_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(ssaInstr, k); + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND + +typedef struct ssaInstr ssaInstr; +struct ssaInstr { + ssaInstrKind kind; + + ssaBlock *parent; + Type *type; + + union { +#define SSA_INSTR_KIND(k, ...) GB_JOIN2(ssaInstr, k) k; + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND + }; +}; + + +typedef enum ssaValueKind { + ssaValue_Invalid, + + ssaValue_Constant, + ssaValue_ConstantSlice, + ssaValue_Nil, + ssaValue_TypeName, + ssaValue_Global, + ssaValue_Param, + + ssaValue_Proc, + ssaValue_Block, + ssaValue_Instr, + + ssaValue_Count, +} ssaValueKind; + +typedef struct ssaValueConstant { + Type * type; + ExactValue value; +} ssaValueConstant; + +typedef struct ssaValueConstantSlice { + Type * type; + ssaValue *backing_array; + i64 count; +} ssaValueConstantSlice; + +typedef struct ssaValueNil { + Type *type; +} ssaValueNil; + +typedef struct ssaValueTypeName { + Type * type; + String name; +} ssaValueTypeName; + +typedef struct ssaValueGlobal { + Entity * entity; + Type * type; + ssaValue * value; + ssaValueArray referrers; + bool is_constant; + bool is_private; + bool is_thread_local; + bool is_unnamed_addr; +} ssaValueGlobal; + +typedef struct ssaValueParam { + ssaProcedure *parent; + Entity * entity; + Type * type; + ssaValueArray referrers; +} ssaValueParam; + +typedef struct ssaValue { + ssaValueKind kind; + i32 index; + union { + ssaValueConstant Constant; + ssaValueConstantSlice ConstantSlice; + ssaValueNil Nil; + ssaValueTypeName TypeName; + ssaValueGlobal Global; + ssaValueParam Param; + ssaProcedure Proc; + ssaBlock Block; + ssaInstr Instr; + }; +} ssaValue; + +gb_global ssaValue *v_zero = NULL; +gb_global ssaValue *v_one = NULL; +gb_global ssaValue *v_zero32 = NULL; +gb_global ssaValue *v_one32 = NULL; +gb_global ssaValue *v_two32 = NULL; +gb_global ssaValue *v_false = NULL; +gb_global ssaValue *v_true = NULL; + +typedef enum ssaAddrKind { + ssaAddr_Default, + ssaAddr_Vector, +} ssaAddrKind; + +typedef struct ssaAddr { + ssaValue * addr; + AstNode * expr; // NOTE(bill): Just for testing - probably remove later + ssaAddrKind kind; + union { + struct { ssaValue *index; } Vector; + }; +} ssaAddr; + +ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) { + ssaAddr v = {addr, expr}; + return v; +} +ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) { + ssaAddr v = ssa_make_addr(addr, expr); + v.kind = ssaAddr_Vector; + v.Vector.index = index; + return v; +} + + + +typedef enum ssaDebugEncoding { + ssaDebugBasicEncoding_Invalid = 0, + + ssaDebugBasicEncoding_address = 1, + ssaDebugBasicEncoding_boolean = 2, + ssaDebugBasicEncoding_float = 3, + ssaDebugBasicEncoding_signed = 4, + ssaDebugBasicEncoding_signed_char = 5, + ssaDebugBasicEncoding_unsigned = 6, + ssaDebugBasicEncoding_unsigned_char = 7, + + ssaDebugBasicEncoding_member = 13, + ssaDebugBasicEncoding_pointer_type = 15, + ssaDebugBasicEncoding_typedef = 22, + + ssaDebugBasicEncoding_array_type = 1, + ssaDebugBasicEncoding_enumeration_type = 4, + ssaDebugBasicEncoding_structure_type = 19, + ssaDebugBasicEncoding_union_type = 23, + +} ssaDebugEncoding; + +typedef enum ssaDebugInfoKind { + ssaDebugInfo_Invalid, + + ssaDebugInfo_CompileUnit, + ssaDebugInfo_File, + ssaDebugInfo_Scope, + ssaDebugInfo_Proc, + ssaDebugInfo_AllProcs, + + ssaDebugInfo_BasicType, // basic types + ssaDebugInfo_ProcType, + ssaDebugInfo_DerivedType, // pointer, typedef + ssaDebugInfo_CompositeType, // array, struct, enum, (raw_)union + ssaDebugInfo_Enumerator, // For ssaDebugInfo_CompositeType if enum + ssaDebugInfo_GlobalVariable, + ssaDebugInfo_LocalVariable, + + + ssaDebugInfo_Count, +} ssaDebugInfoKind; + +typedef struct ssaDebugInfo ssaDebugInfo; +struct ssaDebugInfo { + ssaDebugInfoKind kind; + i32 id; + + union { + struct { + AstFile * file; + String producer; + ssaDebugInfo *all_procs; + } CompileUnit; + struct { + AstFile *file; + String filename; + String directory; + } File; + struct { + ssaDebugInfo *parent; + ssaDebugInfo *file; + TokenPos pos; + Scope * scope; // Actual scope + } Scope; + struct { + Entity * entity; + String name; + ssaDebugInfo *file; + TokenPos pos; + } Proc; + struct { + Array(ssaDebugInfo *) procs; + } AllProcs; + + + struct { + String name; + i32 size; + i32 align; + ssaDebugEncoding encoding; + } BasicType; + struct { + ssaDebugInfo * return_type; + Array(ssaDebugInfo *) param_types; + } ProcType; + struct { + ssaDebugInfo * base_type; + ssaDebugEncoding encoding; + } DerivedType; + struct { + ssaDebugEncoding encoding; + String name; + String identifier; + ssaDebugInfo * file; + TokenPos pos; + i32 size; + i32 align; + Array(ssaDebugInfo *) elements; + } CompositeType; + struct { + String name; + i64 value; + } Enumerator; + struct { + String name; + String linkage_name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + ssaValue *variable; + ssaDebugInfo *declaration; + } GlobalVariable; + struct { + String name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + i32 arg; // Non-zero if proc parameter + ssaDebugInfo *type; + } LocalVariable; + }; +}; + +typedef struct ssaGen { + ssaModule module; + gbFile output_file; + bool opt_called; +} ssaGen; + +ssaValue *ssa_lookup_member(ssaModule *m, String name) { + ssaValue **v = map_ssa_value_get(&m->members, hash_string(name)); + if (v != NULL) { + return *v; + } + return NULL; +} + + +Type *ssa_type(ssaValue *value); +Type *ssa_instr_type(ssaInstr *instr) { + switch (instr->kind) { + case ssaInstr_Local: + return instr->Local.type; + case ssaInstr_Load: + return instr->Load.type; + case ssaInstr_StructElementPtr: + return instr->StructElementPtr.result_type; + case ssaInstr_ArrayElementPtr: + return instr->ArrayElementPtr.result_type; + case ssaInstr_PtrOffset: + return ssa_type(instr->PtrOffset.address); + case ssaInstr_Phi: + return instr->Phi.type; + case ssaInstr_ArrayExtractValue: + return instr->ArrayExtractValue.result_type; + case ssaInstr_StructExtractValue: + return instr->StructExtractValue.result_type; + case ssaInstr_UnionTagPtr: + return instr->UnionTagPtr.type; + case ssaInstr_UnionTagValue: + return instr->UnionTagValue.type; + case ssaInstr_BinaryOp: + return instr->BinaryOp.type; + case ssaInstr_Conv: + return instr->Conv.to; + case ssaInstr_Select: + return ssa_type(instr->Select.true_value); + case ssaInstr_Call: { + Type *pt = base_type(instr->Call.type); + if (pt != NULL) { + if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) { + return pt->Tuple.variables[0]->type; + } + return pt; + } + return NULL; + } break; + case ssaInstr_VectorExtractElement: { + Type *vt = ssa_type(instr->VectorExtractElement.vector); + Type *bt = base_vector_type(vt); + GB_ASSERT(!is_type_vector(bt)); + return bt; + } break; + case ssaInstr_VectorInsertElement: + return ssa_type(instr->VectorInsertElement.vector); + case ssaInstr_VectorShuffle: + return instr->VectorShuffle.type; + } + return NULL; +} + +Type *ssa_type(ssaValue *value) { + switch (value->kind) { + case ssaValue_Constant: + return value->Constant.type; + case ssaValue_ConstantSlice: + return value->ConstantSlice.type; + case ssaValue_Nil: + return value->Nil.type; + case ssaValue_TypeName: + return value->TypeName.type; + case ssaValue_Global: + return value->Global.type; + case ssaValue_Param: + return value->Param.type; + case ssaValue_Proc: + return value->Proc.type; + case ssaValue_Instr: + return ssa_instr_type(&value->Instr); + } + return NULL; +} + +Type *ssa_addr_type(ssaAddr lval) { + if (lval.addr != NULL) { + Type *t = ssa_type(lval.addr); + GB_ASSERT(is_type_pointer(t)); + return type_deref(t); + } + return NULL; +} + + + +bool ssa_is_blank_ident(AstNode *node) { + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + return is_blank_ident(i->string); + } + return false; +} + + +ssaInstr *ssa_get_last_instr(ssaBlock *block) { + if (block != NULL) { + isize len = block->instrs.count; + if (len > 0) { + ssaValue *v = block->instrs.e[len-1]; + GB_ASSERT(v->kind == ssaValue_Instr); + return &v->Instr; + } + } + return NULL; + +} + +bool ssa_is_instr_terminating(ssaInstr *i) { + if (i != NULL) { + switch (i->kind) { + case ssaInstr_Return: + case ssaInstr_Unreachable: + return true; + } + } + + return false; +} + + +void ssa_add_edge(ssaBlock *from, ssaBlock *to) { + array_add(&from->succs, to); + array_add(&to->preds, from); +} + +void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) { + if (instr->kind == ssaValue_Instr) { + instr->Instr.parent = parent; + } +} + +ssaValueArray *ssa_value_referrers(ssaValue *v) { + switch (v->kind) { + case ssaValue_Global: + return &v->Global.referrers; + case ssaValue_Param: + return &v->Param.referrers; + case ssaValue_Proc: { + if (v->Proc.parent != NULL) { + return &v->Proc.referrers; + } + return NULL; + } + case ssaValue_Instr: { + ssaInstr *i = &v->Instr; + switch (i->kind) { + case ssaInstr_Local: + return &i->Local.referrers; + } + } break; + } + + return NULL; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Make +// +//////////////////////////////////////////////////////////////// + +void ssa_module_add_value (ssaModule *m, Entity *e, ssaValue *v); +ssaValue *ssa_emit_zero_init (ssaProcedure *p, ssaValue *address); +ssaValue *ssa_emit_comment (ssaProcedure *p, String text); +ssaValue *ssa_emit_store (ssaProcedure *p, ssaValue *address, ssaValue *value); +ssaValue *ssa_emit_load (ssaProcedure *p, ssaValue *address); +void ssa_emit_jump (ssaProcedure *proc, ssaBlock *block); +ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t); +ssaValue *ssa_type_info (ssaProcedure *proc, Type *type); +ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr); +void ssa_build_stmt (ssaProcedure *proc, AstNode *node); +void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); +void ssa_build_defer_stmt (ssaProcedure *proc, ssaDefer d); +ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr); +void ssa_build_proc (ssaValue *value, ssaProcedure *parent); +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name); + + + + +ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) { + ssaValue *v = gb_alloc_item(a, ssaValue); + v->kind = kind; + return v; +} +ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) { + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr); + v->Instr.kind = kind; + proc->instr_count++; + return v; +} +ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) { + ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo); + di->kind = kind; + return di; +} + + + + +ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName); + v->TypeName.name = name; + v->TypeName.type = type; + return v; +} + +ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Global); + v->Global.entity = e; + v->Global.type = make_type_pointer(a, e->type); + v->Global.value = value; + array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Param); + v->Param.parent = parent; + v->Param.entity = e; + v->Param.type = e->type; + array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Nil); + v->Nil.type = type; + return v; +} + + + +ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, bool zero_initialized) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local); + ssaInstr *i = &v->Instr; + i->Local.entity = e; + i->Local.type = make_type_pointer(p->module->allocator, e->type); + i->Local.zero_initialized = zero_initialized; + array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + ssa_module_add_value(p->module, e, v); + return v; +} + + +ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store); + ssaInstr *i = &v->Instr; + i->Store.address = address; + i->Store.value = value; + return v; +} + +ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit); + ssaInstr *i = &v->Instr; + i->ZeroInit.address = address; + return v; +} + +ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load); + ssaInstr *i = &v->Instr; + i->Load.address = address; + i->Load.type = type_deref(ssa_type(address)); + return v; +} + +ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr); + ssaInstr *i = &v->Instr; + Type *t = ssa_type(address); + GB_ASSERT(is_type_pointer(t)); + t = base_type(type_deref(t)); + GB_ASSERT(is_type_array(t) || is_type_vector(t)); + + Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem); + + i->ArrayElementPtr.address = address; + i->ArrayElementPtr.elem_index = elem_index; + i->ArrayElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr); + ssaInstr *i = &v->Instr; + i->StructElementPtr.address = address; + i->StructElementPtr.elem_index = elem_index; + i->StructElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset); + ssaInstr *i = &v->Instr; + i->PtrOffset.address = address; + i->PtrOffset.offset = offset; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + GB_ASSERT_MSG(is_type_integer(ssa_type(offset)), + "%s", type_to_string(ssa_type(address))); + + return v; +} + + + +ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue); + ssaInstr *i = &v->Instr; + i->ArrayExtractValue.address = address; + i->ArrayExtractValue.index = index; + Type *t = base_type(ssa_type(address)); + GB_ASSERT(is_type_array(t)); + i->ArrayExtractValue.result_type = t->Array.elem; + return v; +} + +ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue); + ssaInstr *i = &v->Instr; + i->StructExtractValue.address = address; + i->StructExtractValue.index = index; + i->StructExtractValue.result_type = result_type; + return v; +} + +ssaValue *ssa_make_instr_union_tag_ptr(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagPtr); + ssaInstr *i = &v->Instr; + i->UnionTagPtr.address = address; + i->UnionTagPtr.type = t_int_ptr; + return v; +} + +ssaValue *ssa_make_instr_union_tag_value(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagValue); + ssaInstr *i = &v->Instr; + i->UnionTagValue.address = address; + i->UnionTagValue.type = t_int_ptr; + return v; +} + + +ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp); + ssaInstr *i = &v->Instr; + i->BinaryOp.op = op; + i->BinaryOp.left = left; + i->BinaryOp.right = right; + i->BinaryOp.type = type; + return v; +} + +ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump); + ssaInstr *i = &v->Instr; + i->Jump.block = block; + return v; +} +ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_If); + ssaInstr *i = &v->Instr; + i->If.cond = cond; + i->If.true_block = true_block; + i->If.false_block = false_block; + return v; +} + + +ssaValue *ssa_make_instr_phi(ssaProcedure *p, ssaValueArray edges, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi); + ssaInstr *i = &v->Instr; + i->Phi.edges = edges; + i->Phi.type = type; + return v; +} + +ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable); + return v; +} + +ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return); + v->Instr.Return.value = value; + return v; +} + +ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select); + v->Instr.Select.cond = cond; + v->Instr.Select.true_value = t; + v->Instr.Select.false_value = f; + return v; +} + +ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call); + v->Instr.Call.value = value; + v->Instr.Call.args = args; + v->Instr.Call.arg_count = arg_count; + v->Instr.Call.type = result_type; + return v; +} + +ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv); + v->Instr.Conv.kind = kind; + v->Instr.Conv.value = value; + v->Instr.Conv.from = from; + v->Instr.Conv.to = to; + return v; +} + +ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement); + v->Instr.VectorExtractElement.vector = vector; + v->Instr.VectorExtractElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement); + v->Instr.VectorInsertElement.vector = vector; + v->Instr.VectorInsertElement.elem = elem; + v->Instr.VectorInsertElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle); + v->Instr.VectorShuffle.vector = vector; + v->Instr.VectorShuffle.indices = indices; + v->Instr.VectorShuffle.index_count = index_count; + + Type *vt = base_type(ssa_type(vector)); + v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); + + return v; +} + +ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment); + v->Instr.Comment.text = text; + return v; +} + +ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck); + v->Instr.BoundsCheck.pos = pos; + v->Instr.BoundsCheck.index = index; + v->Instr.BoundsCheck.len = len; + return v; +} +ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck); + v->Instr.SliceBoundsCheck.pos = pos; + v->Instr.SliceBoundsCheck.low = low; + v->Instr.SliceBoundsCheck.high = high; + v->Instr.SliceBoundsCheck.max = max; + v->Instr.SliceBoundsCheck.is_substring = is_substring; + return v; +} + + + +ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Constant); + v->Constant.type = type; + v->Constant.value = value; + return v; +} + + +ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) { + ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice); + v->ConstantSlice.type = type; + v->ConstantSlice.backing_array = backing_array; + v->ConstantSlice.count = count; + return v; +} + +ssaValue *ssa_make_const_int(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_int, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_bool(gbAllocator a, bool b) { + return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); +} +ssaValue *ssa_make_const_string(gbAllocator a, String s) { + return ssa_make_value_constant(a, t_string, make_exact_value_string(s)); +} + +ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Proc); + v->Proc.module = m; + v->Proc.entity = entity; + v->Proc.type = type; + v->Proc.type_expr = type_expr; + v->Proc.body = body; + v->Proc.name = name; + array_init(&v->Proc.referrers, heap_allocator()); // TODO(bill): replace heap allocator + + Type *t = base_type(type); + GB_ASSERT(is_type_proc(t)); + array_init_reserve(&v->Proc.params, heap_allocator(), t->Proc.param_count); + + return v; +} + +ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) { + Scope *scope = NULL; + if (node != NULL) { + Scope **found = map_scope_get(&proc->module->info->scopes, hash_pointer(node)); + if (found) { + scope = *found; + } else { + GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind])); + } + } + + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block); + v->Block.label = make_string_c(label); + v->Block.node = node; + v->Block.scope = scope; + v->Block.parent = proc; + + array_init(&v->Block.instrs, heap_allocator()); + array_init(&v->Block.locals, heap_allocator()); + + array_init(&v->Block.preds, heap_allocator()); + array_init(&v->Block.succs, heap_allocator()); + + ssaBlock *block = &v->Block; + + array_add(&proc->blocks, block); + proc->block_count++; + + return block; +} + + + + + +ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) { + ssaDefer d = {ssaDefer_Node}; + d.scope_index = scope_index; + d.block = proc->curr_block; + d.stmt = stmt; + array_add(&proc->defer_stmts, d); + return d; +} + + +ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) { + ssaDefer d = {ssaDefer_Instr}; + d.scope_index = proc->scope_index; + d.block = proc->curr_block; + d.instr = instr; // NOTE(bill): It will make a copy everytime it is called + array_add(&proc->defer_stmts, d); + return d; +} + + + +ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { + gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); + + if (is_type_slice(type)) { + ast_node(cl, CompoundLit, value.value_compound); + + isize count = cl->elems.count; + if (count == 0) { + return ssa_make_value_nil(a, type); + } + Type *elem = base_type(type)->Slice.elem; + Type *t = make_type_array(a, elem, count); + ssaValue *backing_array = ssa_add_module_constant(m, t, value); + + + isize max_len = 7+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index); + m->global_array_index++; + + String name = make_string(str, len-1); + + Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value); + ssaValue *g = ssa_make_value_global(a, e, backing_array); + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + + return ssa_make_value_constant_slice(a, type, g, count); + } + + return ssa_make_value_constant(a, type, value); +} + +ssaValue *ssa_add_global_string_array(ssaModule *m, String string) { + // TODO(bill): Should this use the arena allocator or the heap allocator? + // Strings could be huge! + gbAllocator a = m->allocator; + // gbAllocator a = gb_heap_allocator(); + + isize max_len = 6+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index); + m->global_string_index++; + + String name = make_string(str, len-1); + Token token = {Token_String}; + token.string = name; + Type *type = make_type_array(a, t_u8, string.len); + ExactValue ev = make_exact_value_string(string); + Entity *entity = make_entity_constant(a, NULL, token, type, ev); + ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev)); + g->Global.is_private = true; + // g->Global.is_unnamed_addr = true; + // g->Global.is_constant = true; + + ssa_module_add_value(m, entity, g); + map_ssa_value_set(&m->members, hash_string(name), g); + + return g; +} + + + + +ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e) { + ssaBlock *b = proc->decl_block; // all variables must be in the first block + ssaValue *instr = ssa_make_instr_local(proc, e, true); + instr->Instr.parent = b; + array_add(&b->instrs, instr); + array_add(&b->locals, instr); + proc->local_count++; + + // if (zero_initialized) { + ssa_emit_zero_init(proc, instr); + // } + + return instr; +} + +ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, bool zero_initialized) { + Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(name)); + if (found) { + Entity *e = *found; + ssa_emit_comment(proc, e->token.string); + return ssa_add_local(proc, e); + } + return NULL; +} + +ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { + GB_ASSERT(type != NULL); + + Scope *scope = NULL; + if (proc->curr_block) { + scope = proc->curr_block->scope; + } + Entity *e = make_entity_variable(proc->module->allocator, + scope, + empty_token, + type); + return ssa_add_local(proc, e); +} + +ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) { + ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e); +#if 1 + ssaValue *l = ssa_add_local(proc, e); + ssa_emit_store(proc, l, v); +#else + ssa_module_add_value(proc->module, e, v); +#endif + return v; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Debug +// +//////////////////////////////////////////////////////////////// + +ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(file != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File); + di->File.file = file; + + String filename = file->tokenizer.fullpath; + String directory = filename; + isize slash_index = 0; + for (isize i = filename.len-1; i >= 0; i--) { + if (filename.text[i] == '\\' || + filename.text[i] == '/') { + break; + } + slash_index = i; + } + directory.len = slash_index-1; + filename.text = filename.text + slash_index; + filename.len -= slash_index; + + + di->File.filename = filename; + di->File.directory = directory; + + map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(file), di); + return di; +} + + +ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(entity != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc); + di->Proc.entity = entity; + di->Proc.name = name; + di->Proc.file = file; + di->Proc.pos = entity->token.pos; + + map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(entity), di); + return di; +} + +//////////////////////////////////////////////////////////////// +// +// @Emit +// +//////////////////////////////////////////////////////////////// + + +ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { + GB_ASSERT(instr->kind == ssaValue_Instr); + ssaBlock *b = proc->curr_block; + instr->Instr.parent = b; + if (b != NULL) { + ssaInstr *i = ssa_get_last_instr(b); + if (!ssa_is_instr_terminating(i)) { + array_add(&b->instrs, instr); + } + } + return instr; +} +ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + return ssa_emit(p, ssa_make_instr_store(p, address, value)); +} +ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_load(p, address)); +} +ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + return ssa_emit(p, ssa_make_instr_select(p, cond, t, f)); +} + +ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_zero_init(p, address)); +} + +ssaValue *ssa_emit_comment(ssaProcedure *p, String text) { + return ssa_emit(p, ssa_make_instr_comment(p, text)); +} + + +ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) { + Type *pt = base_type(ssa_type(value)); + GB_ASSERT(pt->kind == Type_Proc); + Type *results = pt->Proc.results; + return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results)); +} + +ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) { + String name = make_string_c(name_); + ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); + ssaValue *gp = *found; + return ssa_emit_call(proc, gp, args, arg_count); +} + + + +void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + isize count = proc->defer_stmts.count; + isize i = count; + while (i --> 0) { + ssaDefer d = proc->defer_stmts.e[i]; + if (kind == ssaDeferExit_Default) { + if (proc->scope_index == d.scope_index && + d.scope_index > 1) { + ssa_build_defer_stmt(proc, d); + array_pop(&proc->defer_stmts); + continue; + } else { + break; + } + } else if (kind == ssaDeferExit_Return) { + ssa_build_defer_stmt(proc, d); + } else if (kind == ssaDeferExit_Branch) { + GB_ASSERT(block != NULL); + isize lower_limit = block->scope_index+1; + if (lower_limit < d.scope_index) { + ssa_build_defer_stmt(proc, d); + } + } + } +} + + +void ssa_open_scope(ssaProcedure *proc) { + proc->scope_index++; +} + +void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + ssa_emit_defer_stmts(proc, kind, block); + GB_ASSERT(proc->scope_index > 0); + proc->scope_index--; +} + + + +void ssa_emit_unreachable(ssaProcedure *proc) { + ssa_emit(proc, ssa_make_instr_unreachable(proc)); +} + +void ssa_emit_return(ssaProcedure *proc, ssaValue *v) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL); + ssa_emit(proc, ssa_make_instr_return(proc, v)); +} + +void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_jump(proc, target_block)); + ssa_add_edge(b, target_block); + proc->curr_block = NULL; +} + +void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block)); + ssa_add_edge(b, true_block); + ssa_add_edge(b, false_block); + proc->curr_block = NULL; +} + +void ssa_emit_startup_runtime(ssaProcedure *proc) { + GB_ASSERT(proc->parent == NULL && str_eq(proc->name, str_lit("main"))); + ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime)); +} + + + + +ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + Type *elem_type = base_type(ssa_type(v))->Vector.elem; + ssaValue *elem = ssa_emit_conv(proc, value, elem_type); + ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index)); + return ssa_emit_store(proc, addr.addr, out); + } else { + ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr)); + return ssa_emit_store(proc, addr.addr, v); + } +} +ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) { + if (addr.addr == NULL) { + GB_PANIC("Illegal addr load"); + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index)); + } + Type *t = base_type(ssa_type(addr.addr)); + if (t->kind == Type_Proc) { + // NOTE(bill): Imported procedures don't require a load as they are pointers + return addr.addr; + } + return ssa_emit_load(proc, addr.addr); +} + + + + +ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) { + offset = ssa_emit_conv(proc, offset, t_int); + return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset)); +} + +ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + Type *t_left = ssa_type(left); + Type *t_right = ssa_type(right); + + if (op == Token_Add) { + if (is_type_pointer(t_left)) { + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(ssa_type(right))) { + ssaValue *ptr = ssa_emit_conv(proc, right, type); + ssaValue *offset = left; + return ssa_emit_ptr_offset(proc, ptr, offset); + } + } else if (op == Token_Sub) { + if (is_type_pointer(t_left) && is_type_integer(t_right)) { + // ptr - int + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(t_left) && is_type_pointer(t_right)) { + GB_ASSERT(is_type_integer(type)); + Type *ptr_type = t_left; + ssaModule *m = proc->module; + ssaValue *x = ssa_emit_conv(proc, left, type); + ssaValue *y = ssa_emit_conv(proc, right, type); + ssaValue *diff = ssa_emit_arith(proc, op, x, y, type); + ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); + return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type); + } + } + + + switch (op) { + case Token_AndNot: { + // NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1) + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1)); + op = Token_Xor; + right = ssa_emit_arith(proc, op, right, neg, type); + GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp); + right->Instr.BinaryOp.type = type; + op = Token_And; + } /* fallthrough */ + 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_Shl: + case Token_Shr: + left = ssa_emit_conv(proc, left, type); + right = ssa_emit_conv(proc, right, type); + break; + } + + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type)); +} + +ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) { + Type *a = base_type(ssa_type(left)); + Type *b = base_type(ssa_type(right)); + + if (are_types_identical(a, b)) { + // NOTE(bill): No need for a conversion + } else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) { + left = ssa_emit_conv(proc, left, ssa_type(right)); + } else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) { + right = ssa_emit_conv(proc, right, ssa_type(left)); + } + + Type *result = t_bool; + if (is_type_vector(a)) { + result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); + } + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result)); +} + +ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) { + GB_ASSERT(index != NULL); + Type *st = base_type(type_deref(ssa_type(s))); + GB_ASSERT(is_type_array(st) || is_type_vector(st)); + + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + index = ssa_emit_conv(proc, index, t_i32); + return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index)); +} + +ssaValue *ssa_emit_array_epi(ssaProcedure *proc, ssaValue *s, i32 index) { + return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index)); +} + +ssaValue *ssa_emit_union_tag_ptr(ssaProcedure *proc, ssaValue *u) { + Type *t = ssa_type(u); + GB_ASSERT(is_type_pointer(t) && + is_type_union(type_deref(t))); + return ssa_emit(proc, ssa_make_instr_union_tag_ptr(proc, u)); +} + +ssaValue *ssa_emit_union_tag_value(ssaProcedure *proc, ssaValue *u) { + Type *t = ssa_type(u); + GB_ASSERT(is_type_union(t)); + return ssa_emit(proc, ssa_make_instr_union_tag_value(proc, u)); +} + + + +ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) { + gbAllocator a = proc->module->allocator; + Type *t = base_type(type_deref(ssa_type(s))); + Type *result_type = NULL; + ssaValue *gep = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = make_type_pointer(a, t->Tuple.variables[index]->type); + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; + case 1: result_type = make_type_pointer(a, t_int); break; + case 2: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_u8_ptr); break; + case 1: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_type_info_ptr); break; + case 1: result_type = make_type_pointer(a, t_rawptr); break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Maybe.elem); break; + case 1: result_type = make_type_pointer(a, t_bool); break; + } + } else { + GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type); + return ssa_emit(proc, gep); +} + + + +ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + Type *st = base_type(ssa_type(s)); + GB_ASSERT(is_type_array(st)); + return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index)); +} + +ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + + gbAllocator a = proc->module->allocator; + Type *t = base_type(ssa_type(s)); + Type *result_type = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = t->Record.fields[index]->type; + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = t->Tuple.variables[index]->type; + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Slice.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = t_u8_ptr; break; + case 1: result_type = t_int; break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = t_type_info_ptr; break; + case 1: result_type = t_rawptr; break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = t->Maybe.elem; break; + case 1: result_type = t_bool; break; + } + } else { + GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type)); +} + + +ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + } else if (type->kind == Type_Record) { + type = type->Record.fields[index]->type; + e = ssa_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_struct_ep(proc, e, index); + } break; + + case Basic_string: + e = ssa_emit_struct_ep(proc, e, index); + break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else if (type->kind == Type_Slice) { + e = ssa_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Vector) { + e = ssa_emit_array_epi(proc, e, index); + } else if (type->kind == Type_Array) { + e = ssa_emit_array_epi(proc, e, index); + } else { + GB_PANIC("un-gep-able type"); + } + } + + return e; +} + + +ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index.e[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?"); + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, type); + } else { + e = ssa_emit_struct_ev(proc, e, index); + } + } + + return e; +} + + + + +ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { + return ssa_emit_array_ep(proc, array, v_zero32); +} +ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) { + Type *t = ssa_type(array); + GB_ASSERT(t->kind == Type_Array); + return ssa_make_const_int(proc->module->allocator, t->Array.count); +} +ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) { + return ssa_array_len(proc, array); +} + +ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 0); +} +ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 1); +} +ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 2); +} + +ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 0); +} +ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 1); +} + + + +ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) { + // TODO(bill): array bounds checking for slice creation + // TODO(bill): check that low < high <= max + gbAllocator a = proc->module->allocator; + Type *bt = base_type(ssa_type(base)); + + if (low == NULL) { + low = v_zero; + } + if (high == NULL) { + switch (bt->kind) { + case Type_Array: high = ssa_array_len(proc, base); break; + case Type_Slice: high = ssa_slice_len(proc, base); break; + case Type_Pointer: high = v_one; break; + } + } + if (max == NULL) { + switch (bt->kind) { + case Type_Array: max = ssa_array_cap(proc, base); break; + case Type_Slice: max = ssa_slice_cap(proc, base); break; + case Type_Pointer: max = high; break; + } + } + GB_ASSERT(max != NULL); + + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + + ssaValue *elem = NULL; + switch (bt->kind) { + case Type_Array: elem = ssa_array_elem(proc, base); break; + case Type_Slice: elem = ssa_slice_elem(proc, base); break; + case Type_Pointer: elem = ssa_emit_load(proc, base); break; + } + + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep = NULL; + gep = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, gep, elem); + + gep = ssa_emit_struct_ep(proc, slice, 1); + ssa_emit_store(proc, gep, len); + + gep = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep, cap); + + return slice; +} + +ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0); + ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, str_elem, elem); + ssa_emit_store(proc, str_len, len); + return ssa_emit_load(proc, str); +} + + + + +String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { + Type *prev_src = src; + // Type *prev_dst = dst; + src = base_type(type_deref(src)); + // dst = base_type(type_deref(dst)); + bool src_is_ptr = src != prev_src; + // bool dst_is_ptr = dst != prev_dst; + + GB_ASSERT(is_type_struct(src)); + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) { + if (are_types_identical(dst, f->type)) { + return f->token.string; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(type_deref(dst), f->type)) { + return f->token.string; + } + } + if (is_type_struct(f->type)) { + String name = lookup_polymorphic_field(info, dst, f->type); + if (name.len > 0) { + return name; + } + } + } + } + return str_lit(""); +} + +ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type)); +} + + +ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(get_enum_base_type(src_type)); + Type *dst = base_type(get_enum_base_type(t)); + + if (value->kind == ssaValue_Constant) { + if (is_type_any(dst)) { + ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type)); + ssa_emit_store(proc, default_value, value); + return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any); + } else if (dst->kind == Type_Basic) { + ExactValue ev = value->Constant.value; + if (is_type_float(dst)) { + ev = exact_value_to_float(ev); + } else if (is_type_string(dst)) { + // Handled elsewhere + GB_ASSERT(ev.kind == ExactValue_String); + } else if (is_type_integer(dst)) { + ev = exact_value_to_integer(ev); + } else if (is_type_pointer(dst)) { + // IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null` + ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev); + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst)); + } + return ssa_add_module_constant(proc->module, t, ev); + } + } + + if (are_types_identical(src, dst)) { + return value; + } + + if (is_type_maybe(dst)) { + ssaValue *maybe = ssa_add_local_generated(proc, dst); + ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0); + ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1); + ssa_emit_store(proc, val, value); + ssa_emit_store(proc, set, v_true); + return ssa_emit_load(proc, maybe); + } + + // integer -> integer + if (is_type_integer(src) && is_type_integer(dst)) { + GB_ASSERT(src->kind == Type_Basic && + dst->kind == Type_Basic); + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + if (sz == dz) { + // NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment + return value; + } + + ssaConvKind kind = ssaConv_trunc; + if (dz >= sz) { + kind = ssaConv_zext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // boolean -> integer + if (is_type_boolean(src) && is_type_integer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst)); + } + + // integer -> boolean + if (is_type_integer(src) && is_type_boolean(dst)) { + return ssa_emit_comp(proc, Token_NotEq, value, v_zero); + } + + + // float -> float + if (is_type_float(src) && is_type_float(dst)) { + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + ssaConvKind kind = ssaConv_fptrunc; + if (dz >= sz) { + kind = ssaConv_fpext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // float <-> integer + if (is_type_float(src) && is_type_integer(dst)) { + ssaConvKind kind = ssaConv_fptosi; + if (is_type_unsigned(dst)) { + kind = ssaConv_fptoui; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + if (is_type_integer(src) && is_type_float(dst)) { + ssaConvKind kind = ssaConv_sitofp; + if (is_type_unsigned(src)) { + kind = ssaConv_uitofp; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // Pointer <-> int + if (is_type_pointer(src) && is_type_int_or_uint(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst)); + } + if (is_type_int_or_uint(src) && is_type_pointer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); + } + + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, src_type)) { + ssa_emit_comment(proc, str_lit("union - child to parent")); + gbAllocator allocator = proc->module->allocator; + ssaValue *parent = ssa_add_local_generated(proc, t); + ssaValue *tag = ssa_make_const_int(allocator, i); + ssa_emit_store(proc, ssa_emit_union_tag_ptr(proc, parent), tag); + + ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); + + Type *tag_type = src_type; + Type *tag_type_ptr = make_type_pointer(allocator, tag_type); + ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr); + ssa_emit_store(proc, underlying, value); + + return ssa_emit_load(proc, parent); + } + } + } + + // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's + // subtype polymorphism casting + { + Type *sb = base_type(type_deref(src)); + bool src_is_ptr = src != sb; + if (is_type_struct(sb)) { + String field_name = lookup_polymorphic_field(proc->module->info, t, src); + // gb_printf("field_name: %.*s\n", LIT(field_name)); + if (field_name.len > 0) { + // NOTE(bill): It can be casted + Selection sel = lookup_field(proc->module->allocator, sb, field_name, false); + if (sel.entity != NULL) { + ssa_emit_comment(proc, str_lit("cast - polymorphism")); + if (src_is_ptr) { + value = ssa_emit_load(proc, value); + } + return ssa_emit_deep_field_ev(proc, sb, value, sel); + } + } + } + } + + + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // proc <-> proc + if (is_type_proc(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + // pointer -> proc + if (is_type_pointer(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + // proc -> pointer + if (is_type_proc(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // []byte/[]u8 <-> string + if (is_type_u8_slice(src) && is_type_string(dst)) { + ssaValue *elem = ssa_slice_elem(proc, value); + ssaValue *len = ssa_slice_len(proc, value); + return ssa_emit_string(proc, elem, len); + } + if (is_type_string(src) && is_type_u8_slice(dst)) { + ssaValue *elem = ssa_string_elem(proc, value); + ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem)); + ssa_emit_store(proc, elem_ptr, elem); + + ssaValue *len = ssa_string_len(proc, value); + ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len); + return ssa_emit_load(proc, slice); + } + + if (is_type_vector(dst)) { + Type *dst_elem = dst->Vector.elem; + value = ssa_emit_conv(proc, value, dst_elem); + ssaValue *v = ssa_add_local_generated(proc, t); + v = ssa_emit_load(proc, v); + v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); + // NOTE(bill): Broadcast lowest value to all values + isize index_count = dst->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + + v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count)); + return v; + } + + if (is_type_any(dst)) { + ssaValue *result = ssa_add_local_generated(proc, t_any); + + if (is_type_untyped_nil(src)) { + return ssa_emit_load(proc, result); + } + + ssaValue *data = NULL; + if (value->kind == ssaValue_Instr && + value->Instr.kind == ssaInstr_Load) { + // NOTE(bill): Addressable value + data = value->Instr.Load.address; + } else { + // NOTE(bill): Non-addressable value + data = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, data, value); + } + GB_ASSERT(is_type_pointer(ssa_type(data))); + GB_ASSERT(is_type_typed(src_type)); + data = ssa_emit_conv(proc, data, t_rawptr); + + + ssaValue *ti = ssa_type_info(proc, src_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1); + ssa_emit_store(proc, gep0, ti); + ssa_emit_store(proc, gep1, data); + + return ssa_emit_load(proc, result); + } + + if (is_type_untyped_nil(src) && type_has_nil(dst)) { + return ssa_make_value_nil(proc->module->allocator, t); + } + + + gb_printf_err("ssa_emit_conv: src -> dst\n"); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); + + + GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + +bool ssa_is_type_aggregate(Type *t) { + t = base_type(get_enum_base_type(t)); + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_string: + case Basic_any: + return true; + } + break; + + case Type_Pointer: + case Type_Vector: + return false; + + case Type_Array: + case Type_Slice: + case Type_Maybe: + case Type_Record: + case Type_Tuple: + return true; + + case Type_Named: + return ssa_is_type_aggregate(t->Named.base); + } + + return false; +} + +ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(src_type); + Type *dst = base_type(t); + if (are_types_identical(t, src_type)) { + return value; + } + + ssaModule *m = proc->module; + + i64 sz = type_size_of(m->sizes, m->allocator, src); + i64 dz = type_size_of(m->sizes, m->allocator, dst); + + GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + if (ssa_is_type_aggregate(src) || ssa_is_type_aggregate(dst)) { + ssaValue *s = ssa_add_local_generated(proc, src); + ssa_emit_store(proc, s, value); + + ssaValue *d = ssa_emit_bitcast(proc, s, make_type_pointer(m->allocator, dst)); + return ssa_emit_load(proc, d); + } + + // TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM + + return ssa_emit_bitcast(proc, value, dst); +} + +ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) { + GB_ASSERT(is_type_pointer(ssa_type(value))); + gbAllocator allocator = proc->module->allocator; + + String field_name = check_down_cast_name(t, type_deref(ssa_type(value))); + GB_ASSERT(field_name.len > 0); + Selection sel = lookup_field(proc->module->allocator, t, field_name, false); + ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr); + + i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); + ssaValue *offset = ssa_make_const_int(allocator, -offset_); + ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset); + return ssa_emit_conv(proc, head, t); +} + +ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) { + GB_ASSERT(tuple->kind == Type_Tuple); + gbAllocator a = proc->module->allocator; + + Type *src_type = ssa_type(value); + bool is_ptr = is_type_pointer(src_type); + + ssaValue *v = ssa_add_local_generated(proc, tuple); + + if (is_ptr) { + Type *src = base_type(type_deref(src_type)); + Type *src_ptr = src_type; + GB_ASSERT(is_type_union(src)); + Type *dst_ptr = tuple->Tuple.variables[0]->type; + Type *dst = type_deref(dst_ptr); + + ssaValue *tag = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, value)); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); + ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_conv(proc, value, dst_ptr); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } else { + Type *src = base_type(src_type); + GB_ASSERT(is_type_union(src)); + Type *dst = tuple->Tuple.variables[0]->type; + Type *dst_ptr = make_type_pointer(a, dst); + + ssaValue *tag = ssa_emit_union_tag_value(proc, value); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + // HACK(bill): This is probably not very efficient + ssaValue *union_copy = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, union_copy, value); + + ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); + ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr)); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } + return ssa_emit_load(proc, v); +} + + +isize ssa_type_info_index(CheckerInfo *info, Type *type) { + type = default_type(type); + + isize entry_index = -1; + HashKey key = hash_pointer(type); + isize *found_entry_index = map_isize_get(&info->type_info_map, key); + if (found_entry_index) { + entry_index = *found_entry_index; + } + if (entry_index < 0) { + // NOTE(bill): Do manual search + // TODO(bill): This is O(n) and can be very slow + for_array(i, info->type_info_map.entries){ + MapIsizeEntry *e = &info->type_info_map.entries.e[i]; + Type *prev_type = cast(Type *)e->key.ptr; + if (are_types_identical(prev_type, type)) { + entry_index = e->value; + // NOTE(bill): Add it to the search map + map_isize_set(&info->type_info_map, key, entry_index); + break; + } + } + } + + if (entry_index < 0) { + compiler_error("Type_Info for `%s` could not be found", type_to_string(type)); + } + return entry_index; +} + +ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) { + ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + type = default_type(type); + + i32 entry_index = ssa_type_info_index(info, type); + + // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); + + return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index)); +} + + + +ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) { + ast_node(be, BinaryExpr, expr); +#if 0 + ssaBlock *true_ = ssa_add_block(proc, NULL, "logical.cmp.true"); + ssaBlock *false_ = ssa_add_block(proc, NULL, "logical.cmp.false"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + ssaValue *result = ssa_add_local_generated(proc, t_bool); + ssa_build_cond(proc, expr, true_, false_); + + proc->curr_block = true_; + ssa_emit_store(proc, result, v_true); + ssa_emit_jump(proc, done); + + proc->curr_block = false_; + ssa_emit_store(proc, result, v_false); + ssa_emit_jump(proc, done); + + proc->curr_block = done; + + return ssa_emit_load(proc, result); +#else + ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + Type *type = type_of_expr(proc->module->info, expr); + type = default_type(type); + + ssaValue *short_circuit = NULL; + if (be->op.kind == Token_CmpAnd) { + ssa_build_cond(proc, be->left, rhs, done); + short_circuit = v_false; + } else if (be->op.kind == Token_CmpOr) { + ssa_build_cond(proc, be->left, done, rhs); + short_circuit = v_true; + } + + if (rhs->preds.count == 0) { + proc->curr_block = done; + return short_circuit; + } + + if (done->preds.count == 0) { + proc->curr_block = rhs; + return ssa_build_expr(proc, be->right); + } + + ssaValueArray edges = {0}; + array_init_reserve(&edges, proc->module->allocator, done->preds.count+1); + for_array(i, done->preds) { + array_add(&edges, short_circuit); + } + + proc->curr_block = rhs; + array_add(&edges, ssa_build_expr(proc, be->right)); + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type)); +#endif +} + + +void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + index = ssa_emit_conv(proc, index, t_int); + len = ssa_emit_conv(proc, len, t_int); + + ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 5); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, index, t_int); + // args[4] = ssa_emit_conv(proc, len, t_int); + + // ssa_emit_global_call(proc, "__bounds_check_error", args, 5); +} + +void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + + low = ssa_emit_conv(proc, low, t_int); + high = ssa_emit_conv(proc, high, t_int); + max = ssa_emit_conv(proc, max, t_int); + + ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 6); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, low, t_int); + // args[4] = ssa_emit_conv(proc, high, t_int); + // args[5] = ssa_emit_conv(proc, max, t_int); + + // if (!is_substring) { + // ssa_emit_global_call(proc, "__slice_expr_error", args, 6); + // } else { + // ssa_emit_global_call(proc, "__substring_expr_error", args, 5); + // } +} + + +//////////////////////////////////////////////////////////////// +// +// @Build +// +//////////////////////////////////////////////////////////////// + + +void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { + ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList); + tl->prev = proc->target_list; + tl->break_ = break_; + tl->continue_ = continue_; + tl->fallthrough_ = fallthrough_; + proc->target_list = tl; +} + +void ssa_pop_target_list(ssaProcedure *proc) { + proc->target_list = proc->target_list->prev; +} + + +void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) { + if (field->kind != Entity_TypeName) { + return; + } + String cn = field->token.string; + + isize len = parent.len + 1 + cn.len; + String child = {NULL, len}; + child.text = gb_alloc_array(m->allocator, u8, len); + + isize i = 0; + gb_memmove(child.text+i, parent.text, parent.len); + i += parent.len; + child.text[i++] = '.'; + gb_memmove(child.text+i, cn.text, cn.len); + + map_string_set(&m->type_names, hash_pointer(field->type), child); + ssa_gen_global_type_name(m, field, child); +} + +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { + ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type); + ssa_module_add_value(m, e, t); + map_ssa_value_set(&m->members, hash_string(name), t); + + Type *bt = base_type(e->type); + if (bt->kind == Type_Record) { + TypeRecord *s = &bt->Record; + for (isize j = 0; j < s->other_field_count; j++) { + ssa_mangle_sub_type_name(m, s->other_fields[j], name); + } + } + + if (is_type_union(bt)) { + TypeRecord *s = &bt->Record; + // NOTE(bill): Zeroth entry is null (for `match type` stmts) + for (isize j = 1; j < s->field_count; j++) { + ssa_mangle_sub_type_name(m, s->fields[j], name); + } + } +} + + + + +void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) { + ssaBlock *b = ssa_add_block(proc, NULL, "defer"); + // NOTE(bill): The prev block may defer injection before it's terminator + ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block); + if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) { + ssa_emit_jump(proc, b); + } + proc->curr_block = b; + ssa_emit_comment(proc, str_lit("defer")); + if (d.kind == ssaDefer_Node) { + ssa_build_stmt(proc, d.stmt); + } else if (d.kind == ssaDefer_Instr) { + // NOTE(bill): Need to make a new copy + ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue)); + ssa_emit(proc, instr); + } +} + + + +ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) { + ssaValue **value = map_ssa_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); + return *value; +} + +ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) { + Entity *e = proc->module->info->implicit_values[id]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + Entity *backing = e->ImplicitValue.backing; + ssaValue **value = map_ssa_value_get(&proc->module->values, hash_pointer(backing)); + GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string)); + return *value; +} + + + +ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { + expr = unparen_expr(expr); + switch (expr->kind) { + case_ast_node(bl, BasicLit, expr); + GB_PANIC("Non-constant basic literal"); + case_end; + + case_ast_node(i, Ident, expr); + Entity *e = *map_entity_get(&proc->module->info->uses, hash_pointer(expr)); + if (e->kind == Entity_Builtin) { + Token token = ast_node_token(expr); + GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n" + "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name), + LIT(token.pos.file), token.pos.line, token.pos.column); + return NULL; + } else if (e->kind == Entity_Nil) { + return ssa_make_value_nil(proc->module->allocator, tv->type); + } else if (e->kind == Entity_ImplicitValue) { + return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id)); + } + + ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + ssaValue *v = *found; + if (v->kind == ssaValue_Proc) { + return v; + } + // if (e->kind == Entity_Variable && e->Variable.param) { + // return v; + // } + return ssa_emit_load(proc, v); + } + return NULL; + case_end; + + case_ast_node(re, RunExpr, expr); + // TODO(bill): Run Expression + return ssa_build_single_expr(proc, re->expr, tv); + case_end; + + case_ast_node(de, DerefExpr, expr); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + TypeAndValue *tav = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT(tav != NULL); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: + return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer + + case Token_Maybe: + return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); + + case Token_Add: + return ssa_build_expr(proc, ue->expr); + + case Token_Sub: // NOTE(bill): -`x` == 0 - `x` + return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type); + + case Token_Not: // Boolean not + case Token_Xor: { // Bitwise not + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *left = ssa_build_expr(proc, ue->expr); + ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1)); + return ssa_emit_arith(proc, ue->op.kind, + left, right, + tv->type); + } break; + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + ssaValue *left = ssa_build_expr(proc, be->left); + 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: + case Token_Shl: + case Token_Shr: { + ssaValue *right = ssa_build_expr(proc, be->right); + return ssa_emit_arith(proc, be->op.kind, left, right, type); + } + + + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_LtEq: + case Token_Gt: + case Token_GtEq: { + ssaValue *right = ssa_build_expr(proc, be->right); + ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right); + return ssa_emit_conv(proc, cmp, type); + } break; + + case Token_CmpAnd: + case Token_CmpOr: + return ssa_emit_logical_binary_expr(proc, expr); + + case Token_as: + ssa_emit_comment(proc, str_lit("cast - as")); + return ssa_emit_conv(proc, left, type); + + case Token_transmute: + ssa_emit_comment(proc, str_lit("cast - transmute")); + return ssa_emit_transmute(proc, left, type); + + case Token_down_cast: + ssa_emit_comment(proc, str_lit("cast - down_cast")); + return ssa_emit_down_cast(proc, left, type); + + case Token_union_cast: + ssa_emit_comment(proc, str_lit("cast - union_cast")); + return ssa_emit_union_cast(proc, left, type); + + default: + GB_PANIC("Invalid binary expression"); + break; + } + case_end; + + case_ast_node(pl, ProcLit, expr); + // NOTE(bill): Generate a new name + // parent$count + isize name_len = proc->name.len + 1 + 8 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count); + String name = make_string(name_text, name_len-1); + + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, NULL, type, pl->type, pl->body, name); + + value->Proc.tags = pl->tags; + + array_add(&proc->children, &value->Proc); + ssa_build_proc(value, proc); + + return value; + case_end; + + + case_ast_node(cl, CompoundLit, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + + case_ast_node(ce, CallExpr, expr); + AstNode *p = unparen_expr(ce->proc); + if (p->kind == AstNode_Ident) { + Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(p)); + if (found && (*found)->kind == Entity_Builtin) { + Entity *e = *found; + switch (e->Builtin.id) { + case BuiltinProc_type_info: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0])); + return ssa_type_info(proc, t); + } break; + case BuiltinProc_type_info_of_val: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0])); + return ssa_type_info(proc, t); + } break; + + case BuiltinProc_new: { + ssa_emit_comment(proc, str_lit("new")); + // new :: proc(Type) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args.e[0]); + Type *ptr_type = make_type_pointer(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = ssa_make_const_int(allocator, s); + args[1] = ssa_make_const_int(allocator, a); + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + ssaValue *v = ssa_emit_conv(proc, call, ptr_type); + return v; + } break; + + case BuiltinProc_new_slice: { + ssa_emit_comment(proc, str_lit("new_slice")); + // new_slice :: proc(Type, len: int[, cap: int]) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args.e[0]); + Type *ptr_type = make_type_pointer(allocator, type); + Type *slice_type = make_type_slice(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue *elem_size = ssa_make_const_int(allocator, s); + ssaValue *elem_align = ssa_make_const_int(allocator, a); + + ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[1]), t_int); + ssaValue *cap = len; + if (ce->args.count == 3) { + cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[2]), t_int); + } + + ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, len, cap, false); + + ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = slice_size; + args[1] = elem_align; + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + + ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, ptr); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_assert: { + ssa_emit_comment(proc, str_lit("assert")); + ssaValue *cond = ssa_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_boolean(ssa_type(cond))); + + cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false); + ssaBlock *err = ssa_add_block(proc, NULL, "builtin.assert.err"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done"); + + ssa_emit_if(proc, cond, err, done); + proc->curr_block = err; + + // TODO(bill): Cleanup allocations here + Token token = ast_node_token(ce->args.e[0]); + TokenPos pos = token.pos; + gbString expr = expr_to_string(ce->args.e[0]); + isize expr_len = gb_string_length(expr); + String expr_str = {0}; + expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1); + expr_str.len = expr_len; + gb_string_free(expr); + + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = ssa_make_const_string(proc->module->allocator, expr_str); + ssa_emit_global_call(proc, "__assert", args, 4); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return NULL; + } break; + + case BuiltinProc_panic: { + ssa_emit_comment(proc, str_lit("panic")); + ssaValue *msg = ssa_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_string(ssa_type(msg))); + + Token token = ast_node_token(ce->args.e[0]); + TokenPos pos = token.pos; + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = msg; + ssa_emit_global_call(proc, "__assert", args, 4); + + return NULL; + } break; + + + case BuiltinProc_copy: { + ssa_emit_comment(proc, str_lit("copy")); + // copy :: proc(dst, src: []Type) -> int + AstNode *dst_node = ce->args.e[0]; + AstNode *src_node = ce->args.e[1]; + ssaValue *dst_slice = ssa_build_expr(proc, dst_node); + ssaValue *src_slice = ssa_build_expr(proc, src_node); + Type *slice_type = base_type(ssa_type(dst_slice)); + GB_ASSERT(slice_type->kind == Type_Slice); + Type *elem_type = slice_type->Slice.elem; + i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + + + ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr); + ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr); + + ssaValue *len_dst = ssa_slice_len(proc, dst_slice); + ssaValue *len_src = ssa_slice_len(proc, src_slice); + + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src); + ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src); + + ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem); + ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = dst; + args[1] = src; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + return len; + } break; + case BuiltinProc_append: { + ssa_emit_comment(proc, str_lit("append")); + // append :: proc(s: ^[]Type, item: Type) -> bool + AstNode *sptr_node = ce->args.e[0]; + AstNode *item_node = ce->args.e[1]; + ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node); + ssaValue *slice = ssa_emit_load(proc, slice_ptr); + + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *cap = ssa_slice_cap(proc, slice); + + Type *elem_type = type_deref(ssa_type(elem)); + + ssaValue *item_value = ssa_build_expr(proc, item_node); + item_value = ssa_emit_conv(proc, item_value, elem_type); + + ssaValue *item = ssa_add_local_generated(proc, elem_type); + ssa_emit_store(proc, item, item_value); + + + // NOTE(bill): Check if can append is possible + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap); + ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done"); + + ssa_emit_if(proc, cond, able, done); + proc->curr_block = able; + + // Add new slice item + i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size); + + ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len); + offset = ssa_emit_conv(proc, offset, t_rawptr); + + item = ssa_emit_ptr_offset(proc, item, v_zero); + item = ssa_emit_conv(proc, item, t_rawptr); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = offset; + args[1] = item; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + // Increment slice length + ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int); + ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1); + ssa_emit_store(proc, gep, new_len); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit_conv(proc, cond, t_bool); + } break; + + case BuiltinProc_swizzle: { + ssa_emit_comment(proc, str_lit("swizzle")); + ssaValue *vector = ssa_build_expr(proc, ce->args.e[0]); + isize index_count = ce->args.count-1; + if (index_count == 0) { + return vector; + } + + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + isize index = 0; + for_array(i, ce->args) { + if (i == 0) continue; + TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args.e[i]); + GB_ASSERT(is_type_integer(tv->type)); + GB_ASSERT(tv->value.kind == ExactValue_Integer); + indices[index++] = cast(i32)tv->value.value_integer; + } + + return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count)); + + } break; + +#if 0 + case BuiltinProc_ptr_offset: { + ssa_emit_comment(proc, str_lit("ptr_offset")); + ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *offset = ssa_build_expr(proc, ce->args.e[1]); + return ssa_emit_ptr_offset(proc, ptr, offset); + } break; + + case BuiltinProc_ptr_sub: { + ssa_emit_comment(proc, str_lit("ptr_sub")); + ssaValue *ptr_a = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *ptr_b = ssa_build_expr(proc, ce->args.e[1]); + Type *ptr_type = base_type(ssa_type(ptr_a)); + GB_ASSERT(ptr_type->kind == Type_Pointer); + isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem); + + ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int); + if (elem_size > 1) { + ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size); + v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int); + } + + return v; + } break; +#endif + + case BuiltinProc_slice_ptr: { + ssa_emit_comment(proc, str_lit("slice_ptr")); + ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *len = ssa_build_expr(proc, ce->args.e[1]); + ssaValue *cap = len; + + len = ssa_emit_conv(proc, len, t_int); + + if (ce->args.count == 3) { + cap = ssa_build_expr(proc, ce->args.e[2]); + cap = ssa_emit_conv(proc, cap, t_int); + } + + + Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr))); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_min: { + ssa_emit_comment(proc, str_lit("min")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *y = ssa_build_expr(proc, ce->args.e[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_max: { + ssa_emit_comment(proc, str_lit("max")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + ssaValue *y = ssa_build_expr(proc, ce->args.e[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_abs: { + ssa_emit_comment(proc, str_lit("abs")); + gbAllocator a = proc->module->allocator; + + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + Type *original_type = ssa_type(x); + Type *t = original_type; + i64 sz = type_size_of(proc->module->sizes, a, t); + GB_ASSERT(is_type_integer(t) || is_type_float(t)); + if (is_type_float(t)) { + if (sz == 4) { + t = t_i32; + } else if (sz == 8) { + t = t_i64; + } else { + GB_PANIC("unknown float type for `abs`"); + } + + x = ssa_emit_bitcast(proc, x, t); + } + + /* + NOTE(bill): See Hacker's Delight, section 2-4. + m := x >> (int_size-1) + b := x ^ m + return b - m + */ + + ssaValue *m = ssa_emit_arith(proc, Token_Shr, + x, + ssa_make_value_constant(a, t, make_exact_value_integer(sz-1)), + t); + ssaValue *b = ssa_emit_arith(proc, Token_Xor, x, m, t); + ssaValue *v = ssa_emit_arith(proc, Token_Sub, b, m, t); + + if (is_type_float(t)) { + v = ssa_emit_bitcast(proc, v, original_type); + } + return v; + } break; + + case BuiltinProc_enum_to_string: { + ssa_emit_comment(proc, str_lit("enum_to_string")); + ssaValue *x = ssa_build_expr(proc, ce->args.e[0]); + Type *t = ssa_type(x); + ssaValue *ti = ssa_type_info(proc, t); + + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2); + args[0] = ti; + args[1] = ssa_emit_conv(proc, x, t_i64); + return ssa_emit_global_call(proc, "__enum_to_string", args, 2); + } break; + } + } + } + + + // NOTE(bill): Regular call + ssaValue *value = ssa_build_expr(proc, ce->proc); + Type *proc_type_ = base_type(ssa_type(value)); + GB_ASSERT(proc_type_->kind == Type_Proc); + TypeProc *type = &proc_type_->Proc; + + isize arg_index = 0; + + isize arg_count = 0; + for_array(i, ce->args) { + AstNode *a = ce->args.e[i]; + Type *at = base_type(type_of_expr(proc->module->info, a)); + if (at->kind == Type_Tuple) { + arg_count += at->Tuple.variable_count; + } else { + arg_count++; + } + } + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count); + bool variadic = proc_type_->Proc.variadic; + bool vari_expand = ce->ellipsis.pos.line != 0; + + for_array(i, ce->args) { + ssaValue *a = ssa_build_expr(proc, ce->args.e[i]); + Type *at = ssa_type(a); + if (at->kind == Type_Tuple) { + for (isize i = 0; i < at->Tuple.variable_count; i++) { + Entity *e = at->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, a, i); + args[arg_index++] = v; + } + } else { + args[arg_index++] = a; + } + } + + TypeTuple *pt = &type->params->Tuple; + + if (variadic) { + isize i = 0; + for (; i < type->param_count-1; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + if (!vari_expand) { + Type *variadic_type = pt->variables[i]->type; + GB_ASSERT(is_type_slice(variadic_type)); + variadic_type = base_type(variadic_type)->Slice.elem; + for (; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], variadic_type); + } + } + } else { + for (isize i = 0; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + } + + if (variadic && !vari_expand) { + ssa_emit_comment(proc, str_lit("variadic call argument generation")); + gbAllocator allocator = proc->module->allocator; + Type *slice_type = pt->variables[type->param_count-1]->type; + Type *elem_type = base_type(slice_type)->Slice.elem; + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + isize slice_len = arg_count+1 - type->param_count; + + if (slice_len > 0) { + ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len)); + + for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) { + ssaValue *addr = ssa_emit_array_epi(proc, base_array, j); + ssa_emit_store(proc, addr, args[i]); + } + + ssaValue *base_elem = ssa_emit_array_epi(proc, base_array, 0); + ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, slice_elem, base_elem); + ssaValue *len = ssa_make_const_int(allocator, slice_len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len); + } + + if (args[0]->kind == ssaValue_Constant) { + ssaValueConstant *c = &args[0]->Constant; + gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind); + } + + arg_count = type->param_count; + args[arg_count-1] = ssa_emit_load(proc, slice); + } + + return ssa_emit_call(proc, value, args, arg_count); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(se, SliceExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(ie, IndexExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + } + + GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); + return NULL; +} + + +ssaValue *ssa_build_expr(ssaProcedure *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 ssa_add_module_constant(proc->module, tv->type, tv->value); + } + + ssaValue *value = NULL; + if (tv->mode == Addressing_Variable) { + value = ssa_addr_load(proc, ssa_build_addr(proc, expr)); + } else { + value = ssa_build_single_expr(proc, expr, tv); + } + + return value; +} + +ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) { + 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); + ssaValue **pv = map_ssa_value_get(&proc->module->values, hash_pointer(parent)); + ssaValue *v = NULL; + if (pv != NULL) { + v = *pv; + } else { + v = ssa_build_addr(proc, e->using_expr).addr; + } + GB_ASSERT(v != NULL); + ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel); + map_ssa_value_set(&proc->module->values, hash_pointer(e), var); + return var; +} + +bool ssa_is_elem_const(ssaModule *m, AstNode *elem, Type *elem_type) { + if (base_type(elem_type) == t_any) { + return false; + } + if (elem->kind == AstNode_FieldValue) { + elem = elem->FieldValue.value; + } + TypeAndValue *tav = type_and_value_of_expression(m->info, elem); + GB_ASSERT(tav != NULL); + return tav->value.kind != ExactValue_Invalid; +} + +ssaAddr ssa_build_addr(ssaProcedure *proc, 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(proc->module->info, expr); + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + + GB_ASSERT(e->kind != Entity_Constant); + + ssaValue *v = NULL; + ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + v = *found; + } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + v = ssa_add_using_variable(proc, e); + } else if (e->kind == Entity_ImplicitValue) { + // TODO(bill): Should a copy be made? + v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id); + } + + if (v == NULL) { + GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind])); + } + + return ssa_make_addr(v, expr); + case_end; + + case_ast_node(pe, ParenExpr, expr); + return ssa_build_addr(proc, unparen_expr(expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + ssa_emit_comment(proc, str_lit("SelectorExpr")); + String selector = unparen_expr(se->selector)->Ident.string; + Type *type = base_type(type_of_expr(proc->module->info, se->expr)); + + if (type == t_invalid) { + // NOTE(bill): Imports + Entity *imp = entity_of_ident(proc->module->info, se->expr); + if (imp != NULL) { + GB_ASSERT(imp->kind == Entity_ImportName); + } + return ssa_build_addr(proc, unparen_expr(se->selector)); + } else { + Selection sel = lookup_field(proc->module->allocator, type, selector, false); + GB_ASSERT(sel.entity != NULL); + + ssaValue *a = ssa_build_addr(proc, se->expr).addr; + a = ssa_emit_deep_field_gep(proc, type, a, sel); + return ssa_make_addr(a, expr); + } + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: { + return ssa_build_addr(proc, ue->expr); + } + default: + GB_PANIC("Invalid unary expression for ssa_build_addr"); + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + switch (be->op.kind) { + case Token_as: { + ssa_emit_comment(proc, str_lit("Cast - as")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + case Token_transmute: { + ssa_emit_comment(proc, str_lit("Cast - transmute")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + default: + GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string)); + break; + } + case_end; + + case_ast_node(ie, IndexExpr, expr); + ssa_emit_comment(proc, str_lit("IndexExpr")); + Type *t = base_type(type_of_expr(proc->module->info, ie->expr)); + gbAllocator a = proc->module->allocator; + + + bool deref = is_type_pointer(t); + t = type_deref(t); + + ssaValue *using_addr = NULL; + if (!is_type_indexable(t)) { + // Using index expression + Entity *using_field = find_using_index_expr(t); + if (using_field != NULL) { + Selection sel = lookup_field(a, t, using_field->token.string, false); + ssaValue *e = ssa_build_addr(proc, ie->expr).addr; + using_addr = ssa_emit_deep_field_gep(proc, t, e, sel); + + t = using_field->type; + } + } + + + switch (t->kind) { + case Type_Vector: { + ssaValue *vector = NULL; + if (using_addr != NULL) { + vector = using_addr; + } else { + vector = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + vector = ssa_emit_load(proc, vector); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr_vector(vector, index, expr); + } break; + + case Type_Array: { + ssaValue *array = NULL; + if (using_addr != NULL) { + array = using_addr; + } else { + array = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + array = ssa_emit_load(proc, array); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *elem = ssa_emit_array_ep(proc, array, index); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr(elem, expr); + } break; + + case Type_Slice: { + ssaValue *slice = NULL; + if (using_addr != NULL) { + slice = ssa_emit_load(proc, using_addr); + } else { + slice = ssa_build_expr(proc, ie->expr); + if (deref) { + slice = ssa_emit_load(proc, slice); + } + } + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + ssaValue *v = ssa_emit_ptr_offset(proc, elem, index); + return ssa_make_addr(v, expr); + + } break; + + case Type_Basic: { // Basic_string + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr)); + ssaValue *str; + ssaValue *elem; + ssaValue *len; + ssaValue *index; + + if (using_addr != NULL) { + str = ssa_emit_load(proc, using_addr); + } else { + str = ssa_build_expr(proc, ie->expr); + if (deref) { + str = ssa_emit_load(proc, str); + } + } + elem = ssa_string_elem(proc, str); + len = ssa_string_len(proc, str); + + index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + + return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr); + } break; + } + case_end; + + case_ast_node(se, SliceExpr, expr); + ssa_emit_comment(proc, str_lit("SliceExpr")); + gbAllocator a = proc->module->allocator; + ssaValue *low = v_zero; + ssaValue *high = NULL; + ssaValue *max = NULL; + + if (se->low != NULL) low = ssa_build_expr(proc, se->low); + if (se->high != NULL) high = ssa_build_expr(proc, se->high); + if (se->triple_indexed) max = ssa_build_expr(proc, se->max); + ssaValue *addr = ssa_build_addr(proc, se->expr).addr; + ssaValue *base = ssa_emit_load(proc, addr); + Type *type = base_type(ssa_type(base)); + + if (is_type_pointer(type)) { + type = type_deref(type); + addr = base; + base = ssa_emit_load(proc, base); + } + + // TODO(bill): Cleanup like mad! + + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + + if (high == NULL) high = ssa_slice_len(proc, base); + if (max == NULL) max = ssa_slice_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_slice_elem(proc, base); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Array: { + Type *slice_type = make_type_slice(a, type->Array.elem); + + if (high == NULL) high = ssa_array_len(proc, base); + if (max == NULL) max = ssa_array_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_array_elem(proc, addr); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Basic: { + GB_ASSERT(type == t_string); + if (high == NULL) { + high = ssa_string_len(proc, base); + } + + ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true); + + ssaValue *elem, *len; + len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + + elem = ssa_string_elem(proc, base); + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + + return ssa_make_addr(str, expr); + } break; + } + + GB_PANIC("Unknown slicable type"); + case_end; + + case_ast_node(de, DerefExpr, expr); + // TODO(bill): Is a ptr copy needed? + ssaValue *addr = ssa_build_expr(proc, de->expr); + addr = ssa_emit_ptr_offset(proc, addr, v_zero); + return ssa_make_addr(addr, expr); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + ssa_emit_comment(proc, str_lit("DemaybeExpr")); + ssaValue *maybe = ssa_build_expr(proc, de->expr); + Type *t = default_type(type_of_expr(proc->module->info, expr)); + GB_ASSERT(is_type_tuple(t)); + + ssaValue *result = ssa_add_local_generated(proc, t); + ssa_emit_store(proc, result, maybe); + + return ssa_make_addr(result, expr); + case_end; + + case_ast_node(ce, CallExpr, expr); + ssaValue *e = ssa_build_expr(proc, expr); + ssaValue *v = ssa_add_local_generated(proc, ssa_type(e)); + ssa_emit_store(proc, v, e); + return ssa_make_addr(v, expr); + case_end; + + + case_ast_node(cl, CompoundLit, expr); + ssa_emit_comment(proc, str_lit("CompoundLit")); + Type *type = type_of_expr(proc->module->info, expr); + Type *bt = base_type(type); + ssaValue *v = ssa_add_local_generated(proc, type); + + Type *et = NULL; + switch (bt->kind) { + case Type_Vector: et = bt->Vector.elem; break; + case Type_Array: et = bt->Array.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + } + + switch (bt->kind) { + default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; + + case Type_Vector: { + ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + for_array(index, cl->elems) { + AstNode *elem = cl->elems.e[index]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_elem = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_elem); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_elem, et); + ssaValue *i = ssa_make_const_int(proc->module->allocator, index); + result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i)); + } + + if (cl->elems.count == 1 && bt->Vector.count > 1) { + isize index_count = bt->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count)); + ssa_emit_store(proc, v, sv); + return ssa_make_addr(v, expr); + } + ssa_emit_store(proc, v, result); + } break; + + case Type_Record: { + GB_ASSERT(is_type_struct(bt)); + TypeRecord *st = &bt->Record; + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(field_index, cl->elems) { + AstNode *elem = cl->elems.e[field_index]; + + ssaValue *field_expr = NULL; + Entity *field = NULL; + isize index = field_index; + + if (elem->kind == AstNode_FieldValue) { + ast_node(fv, FieldValue, elem); + Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false); + index = sel.index.e[0]; + elem = fv->value; + } else { + TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem); + Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false); + index = sel.index.e[0]; + } + + field = st->fields[index]; + if (ssa_is_elem_const(proc->module, elem, field->type)) { + continue; + } + + field_expr = ssa_build_expr(proc, elem); + + GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple); + + Type *ft = field->type; + ssaValue *fv = ssa_emit_conv(proc, field_expr, ft); + ssaValue *gep = ssa_emit_struct_ep(proc, v, index); + ssa_emit_store(proc, gep, fv); + } + } + } break; + case Type_Array: { + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, et); + ssaValue *gep = ssa_emit_array_epi(proc, v, i); + ssa_emit_store(proc, gep, ev); + } + } + } break; + case Type_Slice: { + if (cl->elems.count > 0) { + Type *elem_type = bt->Slice.elem; + Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type); + Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type); + ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + GB_ASSERT(slice->kind == ssaValue_ConstantSlice); + + ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ssa_is_elem_const(proc->module, elem, et)) { + continue; + } + + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type); + ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i)); + ssa_emit_store(proc, offset, ev); + } + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1); + + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + } + } break; + } + + return ssa_make_addr(v, expr); + case_end; + + + } + + TokenPos token_pos = ast_node_token(expr).pos; + GB_PANIC("Unexpected address expression\n" + "\tAstNode: %.*s @ " + "%.*s(%td:%td)\n", + LIT(ast_node_strings[expr->kind]), + LIT(token_pos.file), token_pos.line, token_pos.column); + + + return ssa_make_addr(NULL, NULL); +} + +void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) { + ssaValue *old_value = ssa_addr_load(proc, lhs); + Type *type = ssa_type(old_value); + + ssaValue *change = value; + if (is_type_pointer(type) && is_type_integer(ssa_type(value))) { + change = ssa_emit_conv(proc, value, default_type(ssa_type(value))); + } else { + change = ssa_emit_conv(proc, value, type); + } + ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type); + ssa_addr_store(proc, lhs, new_value); +} + +void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + ssa_build_cond(proc, pe->expr, true_block, false_block); + return; + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + ssa_build_cond(proc, ue->expr, false_block, true_block); + return; + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and"); + ssa_build_cond(proc, be->left, block, false_block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } else if (be->op.kind == Token_CmpOr) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or"); + ssa_build_cond(proc, be->left, true_block, block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } + case_end; + } + + ssaValue *expr = ssa_build_expr(proc, cond); + expr = ssa_emit_conv(proc, expr, t_bool); + ssa_emit_if(proc, expr, true_block, false_block); +} + + + + +void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) { + for_array(i, stmts) { + ssa_build_stmt(proc, stmts.e[i]); + } +} + +void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node); +void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + + if (node->stmt_state_flags != 0) { + u32 in = node->stmt_state_flags; + u32 out = proc->module->stmt_state_flags; + + if (in & StmtStateFlag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & StmtStateFlag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + + proc->module->stmt_state_flags = out; + } + + ssa_build_stmt_internal(proc, node); + + proc->module->stmt_state_flags = prev_stmt_state_flags; +} + +void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) { + switch (node->kind) { + case_ast_node(bs, EmptyStmt, node); + case_end; + + case_ast_node(us, UsingStmt, node); + AstNode *decl = unparen_expr(us->node); + if (decl->kind == AstNode_VarDecl) { + ssa_build_stmt(proc, decl); + } + case_end; + + case_ast_node(vd, VarDecl, node); + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + if (vd->values.count == 0) { // declared and zero-initialized + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, true); + } + } + } else { // Tuple(s) + Array(ssaAddr) lvals; + ssaValueArray inits; + array_init_reserve(&lvals, m->tmp_allocator, vd->names.count); + array_init_reserve(&inits, m->tmp_allocator, vd->names.count); + + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + ssaAddr lval = ssa_make_addr(NULL, NULL); + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, false); + lval = ssa_build_addr(proc, name); + } + + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + ssaValue *init = ssa_build_expr(proc, vd->values.e[i]); + Type *t = ssa_type(init); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + + for_array(i, inits) { + if (lvals.e[i].addr == NULL) { + continue; + } + ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i])); + ssa_addr_store(proc, lvals.e[i], v); + } + } + + gb_temp_arena_memory_end(tmp); + case_end; + + case_ast_node(pd, ProcDecl, node); + if (pd->body != NULL) { + CheckerInfo *info = proc->module->info; + + Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + + if (map_entity_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + break; + } + + // NOTE(bill): Generate a new name + // parent.name-guid + String original_name = pd->name->Ident.string; + String pd_name = original_name; + if (pd->link_name.len > 0) { + pd_name = pd->link_name; + } + + isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->children.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid); + String name = make_string(name_text, name_len-1); + + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + value->Proc.parent = proc; + + ssa_module_add_value(proc->module, e, value); + array_add(&proc->children, &value->Proc); + array_add(&proc->module->procs_to_generate, value); + } else { + CheckerInfo *info = proc->module->info; + + Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + // FFI - Foreign function interace + String original_name = pd->name->Ident.string; + String name = original_name; + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + + ssa_module_add_value(proc->module, e, value); + ssa_build_proc(value, proc); + + if (value->Proc.tags & ProcTag_foreign) { + HashKey key = hash_string(name); + ssaValue **prev_value = map_ssa_value_get(&proc->module->members, key); + if (prev_value == NULL) { + // NOTE(bill): Don't do mutliple declarations in the IR + map_ssa_value_set(&proc->module->members, key, value); + } + } else { + array_add(&proc->children, &value->Proc); + } + } + case_end; + + case_ast_node(td, TypeDecl, node); + + // NOTE(bill): Generate a new name + // parent_proc.name-guid + String td_name = td->name->Ident.string; + isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->module->members.entries.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid); + String name = make_string(name_text, name_len-1); + + Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(td->name)); + GB_ASSERT(found != NULL); + Entity *e = *found; + ssaValue *value = ssa_make_value_type_name(proc->module->allocator, + name, e->type); + map_string_set(&proc->module->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(proc->module, e, name); + case_end; + + case_ast_node(ids, IncDecStmt, node); + ssa_emit_comment(proc, str_lit("IncDecStmt")); + TokenKind op = ids->op.kind; + if (op == Token_Increment) { + op = Token_Add; + } else if (op == Token_Decrement) { + op = Token_Sub; + } + ssaAddr lval = ssa_build_addr(proc, ids->expr); + ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval)); + ssa_build_assign_op(proc, lval, one, op); + + case_end; + + case_ast_node(as, AssignStmt, node); + ssa_emit_comment(proc, str_lit("AssignStmt")); + + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + switch (as->op.kind) { + case Token_Eq: { + Array(ssaAddr) lvals; + array_init(&lvals, m->tmp_allocator); + + for_array(i, as->lhs) { + AstNode *lhs = as->lhs.e[i]; + ssaAddr lval = {0}; + if (!ssa_is_blank_ident(lhs)) { + lval = ssa_build_addr(proc, lhs); + } + array_add(&lvals, lval); + } + + if (as->lhs.count == as->rhs.count) { + if (as->lhs.count == 1) { + AstNode *rhs = as->rhs.e[0]; + ssaValue *init = ssa_build_expr(proc, rhs); + ssa_addr_store(proc, lvals.e[0], init); + } else { + ssaValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]); + array_add(&inits, init); + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals.e[i], inits.e[i]); + } + } + } else { + ssaValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]); + Type *t = ssa_type(init); + // TODO(bill): refactor for code reuse as this is repeated a bit + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals.e[i], inits.e[i]); + } + } + + } break; + + default: { + // NOTE(bill): Only 1 += 1 is allowed, no tuples + // +=, -=, etc + i32 op = cast(i32)as->op.kind; + op += Token_Add - Token_AddEq; // Convert += to + + ssaAddr lhs = ssa_build_addr(proc, as->lhs.e[0]); + ssaValue *value = ssa_build_expr(proc, as->rhs.e[0]); + ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op); + } break; + } + + gb_temp_arena_memory_end(tmp); + case_end; + + case_ast_node(es, ExprStmt, node); + // NOTE(bill): No need to use return value + ssa_build_expr(proc, es->expr); + case_end; + + case_ast_node(bs, BlockStmt, node); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, bs->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + case_ast_node(ds, DeferStmt, node); + ssa_emit_comment(proc, str_lit("DeferStmt")); + isize scope_index = proc->scope_index; + if (ds->stmt->kind == AstNode_BlockStmt) { + scope_index--; + } + ssa_add_defer_node(proc, scope_index, ds->stmt); + case_end; + + case_ast_node(rs, ReturnStmt, node); + ssa_emit_comment(proc, str_lit("ReturnStmt")); + ssaValue *v = NULL; + TypeTuple *return_type_tuple = &proc->type->Proc.results->Tuple; + isize return_count = proc->type->Proc.result_count; + if (return_count == 0) { + // No return values + } else if (return_count == 1) { + Entity *e = return_type_tuple->variables[0]; + v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results.e[0]), e->type); + } else { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + ssaValueArray results; + array_init_reserve(&results, proc->module->tmp_allocator, return_count); + + for_array(res_index, rs->results) { + ssaValue *res = ssa_build_expr(proc, rs->results.e[res_index]); + Type *t = ssa_type(res); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, res, i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + + Type *ret_type = proc->type->Proc.results; + v = ssa_add_local_generated(proc, ret_type); + for_array(i, results) { + Entity *e = return_type_tuple->variables[i]; + ssaValue *res = ssa_emit_conv(proc, results.e[i], e->type); + ssaValue *field = ssa_emit_struct_ep(proc, v, i); + ssa_emit_store(proc, field, res); + } + + v = ssa_emit_load(proc, v); + + gb_temp_arena_memory_end(tmp); + } + ssa_emit_return(proc, v); + + case_end; + + case_ast_node(is, IfStmt, node); + ssa_emit_comment(proc, str_lit("IfStmt")); + if (is->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "if.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, is->init); + } + ssaBlock *then = ssa_add_block(proc, node, "if.then"); + ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later + ssaBlock *else_ = done; + if (is->else_stmt != NULL) { + else_ = ssa_add_block(proc, is->else_stmt, "if.else"); + } + + ssa_build_cond(proc, is->cond, then, else_); + proc->curr_block = then; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + + if (is->else_stmt != NULL) { + proc->curr_block = else_; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->else_stmt); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + } + proc->curr_block = done; + case_end; + + case_ast_node(fs, ForStmt, node); + ssa_emit_comment(proc, str_lit("ForStmt")); + if (fs->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "for.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, fs->init); + } + ssaBlock *body = ssa_add_block(proc, node, "for.body"); + ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later + + ssaBlock *loop = body; + + if (fs->cond != NULL) { + loop = ssa_add_block(proc, node, "for.loop"); + } + ssaBlock *cont = loop; + if (fs->post != NULL) { + cont = ssa_add_block(proc, node, "for.post"); + + } + ssa_emit_jump(proc, loop); + proc->curr_block = loop; + if (loop != body) { + ssa_build_cond(proc, fs->cond, body, done); + proc->curr_block = body; + } + + ssa_push_target_list(proc, done, cont, NULL); + + ssa_open_scope(proc); + ssa_build_stmt(proc, fs->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_pop_target_list(proc); + ssa_emit_jump(proc, cont); + + if (fs->post != NULL) { + proc->curr_block = cont; + ssa_build_stmt(proc, fs->post); + ssa_emit_jump(proc, loop); + } + + + proc->curr_block = done; + + case_end; + + case_ast_node(ms, MatchStmt, node); + ssa_emit_comment(proc, str_lit("MatchStmt")); + if (ms->init != NULL) { + ssa_build_stmt(proc, ms->init); + } + ssaValue *tag = v_true; + if (ms->tag != NULL) { + tag = ssa_build_expr(proc, ms->tag); + } + ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + AstNodeArray default_stmts = {0}; + ssaBlock *default_fall = NULL; + ssaBlock *default_block = NULL; + + ssaBlock *fall = NULL; + bool append_fall = false; + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts.e[i]; + ssaBlock *body = fall; + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + if (cc->list.count == 0) { + body = ssa_add_block(proc, clause, "match.dflt.body"); + } else { + body = ssa_add_block(proc, clause, "match.case.body"); + } + } + if (append_fall && body == fall) { + append_fall = false; + } + + fall = done; + if (i+1 < case_count) { + append_fall = true; + fall = ssa_add_block(proc, clause, "match.fall.body"); + } + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_fall = fall; + default_block = body; + continue; + } + + ssaBlock *next_cond = NULL; + for_array(j, cc->list) { + AstNode *expr = cc->list.e[j]; + next_cond = ssa_add_block(proc, clause, "match.case.next"); + + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr)); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + } + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + ssa_pop_target_list(proc); + + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, default_fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + + case_ast_node(ms, TypeMatchStmt, node); + ssa_emit_comment(proc, str_lit("TypeMatchStmt")); + gbAllocator allocator = proc->module->allocator; + + ssaValue *parent = ssa_build_expr(proc, ms->tag); + bool is_union_ptr = false; + bool is_any = false; + GB_ASSERT(check_valid_type_match_type(ssa_type(parent), &is_union_ptr, &is_any)); + + ssaValue *tag_index = NULL; + ssaValue *union_data = NULL; + if (is_union_ptr) { + ssa_emit_comment(proc, str_lit("get union's tag")); + tag_index = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, parent)); + union_data = ssa_emit_conv(proc, parent, t_rawptr); + } + + ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first"); + ssa_emit_jump(proc, start_block); + proc->curr_block = start_block; + + ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + String tag_var_name = ms->var->Ident.string; + + AstNodeArray default_stmts = {0}; + ssaBlock *default_block = NULL; + + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts.e[i]; + ast_node(cc, CaseClause, clause); + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_block = ssa_add_block(proc, clause, "type-match.dflt.body"); + continue; + } + + + ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body"); + + Scope *scope = *map_scope_get(&proc->module->info->scopes, hash_pointer(clause)); + Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); + GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name)); + + ssaBlock *next_cond = NULL; + ssaValue *cond = NULL; + + if (is_union_ptr) { + Type *bt = type_deref(tag_var_entity->type); + ssaValue *index = NULL; + Type *ut = base_type(type_deref(ssa_type(parent))); + GB_ASSERT(ut->Record.kind == TypeRecord_Union); + for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { + Entity *f = ut->Record.fields[field_index]; + if (are_types_identical(f->type, bt)) { + index = ssa_make_const_int(allocator, field_index); + break; + } + } + GB_ASSERT(index != NULL); + + ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); + ssaValue *data_ptr = ssa_emit_conv(proc, union_data, tag_var_entity->type); + ssa_emit_store(proc, tag_var, data_ptr); + + cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index); + } else if (is_any) { + Type *type = tag_var_entity->type; + ssaValue *any_data = ssa_emit_struct_ev(proc, parent, 1); + ssaValue *data = ssa_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); + ssa_module_add_value(proc->module, tag_var_entity, data); + + ssaValue *any_ti = ssa_emit_struct_ev(proc, parent, 0); + ssaValue *case_ti = ssa_type_info(proc, type); + cond = ssa_emit_comp(proc, Token_CmpEq, any_ti, case_ti); + } else { + GB_PANIC("Invalid type for type match statement"); + } + + next_cond = ssa_add_block(proc, clause, "type-match.case.next"); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + ssa_pop_target_list(proc); + + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + case_ast_node(bs, BranchStmt, node); + ssaBlock *block = NULL; + switch (bs->token.kind) { + case Token_break: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->break_; + } + break; + case Token_continue: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->continue_; + } + break; + case Token_fallthrough: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->fallthrough_; + } + break; + } + if (block != NULL) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block); + } + switch (bs->token.kind) { + case Token_break: ssa_emit_comment(proc, str_lit("break")); break; + case Token_continue: ssa_emit_comment(proc, str_lit("continue")); break; + case Token_fallthrough: ssa_emit_comment(proc, str_lit("fallthrough")); break; + } + ssa_emit_jump(proc, block); + ssa_emit_unreachable(proc); + case_end; + + + + case_ast_node(pa, PushAllocator, node); + ssa_emit_comment(proc, str_lit("PushAllocator")); + ssa_open_scope(proc); + + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); + ssaValue *prev_context = ssa_add_local_generated(proc, t_context); + ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1); + ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + + case_ast_node(pa, PushContext, node); + ssa_emit_comment(proc, str_lit("PushContext")); + ssa_open_scope(proc); + + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); + ssaValue *prev_context = ssa_add_local_generated(proc, t_context); + ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Procedure +// +//////////////////////////////////////////////////////////////// + +void ssa_number_proc_registers(ssaProcedure *proc) { + i32 reg_index = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + b->index = i; + for_array(j, b->instrs) { + ssaValue *value = b->instrs.e[j]; + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + continue; + } + value->index = reg_index; + reg_index++; + } + } +} + +void ssa_begin_procedure_body(ssaProcedure *proc) { + array_add(&proc->module->procs, proc); + + array_init(&proc->blocks, heap_allocator()); + array_init(&proc->defer_stmts, heap_allocator()); + array_init(&proc->children, heap_allocator()); + + proc->decl_block = ssa_add_block(proc, proc->type_expr, "decls"); + proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry"); + proc->curr_block = proc->entry_block; + + if (proc->type->Proc.params != NULL) { + TypeTuple *params = &proc->type->Proc.params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + ssaValue *param = ssa_add_param(proc, e); + array_add(&proc->params, param); + } + } +} + + +void ssa_end_procedure_body(ssaProcedure *proc) { + if (proc->type->Proc.result_count == 0) { + ssa_emit_return(proc, NULL); + } + + if (proc->curr_block->instrs.count == 0) { + ssa_emit_unreachable(proc); + } + + proc->curr_block = proc->decl_block; + ssa_emit_jump(proc, proc->entry_block); + + ssa_number_proc_registers(proc); +} + + +void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) { + if (parent == NULL) { + if (str_eq(proc->name, str_lit("main"))) { + ssa_emit_startup_runtime(proc); + } + } +} + +void ssa_build_proc(ssaValue *value, ssaProcedure *parent) { + ssaProcedure *proc = &value->Proc; + + proc->parent = parent; + + if (proc->entity != NULL) { + ssaModule *m = proc->module; + CheckerInfo *info = m->info; + Entity *e = proc->entity; + String filename = e->token.pos.file; + AstFile **found = map_ast_file_get(&info->files, hash_string(filename)); + GB_ASSERT(found != NULL); + AstFile *f = *found; + ssaDebugInfo *di_file = NULL; + + ssaDebugInfo **di_file_found = map_ssa_debug_info_get(&m->debug_info, hash_pointer(f)); + if (di_file_found) { + di_file = *di_file_found; + GB_ASSERT(di_file->kind == ssaDebugInfo_File); + } else { + di_file = ssa_add_debug_info_file(proc, f); + } + + ssa_add_debug_info_proc(proc, e, proc->name, di_file); + } + + if (proc->body != NULL) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + + if (proc->tags != 0) { + u32 in = proc->tags; + u32 out = proc->module->stmt_state_flags; + if (in & ProcTag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & ProcTag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + proc->module->stmt_state_flags = out; + } + + + ssa_begin_procedure_body(proc); + ssa_insert_code_before_proc(proc, parent); + ssa_build_stmt(proc, proc->body); + ssa_end_procedure_body(proc); + + proc->module->stmt_state_flags = prev_stmt_state_flags; + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Module +// +//////////////////////////////////////////////////////////////// + + + +void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) { + map_ssa_value_set(&m->values, hash_pointer(e), v); +} + +void ssa_init_module(ssaModule *m, Checker *c) { + // TODO(bill): Determine a decent size for the arena + isize token_count = c->parser->total_token_count; + isize arena_size = 4 * token_count * gb_size_of(ssaValue); + gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size); + gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size); + m->allocator = gb_arena_allocator(&m->arena); + m->tmp_allocator = gb_arena_allocator(&m->tmp_arena); + m->info = &c->info; + m->sizes = c->sizes; + + map_ssa_value_init(&m->values, heap_allocator()); + map_ssa_value_init(&m->members, heap_allocator()); + map_ssa_debug_info_init(&m->debug_info, heap_allocator()); + map_string_init(&m->type_names, heap_allocator()); + array_init(&m->procs, heap_allocator()); + array_init(&m->procs_to_generate, heap_allocator()); + + // Default states + m->stmt_state_flags = 0; + m->stmt_state_flags |= StmtStateFlag_bounds_check; + + { + // Add type info data + { + String name = str_lit(SSA_TYPE_INFO_DATA_NAME); + isize count = c->info.type_info_map.entries.count; + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + g->Global.is_private = true; + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + } + + // Type info member buffer + { + // NOTE(bill): Removes need for heap allocation by making it global memory + isize count = 0; + + for_array(entry_index, m->info->type_info_map.entries) { + MapIsizeEntry *entry = &m->info->type_info_map.entries.e[entry_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + + switch (t->kind) { + case Type_Record: + switch (t->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + count += t->Record.field_count; + } + break; + case Type_Tuple: + count += t->Tuple.variable_count; + break; + } + } + + String name = str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME); + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), + make_type_array(m->allocator, t_type_info_member, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + ssa_module_add_value(m, e, g); + map_ssa_value_set(&m->members, hash_string(name), g); + } + } + + { + ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit); + di->CompileUnit.file = m->info->files.entries.e[0].value; // Zeroth is the init file + di->CompileUnit.producer = str_lit("odin"); + + map_ssa_debug_info_set(&m->debug_info, hash_pointer(m), di); + } +} + +void ssa_destroy_module(ssaModule *m) { + map_ssa_value_destroy(&m->values); + map_ssa_value_destroy(&m->members); + map_string_destroy(&m->type_names); + map_ssa_debug_info_destroy(&m->debug_info); + array_free(&m->procs_to_generate); + gb_arena_free(&m->arena); +} + + + +//////////////////////////////////////////////////////////////// +// +// @Code Generation +// +//////////////////////////////////////////////////////////////// + + +bool ssa_gen_init(ssaGen *s, Checker *c) { + if (global_error_collector.count != 0) { + return false; + } + + isize tc = c->parser->total_token_count; + if (tc < 2) { + return false; + } + + ssa_init_module(&s->module, c); + s->module.generate_debug_info = false; + + // TODO(bill): generate appropriate output name + int pos = cast(int)string_extension_position(c->parser->init_fullpath); + gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text)); + if (err != gbFileError_None) { + return false; + } + + return true; +} + +void ssa_gen_destroy(ssaGen *s) { + ssa_destroy_module(&s->module); + gb_file_close(&s->output_file); +} + +String ssa_mangle_name(ssaGen *s, String path, String name) { + // NOTE(bill): prefix names not in the init scope + // TODO(bill): make robust and not just rely on the file's name + + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + AstFile *file = *map_ast_file_get(&info->files, hash_string(path)); + + char *str = gb_alloc_array(a, char, path.len+1); + gb_memmove(str, path.text, path.len); + str[path.len] = 0; + for (isize i = 0; i < path.len; i++) { + if (str[i] == '\\') { + str[i] = '/'; + } + } + + char const *base = gb_path_base_name(str); + char const *ext = gb_path_extension(base); + isize base_len = ext-1-base; + + isize max_len = base_len + 1 + 10 + 1 + name.len; + u8 *new_name = gb_alloc_array(a, u8, max_len); + isize new_name_len = gb_snprintf( + cast(char *)new_name, max_len, + "%.*s-%u.%.*s", + cast(int)base_len, base, + file->id, + LIT(name)); + + return make_string(new_name, new_name_len-1); +} + +ssaValue *ssa_get_type_info_ptr(ssaProcedure *proc, ssaValue *type_info_data, Type *type) { + i32 index = cast(i32)ssa_type_info_index(proc->module->info, type); + // gb_printf_err("%d %s\n", index, type_to_string(type)); + return ssa_emit_array_epi(proc, type_info_data, index); +} + +ssaValue *ssa_type_info_member_offset(ssaProcedure *proc, ssaValue *data, isize count, i32 *index) { + ssaValue *offset = ssa_emit_array_epi(proc, data, *index); + *index += count; + return offset; +} + +void ssa_gen_tree(ssaGen *s) { + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + + if (v_zero == NULL) { + v_zero = ssa_make_const_int (m->allocator, 0); + v_one = ssa_make_const_int (m->allocator, 1); + v_zero32 = ssa_make_const_i32 (m->allocator, 0); + v_one32 = ssa_make_const_i32 (m->allocator, 1); + v_two32 = ssa_make_const_i32 (m->allocator, 2); + v_false = ssa_make_const_bool(m->allocator, false); + v_true = ssa_make_const_bool(m->allocator, true); + } + + isize global_variable_max_count = 0; + Entity *entry_point = NULL; + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + String name = e->token.string; + if (e->kind == Entity_Variable) { + global_variable_max_count++; + } else if (e->kind == Entity_Procedure) { + if (e->scope->is_init && str_eq(name, str_lit("main"))) { + entry_point = e; + } + } + } + + typedef struct ssaGlobalVariable { + ssaValue *var, *init; + DeclInfo *decl; + } ssaGlobalVariable; + Array(ssaGlobalVariable) global_variables; + array_init_reserve(&global_variables, m->tmp_allocator, global_variable_max_count); + + m->min_dep_map = generate_minimum_dependency_map(info, entry_point); + + for_array(i, info->entities.entries) { + MapDeclInfoEntry *entry = &info->entities.entries.e[i]; + Entity *e = cast(Entity *)entry->key.ptr; + String name = e->token.string; + DeclInfo *decl = entry->value; + Scope *scope = e->scope; + + if (!scope->is_file) { + continue; + } + + if (map_entity_get(&m->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + continue; + } + + if (!scope->is_global && !scope->is_init) { + name = ssa_mangle_name(s, e->token.pos.file, name); + } + + + switch (e->kind) { + case Entity_TypeName: + GB_ASSERT(e->type->kind == Type_Named); + map_string_set(&m->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(m, e, name); + break; + + case Entity_Variable: { + ssaValue *g = ssa_make_value_global(a, e, NULL); + if (decl->var_decl_tags & VarDeclTag_thread_local) { + g->Global.is_thread_local = true; + } + ssaGlobalVariable var = {0}; + var.var = g; + var.decl = decl; + + if (decl->init_expr != NULL) { + TypeAndValue *tav = map_tav_get(&info->types, hash_pointer(decl->init_expr)); + if (tav != NULL) { + if (tav->value.kind != ExactValue_Invalid) { + ExactValue v = tav->value; + // if (v.kind != ExactValue_String) { + g->Global.value = ssa_add_module_constant(m, tav->type, v); + // } + } + } + } + + if (g->Global.value == NULL) { + array_add(&global_variables, var); + } + + map_ssa_value_set(&m->values, hash_pointer(e), g); + map_ssa_value_set(&m->members, hash_string(name), g); + } break; + + case Entity_Procedure: { + AstNodeProcDecl *pd = &decl->proc_decl->ProcDecl; + String original_name = name; + AstNode *body = pd->body; + if (pd->tags & ProcTag_foreign) { + name = pd->name->Ident.string; + } + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } else if (pd->link_name.len > 0) { + name = pd->link_name; + } + + ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + p->Proc.tags = pd->tags; + + map_ssa_value_set(&m->values, hash_pointer(e), p); + HashKey hash_name = hash_string(name); + if (map_ssa_value_get(&m->members, hash_name) == NULL) { + map_ssa_value_set(&m->members, hash_name, p); + } + } break; + } + } + + for_array(i, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[i]; + ssaValue *v = entry->value; + if (v->kind == ssaValue_Proc) + ssa_build_proc(v, NULL); + } + + ssaDebugInfo *compile_unit = m->debug_info.entries.e[0].value; + GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit); + ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs); + + isize all_proc_max_count = 0; + for_array(i, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + all_proc_max_count++; + } + } + + array_init_reserve(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); + map_ssa_debug_info_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped + compile_unit->CompileUnit.all_procs = all_procs; + + + for_array(i, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + array_add(&all_procs->AllProcs.procs, di); + } + } + + + { // Startup Runtime + // Cleanup(bill): probably better way of doing code insertion + String name = str_lit(SSA_STARTUP_RUNTIME_PROC_NAME); + Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), + NULL, 0, + NULL, 0, false); + AstNode *body = gb_alloc_item(a, AstNode); + ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name); + Token token = {0}; + token.string = name; + Entity *e = make_entity_procedure(a, NULL, token, proc_type); + + map_ssa_value_set(&m->values, hash_pointer(e), p); + map_ssa_value_set(&m->members, hash_string(name), p); + + ssaProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + + ssa_begin_procedure_body(proc); + + // TODO(bill): Should do a dependency graph do check which order to initialize them in? + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->decl->init_expr != NULL) { + var->init = ssa_build_expr(proc, var->decl->init_expr); + } + } + + // NOTE(bill): Initialize constants first + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind == ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind != ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + { // NOTE(bill): Setup type_info data + // TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR + ssaValue *type_info_data = NULL; + ssaValue *type_info_member_data = NULL; + + ssaValue **found = NULL; + found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + type_info_data = *found; + + found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME))); + GB_ASSERT(found != NULL); + type_info_member_data = *found; + + CheckerInfo *info = proc->module->info; + + // Useful types + Type *t_i64_slice_ptr = make_type_pointer(a, make_type_slice(a, t_i64)); + Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string)); + + i32 type_info_member_index = 0; + + for_array(type_info_map_index, info->type_info_map.entries) { + MapIsizeEntry *entry = &info->type_info_map.entries.e[type_info_map_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + t = default_type(t); + isize entry_index = entry->value; + + ssaValue *tag = NULL; + + switch (t->kind) { + case Type_Named: { + tag = ssa_add_local_generated(proc, t_type_info_named); + + // TODO(bill): Which is better? The mangled name or actual name? + ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string); + ssaValue *gtip = ssa_get_type_info_ptr(proc, type_info_data, t->Named.base); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip); + } break; + + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: + tag = ssa_add_local_generated(proc, t_type_info_boolean); + break; + case Basic_i8: + case Basic_u8: + case Basic_i16: + case Basic_u16: + case Basic_i32: + case Basic_u32: + case Basic_i64: + case Basic_u64: + case Basic_i128: + case Basic_u128: + case Basic_int: + case Basic_uint: { + tag = ssa_add_local_generated(proc, t_type_info_integer); + bool is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed); + } break; + + // case Basic_f16: + case Basic_f32: + case Basic_f64: + // case Basic_f128: + { + tag = ssa_add_local_generated(proc, t_type_info_float); + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + } break; + + case Basic_rawptr: + tag = ssa_add_local_generated(proc, t_type_info_pointer); + break; + + case Basic_string: + tag = ssa_add_local_generated(proc, t_type_info_string); + break; + + case Basic_any: + tag = ssa_add_local_generated(proc, t_type_info_any); + break; + } + break; + + case Type_Pointer: { + tag = ssa_add_local_generated(proc, t_type_info_pointer); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Maybe: { + tag = ssa_add_local_generated(proc, t_type_info_maybe); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Maybe.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Array: { + tag = ssa_add_local_generated(proc, t_type_info_array); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Array.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Array.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count)); + + } break; + case Type_Slice: { + tag = ssa_add_local_generated(proc, t_type_info_slice); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Slice.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Slice.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + } break; + case Type_Vector: { + tag = ssa_add_local_generated(proc, t_type_info_vector); + ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Vector.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Vector.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count)); + + ssaValue *align = ssa_emit_struct_ep(proc, tag, 3); + ssa_emit_store(proc, count, ssa_make_const_int(a, type_align_of(m->sizes, a, t))); + + } break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + tag = ssa_add_local_generated(proc, t_type_info_struct); + + { + ssaValue *packed = ssa_make_const_bool(a, t->Record.struct_is_packed); + ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered); + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet + for (isize source_index = 0; source_index < t->Record.field_count; source_index++) { + // TODO(bill): Order fields in source order not layout order + Entity *f = t->Record.fields_in_src_order[source_index]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Union: + tag = ssa_add_local_generated(proc, t_type_info_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + break; + case TypeRecord_RawUnion: { + tag = ssa_add_local_generated(proc, t_type_info_raw_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + for (isize i = 0; i < t->Record.field_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + Entity *f = t->Record.fields[i]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, 0)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Enum: { + tag = ssa_add_local_generated(proc, t_type_info_enum); + Type *enum_base = t->Record.enum_base; + if (enum_base == NULL) { + enum_base = t_int; + } + ssaValue *base = ssa_emit_struct_ep(proc, tag, 0); + ssa_emit_store(proc, base, ssa_get_type_info_ptr(proc, type_info_data, enum_base)); + + if (t->Record.other_field_count > 0) { + Entity **fields = t->Record.other_fields; + isize count = t->Record.other_field_count; + ssaValue *value_array = NULL; + ssaValue *name_array = NULL; + + + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_values"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count)); + value_array = ssa_make_value_global(a, e, NULL); + value_array->Global.is_private = true; + ssa_module_add_value(m, e, value_array); + map_ssa_value_set(&m->members, hash_string(token.string), value_array); + } + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_names"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count)); + name_array = ssa_make_value_global(a, e, NULL); + name_array->Global.is_private = true; + ssa_module_add_value(m, e, name_array); + map_ssa_value_set(&m->members, hash_string(token.string), name_array); + } + + for (isize i = 0; i < count; i++) { + ssaValue *value_gep = ssa_emit_array_epi(proc, value_array, i); + ssaValue *name_gep = ssa_emit_array_epi(proc, name_array, i); + + ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer)); + ssa_emit_store(proc, name_gep, ssa_make_const_string(a, fields[i]->token.string)); + } + + ssaValue *v_count = ssa_make_const_int(a, count); + + + ssaValue *values = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *names = ssa_emit_struct_ep(proc, tag, 2); + ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr)); + ssaValue *name_slice = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr)); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count); + + ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice)); + ssa_emit_store(proc, names, ssa_emit_load(proc, name_slice)); + } + } break; + } + } break; + + case Type_Tuple: { + tag = ssa_add_local_generated(proc, t_type_info_tuple); + + { + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index); + + for (isize i = 0; i < t->Tuple.variable_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + // NOTE(bill): offset is not used for tuples + + Entity *f = t->Tuple.variables[i]; + ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, variable_count); + ssa_emit_store(proc, cap, variable_count); + } break; + + case Type_Proc: { + tag = ssa_add_local_generated(proc, t_type_info_procedure); + + ssaValue *params = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *results = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2); + + if (t->Proc.params) { + ssa_emit_store(proc, params, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.params)); + } + if (t->Proc.results) { + ssa_emit_store(proc, results, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.results)); + } + ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic)); + + // TODO(bill): Type_Info for procedures + } break; + } + + if (tag != NULL) { + ssaValue *gep = ssa_emit_array_epi(proc, type_info_data, entry_index); + ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info); + ssa_emit_store(proc, gep, val); + } + } + } + + ssa_end_procedure_body(proc); + } + + for_array(i, m->procs_to_generate) { + ssa_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); + } + + // { + // DWORD old_protect = 0; + // DWORD new_protect = PAGE_READONLY; + // BOOL ok = VirtualProtect(m->arena.physical_start, m->arena.total_size, new_protect, &old_protect); + // } + + + + // m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); +} + diff --git a/src/ssa_opt.c b/src/ssa_opt.c new file mode 100644 index 000000000..5fccbfcb6 --- /dev/null +++ b/src/ssa_opt.c @@ -0,0 +1,493 @@ +// Optimizations for the SSA code + +void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) { + switch (i->kind) { + case ssaInstr_Comment: + break; + case ssaInstr_Local: + break; + case ssaInstr_ZeroInit: + array_add(ops, i->ZeroInit.address); + break; + case ssaInstr_Store: + array_add(ops, i->Store.address); + array_add(ops, i->Store.value); + break; + case ssaInstr_Load: + array_add(ops, i->Load.address); + break; + case ssaInstr_ArrayElementPtr: + array_add(ops, i->ArrayElementPtr.address); + array_add(ops, i->ArrayElementPtr.elem_index); + break; + case ssaInstr_StructElementPtr: + array_add(ops, i->StructElementPtr.address); + break; + case ssaInstr_PtrOffset: + array_add(ops, i->PtrOffset.address); + array_add(ops, i->PtrOffset.offset); + break; + case ssaInstr_ArrayExtractValue: + array_add(ops, i->ArrayExtractValue.address); + break; + case ssaInstr_StructExtractValue: + array_add(ops, i->StructExtractValue.address); + break; + case ssaInstr_Conv: + array_add(ops, i->Conv.value); + break; + case ssaInstr_Jump: + break; + case ssaInstr_If: + array_add(ops, i->If.cond); + break; + case ssaInstr_Return: + if (i->Return.value != NULL) { + array_add(ops, i->Return.value); + } + break; + case ssaInstr_Select: + array_add(ops, i->Select.cond); + break; + case ssaInstr_Phi: + for_array(j, i->Phi.edges) { + array_add(ops, i->Phi.edges.e[j]); + } + break; + case ssaInstr_Unreachable: break; + case ssaInstr_BinaryOp: + array_add(ops, i->BinaryOp.left); + array_add(ops, i->BinaryOp.right); + break; + case ssaInstr_Call: + array_add(ops, i->Call.value); + for (isize j = 0; j < i->Call.arg_count; j++) { + array_add(ops, i->Call.args[j]); + } + break; + case ssaInstr_VectorExtractElement: + array_add(ops, i->VectorExtractElement.vector); + array_add(ops, i->VectorExtractElement.index); + break; + case ssaInstr_VectorInsertElement: + array_add(ops, i->VectorInsertElement.vector); + array_add(ops, i->VectorInsertElement.elem); + array_add(ops, i->VectorInsertElement.index); + break; + case ssaInstr_VectorShuffle: + array_add(ops, i->VectorShuffle.vector); + break; + case ssaInstr_StartupRuntime: + break; + case ssaInstr_BoundsCheck: + array_add(ops, i->BoundsCheck.index); + array_add(ops, i->BoundsCheck.len); + break; + case ssaInstr_SliceBoundsCheck: + array_add(ops, i->SliceBoundsCheck.low); + array_add(ops, i->SliceBoundsCheck.high); + array_add(ops, i->SliceBoundsCheck.max); + break; + + + } +} + + + + + +void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) { + for_array(i, b->preds) { + ssaBlock *pred = b->preds.e[i]; + if (pred == from) { + b->preds.e[i] = to; + } + } +} + +void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) { + for_array(i, b->succs) { + ssaBlock *succ = b->succs.e[i]; + if (succ == from) { + b->succs.e[i] = to; + } + } +} + +bool ssa_opt_block_has_phi(ssaBlock *b) { + return b->instrs.e[0]->Instr.kind == ssaInstr_Phi; +} + + + + + + + + + + +ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) { + ssaValueArray phis = {0}; + for_array(i, b->instrs) { + ssaInstr *instr = &b->instrs.e[i]->Instr; + if (instr->kind != ssaInstr_Phi) { + phis = b->instrs; + phis.count = i; + return phis; + } + } + return phis; +} + +void ssa_remove_pred(ssaBlock *b, ssaBlock *p) { + ssaValueArray phis = ssa_get_block_phi_nodes(b); + isize i = 0; + for_array(j, b->preds) { + ssaBlock *pred = b->preds.e[j]; + if (pred != p) { + b->preds.e[i] = b->preds.e[j]; + for_array(k, phis) { + ssaInstrPhi *phi = &phis.e[k]->Instr.Phi; + phi->edges.e[i] = phi->edges.e[j]; + } + i++; + } + } + b->preds.count = i; + for_array(k, phis) { + ssaInstrPhi *phi = &phis.e[k]->Instr.Phi; + phi->edges.count = i; + } + +} + +void ssa_remove_dead_blocks(ssaProcedure *proc) { + isize j = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b == NULL) { + continue; + } + // NOTE(bill): Swap order + b->index = j; + proc->blocks.e[j++] = b; + } + proc->blocks.count = j; +} + +void ssa_mark_reachable(ssaBlock *b) { + isize const WHITE = 0; + isize const BLACK = -1; + b->index = BLACK; + for_array(i, b->succs) { + ssaBlock *succ = b->succs.e[i]; + if (succ->index == WHITE) { + ssa_mark_reachable(succ); + } + } +} + +void ssa_remove_unreachable_blocks(ssaProcedure *proc) { + isize const WHITE = 0; + isize const BLACK = -1; + for_array(i, proc->blocks) { + proc->blocks.e[i]->index = WHITE; + } + + ssa_mark_reachable(proc->blocks.e[0]); + + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b->index == WHITE) { + for_array(j, b->succs) { + ssaBlock *c = b->succs.e[j]; + if (c->index == BLACK) { + ssa_remove_pred(c, b); + } + } + // NOTE(bill): Mark as empty but don't actually free it + // As it's been allocated with an arena + proc->blocks.e[i] = NULL; + } + } + ssa_remove_dead_blocks(proc); +} + +bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) { + if (a->succs.count != 1) { + return false; + } + ssaBlock *b = a->succs.e[0]; + if (b->preds.count != 1) { + return false; + } + + if (ssa_opt_block_has_phi(b)) { + return false; + } + + array_pop(&a->instrs); // Remove branch at end + for_array(i, b->instrs) { + array_add(&a->instrs, b->instrs.e[i]); + ssa_set_instr_parent(b->instrs.e[i], a); + } + + array_clear(&a->succs); + for_array(i, b->succs) { + array_add(&a->succs, b->succs.e[i]); + } + + // Fix preds links + for_array(i, b->succs) { + ssa_opt_block_replace_pred(b->succs.e[i], b, a); + } + + proc->blocks.e[b->index] = NULL; + return true; +} + +void ssa_opt_blocks(ssaProcedure *proc) { + ssa_remove_unreachable_blocks(proc); + +#if 1 + bool changed = true; + while (changed) { + changed = false; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + if (b == NULL) { + continue; + } + GB_ASSERT(b->index == i); + + if (ssa_opt_block_fusion(proc, b)) { + changed = true; + } + // TODO(bill): other simple block optimizations + } + } +#endif + + ssa_remove_dead_blocks(proc); +} +void ssa_opt_build_referrers(ssaProcedure *proc) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer + array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks.e[i]; + for_array(j, b->instrs) { + ssaValue *instr = b->instrs.e[j]; + array_clear(&ops); + ssa_opt_add_operands(&ops, &instr->Instr); + for_array(k, ops) { + ssaValue *op = ops.e[k]; + if (op == NULL) { + continue; + } + ssaValueArray *refs = ssa_value_referrers(op); + if (refs != NULL) { + array_add(refs, instr); + } + } + } + } + + gb_temp_arena_memory_end(tmp); +} + + + + + + + +// State of Lengauer-Tarjan algorithm +// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf +typedef struct ssaLTState { + isize count; + // NOTE(bill): These are arrays + ssaBlock **sdom; // Semidominator + ssaBlock **parent; // Parent in DFS traversal of CFG + ssaBlock **ancestor; +} ssaLTState; + +// §2.2 - bottom of page +void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) { + lt->ancestor[q->index] = p; +} + +i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) { + preorder[i] = p; + p->dom.pre = i++; + lt->sdom[p->index] = p; + ssa_lt_link(lt, NULL, p); + for_array(index, p->succs) { + ssaBlock *q = p->succs.e[index]; + if (lt->sdom[q->index] == NULL) { + lt->parent[q->index] = p; + i = ssa_lt_depth_first_search(lt, q, i, preorder); + } + } + return i; +} + +ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) { + ssaBlock *u = v; + for (; + lt->ancestor[v->index] != NULL; + v = lt->ancestor[v->index]) { + if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) { + u = v; + } + } + return u; +} + +typedef struct ssaDomPrePost { + i32 pre, post; +} ssaDomPrePost; + +ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) { + ssaDomPrePost result = {pre, post}; + + v->dom.pre = pre++; + for_array(i, v->dom.children) { + result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post); + } + v->dom.post = post++; + + result.pre = pre; + result.post = post; + return result; +} + + +// NOTE(bill): Requires `ssa_opt_blocks` to be called before this +void ssa_opt_build_dom_tree(ssaProcedure *proc) { + // Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + isize n = proc->blocks.count; + ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n); + + ssaLTState lt = {0}; + lt.count = n; + lt.sdom = &buf[0*n]; + lt.parent = &buf[1*n]; + lt.ancestor = &buf[2*n]; + + ssaBlock **preorder = &buf[3*n]; + ssaBlock **buckets = &buf[4*n]; + ssaBlock *root = proc->blocks.e[0]; + + // Step 1 - number vertices + i32 pre_num = ssa_lt_depth_first_search(<, root, 0, preorder); + gb_memmove(buckets, preorder, n*gb_size_of(preorder[0])); + + for (i32 i = n-1; i > 0; i--) { + ssaBlock *w = preorder[i]; + + // Step 3 - Implicitly define idom for nodes + for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) { + ssaBlock *u = ssa_lt_eval(<, v); + if (lt.sdom[u->index]->dom.pre < i) { + v->dom.idom = u; + } else { + v->dom.idom = w; + } + } + + // Step 2 - Compute all sdoms + lt.sdom[w->index] = lt.parent[w->index]; + for_array(pred_index, w->preds) { + ssaBlock *v = w->preds.e[pred_index]; + ssaBlock *u = ssa_lt_eval(<, v); + if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) { + lt.sdom[w->index] = lt.sdom[u->index]; + } + } + + ssa_lt_link(<, lt.parent[w->index], w); + + if (lt.parent[w->index] == lt.sdom[w->index]) { + w->dom.idom = lt.parent[w->index]; + } else { + buckets[i] = buckets[lt.sdom[w->index]->dom.pre]; + buckets[lt.sdom[w->index]->dom.pre] = w; + } + } + + // The rest of Step 3 + for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) { + v->dom.idom = root; + } + + // Step 4 - Explicitly define idom for nodes (in preorder) + for (isize i = 1; i < n; i++) { + ssaBlock *w = preorder[i]; + if (w == root) { + w->dom.idom = NULL; + } else { + // Weird tree relationships here! + + if (w->dom.idom != lt.sdom[w->index]) { + w->dom.idom = w->dom.idom->dom.idom; + } + + // Calculate children relation as inverse of idom + if (w->dom.idom->dom.children.e == NULL) { + // TODO(bill): Is this good enough for memory allocations? + array_init(&w->dom.idom->dom.children, heap_allocator()); + } + array_add(&w->dom.idom->dom.children, w); + } + } + + ssa_opt_number_dom_tree(root, 0, 0); + + gb_temp_arena_memory_end(tmp); +} + +void ssa_opt_mem2reg(ssaProcedure *proc) { + // TODO(bill): ssa_opt_mem2reg +} + + + +void ssa_opt_tree(ssaGen *s) { + s->opt_called = true; + + for_array(member_index, s->module.procs) { + ssaProcedure *proc = s->module.procs.e[member_index]; + if (proc->blocks.count == 0) { // Prototype/external procedure + continue; + } + + ssa_opt_blocks(proc); + #if 1 + ssa_opt_build_referrers(proc); + ssa_opt_build_dom_tree(proc); + + // TODO(bill): ssa optimization + // [ ] cse (common-subexpression) elim + // [ ] copy elim + // [ ] dead code elim + // [ ] dead store/load elim + // [ ] phi elim + // [ ] short circuit elim + // [ ] bounds check elim + // [ ] lift/mem2reg + // [ ] lift/mem2reg + + ssa_opt_mem2reg(proc); + #endif + + GB_ASSERT(proc->blocks.count > 0); + ssa_number_proc_registers(proc); + } +} diff --git a/src/ssa_print.c b/src/ssa_print.c new file mode 100644 index 000000000..e6e6532d5 --- /dev/null +++ b/src/ssa_print.c @@ -0,0 +1,1439 @@ +typedef struct ssaFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile * output; +} ssaFileBuffer; + +void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { + isize size = 8*gb_virtual_memory_page_size(NULL); + f->vm = gb_vm_alloc(NULL, size); + f->offset = 0; + f->output = output; +} + +void ssa_file_buffer_destroy(ssaFileBuffer *f) { + if (f->offset > 0) { + // NOTE(bill): finish writing buffered data + gb_file_write(f->output, f->vm.data, f->offset); + } + + gb_vm_free(f->vm); +} + +void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { + if (len > f->vm.size) { + gb_file_write(f->output, data, len); + return; + } + + if ((f->vm.size - f->offset) < len) { + gb_file_write(f->output, f->vm.data, f->offset); + f->offset = 0; + } + u8 *cursor = cast(u8 *)f->vm.data + f->offset; + gb_memmove(cursor, data, len); + f->offset += len; +} + + +void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { + va_list va; + va_start(va, fmt); + char buf[4096] = {0}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + ssa_file_buffer_write(f, buf, len-1); + va_end(va); +} + + +void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { + ssa_file_buffer_write(f, data, len); +} + + +bool ssa_valid_char(u8 c) { + if (c >= 0x80) { + return false; + } + + if (gb_char_is_alphanumeric(c)) { + return true; + } + + switch (c) { + case '$': + case '-': + case '.': + case '_': + return true; + } + + return false; +} + +void ssa_print_escape_string(ssaFileBuffer *f, String name, bool print_quotes) { + isize extra = 0; + for (isize i = 0; i < name.len; i++) { + u8 c = name.text[i]; + if (!ssa_valid_char(c)) { + extra += 2; + } + } + + if (extra == 0) { + ssa_fprintf(f, "%.*s", LIT(name)); + return; + } + + + char hex_table[] = "0123456789ABCDEF"; + isize buf_len = name.len + extra + 2; + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena); + + u8 *buf = gb_alloc_array(string_buffer_allocator, u8, buf_len); + + isize j = 0; + + if (print_quotes) { + buf[j++] = '"'; + } + + for (isize i = 0; i < name.len; i++) { + u8 c = name.text[i]; + if (ssa_valid_char(c)) { + buf[j++] = c; + } else { + buf[j] = '\\'; + buf[j+1] = hex_table[c >> 4]; + buf[j+2] = hex_table[c & 0x0f]; + j += 3; + } + } + + if (print_quotes) { + buf[j++] = '"'; + } + + ssa_file_write(f, buf, j); + + gb_temp_arena_memory_end(tmp); +} + + + +void ssa_print_encoded_local(ssaFileBuffer *f, String name) { + ssa_fprintf(f, "%%"); + ssa_print_escape_string(f, name, true); +} + +void ssa_print_encoded_global(ssaFileBuffer *f, String name, bool global_scope) { + ssa_fprintf(f, "@"); + if (!global_scope && str_ne(name, str_lit("main"))) { + ssa_fprintf(f, "."); + } + ssa_print_escape_string(f, name, true); +} + + +void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) { + BaseTypeSizes s = m->sizes; + i64 word_bits = 8*s.word_size; + GB_ASSERT_NOT_NULL(t); + t = default_type(t); + + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: ssa_fprintf(f, "i1"); break; + case Basic_i8: ssa_fprintf(f, "i8"); break; + case Basic_u8: ssa_fprintf(f, "i8"); break; + case Basic_i16: ssa_fprintf(f, "i16"); break; + case Basic_u16: ssa_fprintf(f, "i16"); break; + case Basic_i32: ssa_fprintf(f, "i32"); break; + case Basic_u32: ssa_fprintf(f, "i32"); break; + case Basic_i64: ssa_fprintf(f, "i64"); break; + case Basic_u64: ssa_fprintf(f, "i64"); break; + case Basic_i128: ssa_fprintf(f, "i128"); break; + case Basic_u128: ssa_fprintf(f, "i128"); break; + // case Basic_f16: ssa_fprintf(f, "half"); break; + case Basic_f32: ssa_fprintf(f, "float"); break; + case Basic_f64: ssa_fprintf(f, "double"); break; + // case Basic_f128: ssa_fprintf(f, "fp128"); break; + case Basic_rawptr: ssa_fprintf(f, "%%..rawptr"); break; + case Basic_string: ssa_fprintf(f, "%%..string"); break; + case Basic_uint: ssa_fprintf(f, "i%lld", word_bits); break; + case Basic_int: ssa_fprintf(f, "i%lld", word_bits); break; + case Basic_any: ssa_fprintf(f, "%%..any"); break; + } + break; + case Type_Pointer: + ssa_print_type(f, m, t->Pointer.elem); + ssa_fprintf(f, "*"); + break; + case Type_Maybe: + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t->Maybe.elem); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, "}"); + break; + case Type_Array: + ssa_fprintf(f, "[%lld x ", t->Array.count); + ssa_print_type(f, m, t->Array.elem); + ssa_fprintf(f, "]"); + break; + case Type_Vector: + ssa_fprintf(f, "<%lld x ", t->Vector.count); + ssa_print_type(f, m, t->Vector.elem); + ssa_fprintf(f, ">"); + break; + case Type_Slice: + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t->Slice.elem); + ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); + break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: + if (t->Record.struct_is_packed) { + ssa_fprintf(f, "<"); + } + ssa_fprintf(f, "{"); + for (isize i = 0; i < t->Record.field_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + Type *ft = t->Record.fields[i]->type; + Type *bft = base_type(ft); + if (!is_type_struct(bft)) { + ft = bft; + } + ssa_print_type(f, m, ft); + } + ssa_fprintf(f, "}"); + if (t->Record.struct_is_packed) { + ssa_fprintf(f, ">"); + } + break; + case TypeRecord_Union: { + // NOTE(bill): The zero size array is used to fix the alignment used in a structure as + // LLVM takes the first element's alignment as the entire alignment (like C) + i64 size_of_union = type_size_of(s, heap_allocator(), t) - s.word_size; + i64 align_of_union = type_align_of(s, heap_allocator(), t); + ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits); + } break; + case TypeRecord_RawUnion: { + // NOTE(bill): The zero size array is used to fix the alignment used in a structure as + // LLVM takes the first element's alignment as the entire alignment (like C) + i64 size_of_union = type_size_of(s, heap_allocator(), t); + i64 align_of_union = type_align_of(s, heap_allocator(), t); + ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union); + } break; + case TypeRecord_Enum: + ssa_print_type(f, m, t->Record.enum_base); + break; + } + } break; + + + case Type_Named: + if (is_type_struct(t) || is_type_union(t)) { + String *name = map_string_get(&m->type_names, hash_pointer(t)); + GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name)); + ssa_print_encoded_local(f, *name); + // ssa_print_encoded_local(f, t->Named.name); + } else { + ssa_print_type(f, m, base_type(t)); + } + break; + case Type_Tuple: + if (t->Tuple.variable_count == 1) { + ssa_print_type(f, m, t->Tuple.variables[0]->type); + } else { + ssa_fprintf(f, "{"); + for (isize i = 0; i < t->Tuple.variable_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, t->Tuple.variables[i]->type); + } + ssa_fprintf(f, "}"); + } + break; + case Type_Proc: { + if (t->Proc.result_count == 0) { + ssa_fprintf(f, "void"); + } else { + ssa_print_type(f, m, t->Proc.results); + } + ssa_fprintf(f, " ("); + TypeTuple *params = &t->Proc.params->Tuple; + for (isize i = 0; i < t->Proc.param_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, params->variables[i]->type); + } + ssa_fprintf(f, ")*"); + } break; + } +} + +void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type); + +void ssa_print_compound_element(ssaFileBuffer *f, ssaModule *m, ExactValue v, Type *elem_type) { + ssa_print_type(f, m, elem_type); + ssa_fprintf(f, " "); + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + Type *t = base_type(elem_type)->Maybe.elem; + ssa_fprintf(f, "{"); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + } + + if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) { + ssa_fprintf(f, "zeroinitializer"); + } else { + ssa_print_exact_value(f, m, v, elem_type); + } + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, " "); + ssa_fprintf(f, "true}"); + } +} + +void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) { + type = base_type(type); + if (is_type_float(type)) { + value = exact_value_to_float(value); + } else if (is_type_integer(type)) { + value = exact_value_to_integer(value); + } else if (is_type_pointer(type)) { + value = exact_value_to_integer(value); + } + + switch (value.kind) { + case ExactValue_Bool: + ssa_fprintf(f, "%s", (value.value_bool ? "true" : "false")); + break; + case ExactValue_String: { + String str = value.value_string; + if (str.len == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + if (!is_type_string(type)) { + GB_ASSERT(is_type_array(type)); + ssa_fprintf(f, "c\""); + ssa_print_escape_string(f, str, false); + ssa_fprintf(f, "\""); + } else { + // HACK NOTE(bill): This is a hack but it works because strings are created at the very end + // of the .ll file + ssaValue *str_array = ssa_add_global_string_array(m, str); + + ssa_fprintf(f, "{i8* getelementptr inbounds ("); + ssa_print_type(f, m, str_array->Global.entity->type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, str_array->Global.entity->type); + ssa_fprintf(f, "* "); + ssa_print_encoded_global(f, str_array->Global.entity->token.string, false); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, i32 0), "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld}", cast(i64)str.len); + } + } break; + case ExactValue_Integer: { + if (is_type_pointer(type)) { + if (value.value_integer == 0) { + ssa_fprintf(f, "null"); + } else { + ssa_fprintf(f, "inttoptr ("); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %llu to ", value.value_integer); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, ")"); + } + } else { + ssa_fprintf(f, "%lld", value.value_integer); + } + } break; + case ExactValue_Float: { + GB_ASSERT(is_type_float(type)); + type = base_type(type); + u64 u = *cast(u64*)&value.value_float; + switch (type->Basic.kind) { + case Basic_f32: + // IMPORTANT NOTE(bill): LLVM requires all floating point constants to be + // a 64 bit number if bits_of(float type) <= 64. + // https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M + // 64 bit mantissa: 52 bits + // 32 bit mantissa: 23 bits + // 29 == 52-23 + u >>= 29; + u <<= 29; + break; + } + + switch (type->Basic.kind) { + case 0: break; +#if 0 + case Basic_f16: + ssa_fprintf(f, "bitcast ("); + ssa_print_type(f, m, t_u16); + ssa_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float)); + ssa_print_type(f, m, t_f16); + ssa_fprintf(f, ")"); + break; + case Basic_f128: + ssa_fprintf(f, "bitcast ("); + ssa_fprintf(f, "i128"); + // TODO(bill): Actually support f128 + ssa_fprintf(f, " %llu to ", u); + ssa_print_type(f, m, t_f128); + ssa_fprintf(f, ")"); + break; +#endif + default: + ssa_fprintf(f, "0x%016llx", u); + break; + } + } break; + case ExactValue_Pointer: + if (value.value_pointer == 0) { + ssa_fprintf(f, "null"); + } else { + ssa_fprintf(f, "inttoptr ("); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, ")"); + } + break; + + case ExactValue_Compound: { + type = base_type(type); + if (is_type_array(type)) { + ast_node(cl, CompoundLit, value.value_compound); + isize elem_count = cl->elems.count; + if (elem_count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + ssa_fprintf(f, "["); + Type *elem_type = type->Array.elem; + + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ssa_print_compound_element(f, m, tav->value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, elem_type); + ssa_fprintf(f, " zeroinitializer"); + } + + ssa_fprintf(f, "]"); + } else if (is_type_vector(type)) { + ast_node(cl, CompoundLit, value.value_compound); + isize elem_count = cl->elems.count; + if (elem_count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + ssa_fprintf(f, "<"); + Type *elem_type = type->Vector.elem; + + if (elem_count == 1 && type->Vector.count > 1) { + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[0]); + GB_ASSERT(tav != NULL); + + for (isize i = 0; i < type->Vector.count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_compound_element(f, m, tav->value, elem_type); + } + } else { + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ssa_print_compound_element(f, m, tav->value, elem_type); + } + } + + ssa_fprintf(f, ">"); + } else if (is_type_struct(type)) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + ast_node(cl, CompoundLit, value.value_compound); + + if (cl->elems.count == 0) { + ssa_fprintf(f, "zeroinitializer"); + break; + } + + + isize value_count = type->Record.field_count; + ExactValue *values = gb_alloc_array(m->tmp_allocator, ExactValue, value_count); + + + if (cl->elems.e[0]->kind == AstNode_FieldValue) { + isize elem_count = cl->elems.count; + for (isize i = 0; i < elem_count; i++) { + ast_node(fv, FieldValue, cl->elems.e[i]); + String name = fv->field->Ident.string; + + TypeAndValue *tav = type_and_value_of_expression(m->info, fv->value); + GB_ASSERT(tav != NULL); + + Selection sel = lookup_field(m->allocator, type, name, false); + Entity *f = type->Record.fields[sel.index.e[0]]; + + values[f->Variable.field_index] = tav->value; + } + } else { + for (isize i = 0; i < value_count; i++) { + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + + Entity *f = type->Record.fields_in_src_order[i]; + + values[f->Variable.field_index] = tav->value; + } + } + + + + if (type->Record.struct_is_packed) { + ssa_fprintf(f, "<"); + } + ssa_fprintf(f, "{"); + + + for (isize i = 0; i < value_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + Type *elem_type = type->Record.fields[i]->type; + + ssa_print_compound_element(f, m, values[i], elem_type); + } + + + ssa_fprintf(f, "}"); + if (type->Record.struct_is_packed) { + ssa_fprintf(f, ">"); + } + + gb_temp_arena_memory_end(tmp); + } else { + ssa_fprintf(f, "zeroinitializer"); + } + + } break; + + default: + ssa_fprintf(f, "zeroinitializer"); + // GB_PANIC("Invalid ExactValue: %d", value.kind); + break; + } +} + +void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) { + if (b != NULL) { + ssa_print_escape_string(f, b->label, false); + ssa_fprintf(f, "-%td", b->index); + } else { + ssa_fprintf(f, ""); + } +} + +void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) { + if (value == NULL) { + ssa_fprintf(f, "!!!NULL_VALUE"); + return; + } + switch (value->kind) { + default: GB_PANIC("Unknown ssaValue kind"); break; + + case ssaValue_Constant: + ssa_print_exact_value(f, m, value->Constant.value, type_hint); + break; + + case ssaValue_ConstantSlice: { + ssaValueConstantSlice *cs = &value->ConstantSlice; + if (cs->backing_array == NULL || cs->count == 0) { + ssa_fprintf(f, "zeroinitializer"); + } else { + Type *at = base_type(type_deref(ssa_type(cs->backing_array))); + Type *et = at->Array.elem; + ssa_fprintf(f, "{"); + ssa_print_type(f, m, et); + ssa_fprintf(f, "* getelementptr inbounds ("); + ssa_print_type(f, m, at); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, at); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, cs->backing_array, at); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, i32 0), "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld, ", cs->count); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " %lld}", cs->count); + } + } break; + + case ssaValue_Nil: + ssa_fprintf(f, "zeroinitializer"); + break; + + case ssaValue_TypeName: + ssa_print_encoded_local(f, value->TypeName.name); + break; + case ssaValue_Global: { + Scope *scope = value->Global.entity->scope; + bool in_global_scope = false; + if (scope != NULL) { + in_global_scope = scope->is_global || scope->is_init; + } + ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope); + } break; + case ssaValue_Param: + ssa_print_encoded_local(f, value->Param.entity->token.string); + break; + case ssaValue_Proc: + ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_link_name)) != 0); + break; + case ssaValue_Instr: + ssa_fprintf(f, "%%%d", value->index); + break; + } +} + +void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) { + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + + ssa_fprintf(f, "\t"); + + switch (instr->kind) { + case ssaInstr_StartupRuntime: { + ssa_fprintf(f, "call void "); + ssa_print_encoded_global(f, str_lit(SSA_STARTUP_RUNTIME_PROC_NAME), false); + ssa_fprintf(f, "()\n"); + } break; + + case ssaInstr_Comment: + ssa_fprintf(f, "; %.*s\n", LIT(instr->Comment.text)); + break; + + case ssaInstr_Local: { + Type *type = instr->Local.entity->type; + ssa_fprintf(f, "%%%d = alloca ", value->index); + ssa_print_type(f, m, type); + ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case ssaInstr_ZeroInit: { + Type *type = type_deref(ssa_type(instr->ZeroInit.address)); + ssa_fprintf(f, "store "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " zeroinitializer, "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* %%%d\n", instr->ZeroInit.address->index); + } break; + + case ssaInstr_Store: { + Type *type = ssa_type(instr->Store.value); + ssa_fprintf(f, "store "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Store.value, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, instr->Store.address, type); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Load: { + Type *type = instr->Load.type; + ssa_fprintf(f, "%%%d = load ", value->index); + ssa_print_type(f, m, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, "* "); + ssa_print_value(f, m, instr->Load.address, type); + ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case ssaInstr_ArrayElementPtr: { + Type *et = ssa_type(instr->ArrayElementPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->ArrayElementPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + + ssaValue *index =instr->ArrayElementPtr.elem_index; + Type *t = ssa_type(index); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, index, t); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_StructElementPtr: { + Type *et = ssa_type(instr->StructElementPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->StructElementPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + ssa_print_type(f, m, t_i32); + ssa_fprintf(f, " %d", instr->StructElementPtr.elem_index); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_PtrOffset: { + Type *pt = ssa_type(instr->PtrOffset.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + ssa_print_type(f, m, type_deref(pt)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, pt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->PtrOffset.address, pt); + + ssaValue *offset = instr->PtrOffset.offset; + Type *t = ssa_type(offset); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, offset, t); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Phi: { + ssa_fprintf(f, "%%%d = phi ", value->index); + ssa_print_type(f, m, instr->Phi.type); + ssa_fprintf(f, " ", value->index); + + for (isize i = 0; i < instr->Phi.edges.count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + + ssaValue *edge = instr->Phi.edges.e[i]; + ssaBlock *block = NULL; + if (instr->parent != NULL && + i < instr->parent->preds.count) { + block = instr->parent->preds.e[i]; + } + + ssa_fprintf(f, "[ "); + ssa_print_value(f, m, edge, instr->Phi.type); + ssa_fprintf(f, ", %%"); + ssa_print_block_name(f, block); + ssa_fprintf(f, " ]"); + } + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_ArrayExtractValue: { + Type *et = ssa_type(instr->ArrayExtractValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->ArrayExtractValue.address, et); + ssa_fprintf(f, ", %d\n", instr->ArrayExtractValue.index); + } break; + + case ssaInstr_StructExtractValue: { + Type *et = ssa_type(instr->StructExtractValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->StructExtractValue.address, et); + ssa_fprintf(f, ", %d\n", instr->StructExtractValue.index); + } break; + + case ssaInstr_UnionTagPtr: { + Type *et = ssa_type(instr->UnionTagPtr.address); + ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ssa_print_type(f, m, type_deref(et)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->UnionTagPtr.address, et); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " 0, "); + ssa_print_type(f, m, t_i32); + ssa_fprintf(f, " %d", 2); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_UnionTagValue: { + Type *et = ssa_type(instr->UnionTagValue.address); + ssa_fprintf(f, "%%%d = extractvalue ", value->index); + + ssa_print_type(f, m, et); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->UnionTagValue.address, et); + ssa_fprintf(f, ", %d\n", 2); + } break; + + case ssaInstr_Jump: {; + ssa_fprintf(f, "br label %%"); + ssa_print_block_name(f, instr->Jump.block); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_If: {; + ssa_fprintf(f, "br "); + ssa_print_type(f, m, t_bool); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->If.cond, t_bool); + ssa_fprintf(f, ", ", instr->If.cond->index); + ssa_fprintf(f, "label %%"); ssa_print_block_name(f, instr->If.true_block); + ssa_fprintf(f, ", label %%"); ssa_print_block_name(f, instr->If.false_block); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_Return: { + ssaInstrReturn *ret = &instr->Return; + ssa_fprintf(f, "ret "); + if (ret->value == NULL) { + ssa_fprintf(f, "void"); + } else { + Type *t = ssa_type(ret->value); + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ret->value, t); + } + + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Conv: { + ssaInstrConv *c = &instr->Conv; + ssa_fprintf(f, "%%%d = %.*s ", value->index, LIT(ssa_conv_strings[c->kind])); + ssa_print_type(f, m, c->from); + ssa_fprintf(f, " "); + ssa_print_value(f, m, c->value, c->from); + ssa_fprintf(f, " to "); + ssa_print_type(f, m, c->to); + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Unreachable: { + ssa_fprintf(f, "unreachable\n"); + } break; + + case ssaInstr_BinaryOp: { + ssaInstrBinaryOp *bo = &value->Instr.BinaryOp; + Type *type = base_type(ssa_type(bo->left)); + Type *elem_type = type; + while (elem_type->kind == Type_Vector) { + elem_type = base_type(elem_type->Vector.elem); + } + + ssa_fprintf(f, "%%%d = ", value->index); + + if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_string(elem_type)) { + ssa_fprintf(f, "call "); + ssa_print_type(f, m, t_bool); + char *runtime_proc = ""; + switch (bo->op) { + case Token_CmpEq: runtime_proc = "__string_eq"; break; + case Token_NotEq: runtime_proc = "__string_ne"; break; + case Token_Lt: runtime_proc = "__string_lt"; break; + case Token_Gt: runtime_proc = "__string_gt"; break; + case Token_LtEq: runtime_proc = "__string_le"; break; + case Token_GtEq: runtime_proc = "__string_gt"; break; + } + + ssa_fprintf(f, " "); + ssa_print_encoded_global(f, make_string_c(runtime_proc), false); + ssa_fprintf(f, "("); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->left, type); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->right, type); + ssa_fprintf(f, ")\n"); + return; + + } else if (is_type_float(elem_type)) { + ssa_fprintf(f, "fcmp "); + switch (bo->op) { + case Token_CmpEq: ssa_fprintf(f, "oeq"); break; + case Token_NotEq: ssa_fprintf(f, "one"); break; + case Token_Lt: ssa_fprintf(f, "olt"); break; + case Token_Gt: ssa_fprintf(f, "ogt"); break; + case Token_LtEq: ssa_fprintf(f, "ole"); break; + case Token_GtEq: ssa_fprintf(f, "oge"); break; + } + } else { + ssa_fprintf(f, "icmp "); + if (bo->op != Token_CmpEq && + bo->op != Token_NotEq) { + if (is_type_unsigned(elem_type)) { + ssa_fprintf(f, "u"); + } else { + ssa_fprintf(f, "s"); + } + } + switch (bo->op) { + case Token_CmpEq: ssa_fprintf(f, "eq"); break; + case Token_NotEq: ssa_fprintf(f, "ne"); break; + case Token_Lt: ssa_fprintf(f, "lt"); break; + case Token_Gt: ssa_fprintf(f, "gt"); break; + case Token_LtEq: ssa_fprintf(f, "le"); break; + case Token_GtEq: ssa_fprintf(f, "ge"); break; + } + } + } else { + if (is_type_float(elem_type)) { + ssa_fprintf(f, "f"); + } + + switch (bo->op) { + case Token_Add: ssa_fprintf(f, "add"); break; + case Token_Sub: ssa_fprintf(f, "sub"); break; + case Token_And: ssa_fprintf(f, "and"); break; + case Token_Or: ssa_fprintf(f, "or"); break; + case Token_Xor: ssa_fprintf(f, "xor"); break; + case Token_Shl: ssa_fprintf(f, "shl"); break; + case Token_Shr: ssa_fprintf(f, "lshr"); break; + case Token_Mul: ssa_fprintf(f, "mul"); break; + case Token_Not: ssa_fprintf(f, "xor"); break; + + case Token_AndNot: GB_PANIC("Token_AndNot Should never be called"); + + default: { + if (!is_type_float(elem_type)) { + if (is_type_unsigned(elem_type)) ssa_fprintf(f, "u"); + else ssa_fprintf(f, "s"); + } + + switch (bo->op) { + case Token_Quo: ssa_fprintf(f, "div"); break; + case Token_Mod: ssa_fprintf(f, "rem"); break; + } + } break; + } + } + + ssa_fprintf(f, " "); + ssa_print_type(f, m, type); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bo->left, type); + ssa_fprintf(f, ", "); + ssa_print_value(f, m, bo->right, type); + ssa_fprintf(f, "\n"); + + } break; + + case ssaInstr_Call: { + ssaInstrCall *call = &instr->Call; + Type *result_type = call->type; + if (result_type) { + ssa_fprintf(f, "%%%d = ", value->index); + } + ssa_fprintf(f, "call "); + if (result_type) { + ssa_print_type(f, m, result_type); + } else { + ssa_fprintf(f, "void"); + } + ssa_fprintf(f, " "); + ssa_print_value(f, m, call->value, call->type); + + + ssa_fprintf(f, "("); + if (call->arg_count > 0) { + Type *proc_type = base_type(ssa_type(call->value)); + GB_ASSERT(proc_type->kind == Type_Proc); + TypeTuple *params = &proc_type->Proc.params->Tuple; + for (isize i = 0; i < call->arg_count; i++) { + Entity *e = params->variables[i]; + GB_ASSERT(e != NULL); + Type *t = e->type; + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, t); + ssa_fprintf(f, " "); + ssaValue *arg = call->args[i]; + ssa_print_value(f, m, arg, t); + } + } + ssa_fprintf(f, ")\n"); + + } break; + + case ssaInstr_Select: { + ssa_fprintf(f, "%%%d = select i1 ", value->index); + ssa_print_value(f, m, instr->Select.cond, t_bool); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, ssa_type(instr->Select.true_value)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Select.true_value, ssa_type(instr->Select.true_value)); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, ssa_type(instr->Select.false_value)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->Select.false_value, ssa_type(instr->Select.false_value)); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorExtractElement: { + Type *vt = ssa_type(instr->VectorExtractElement.vector); + Type *it = ssa_type(instr->VectorExtractElement.index); + ssa_fprintf(f, "%%%d = extractelement ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->VectorExtractElement.vector, vt); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, it); + ssa_fprintf(f, " "); + ssa_print_value(f, m, instr->VectorExtractElement.index, it); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorInsertElement: { + ssaInstrVectorInsertElement *ie = &instr->VectorInsertElement; + Type *vt = ssa_type(ie->vector); + ssa_fprintf(f, "%%%d = insertelement ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->vector, vt); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, ssa_type(ie->elem)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->elem, ssa_type(ie->elem)); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, ssa_type(ie->index)); + ssa_fprintf(f, " "); + ssa_print_value(f, m, ie->index, ssa_type(ie->index)); + + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_VectorShuffle: { + ssaInstrVectorShuffle *sv = &instr->VectorShuffle; + Type *vt = ssa_type(sv->vector); + ssa_fprintf(f, "%%%d = shufflevector ", value->index); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, sv->vector, vt); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, vt); + ssa_fprintf(f, " "); + ssa_print_value(f, m, sv->vector, vt); + ssa_fprintf(f, ", "); + + ssa_fprintf(f, "<%td x i32> <", sv->index_count); + for (isize i = 0; i < sv->index_count; i++) { + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_fprintf(f, "i32 %d", sv->indices[i]); + } + ssa_fprintf(f, ">"); + ssa_fprintf(f, "\n"); + } break; + + case ssaInstr_BoundsCheck: { + ssaInstrBoundsCheck *bc = &instr->BoundsCheck; + ssa_fprintf(f, "call void "); + ssa_print_encoded_global(f, str_lit("__bounds_check_error"), false); + ssa_fprintf(f, "("); + ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->index, t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->len, t_int); + + ssa_fprintf(f, ")\n"); + } break; + + case ssaInstr_SliceBoundsCheck: { + ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; + ssa_fprintf(f, "call void "); + if (bc->is_substring) { + ssa_print_encoded_global(f, str_lit("__substring_expr_error"), false); + } else { + ssa_print_encoded_global(f, str_lit("__slice_expr_error"), false); + } + + ssa_fprintf(f, "("); + ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->low, t_int); + ssa_fprintf(f, ", "); + + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->high, t_int); + + if (!bc->is_substring) { + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, " "); + ssa_print_value(f, m, bc->max, t_int); + } + + ssa_fprintf(f, ")\n"); + } break; + + + default: { + GB_PANIC(" %d\n", instr->kind); + ssa_fprintf(f, "; %d\n", instr->kind); + } break; + } +} + +void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) { + if (proc->body == NULL) { + ssa_fprintf(f, "declare "); + if (proc->tags & ProcTag_dll_import) { + ssa_fprintf(f, "dllimport "); + } + if (proc->tags & ProcTag_dll_export) { + ssa_fprintf(f, "dllexport "); + } + } else { + ssa_fprintf(f, "\ndefine "); + } + + if (proc->tags & ProcTag_stdcall) { + ssa_fprintf(f, "cc 64 "); + } else if (proc->tags & ProcTag_fastcall) { + ssa_fprintf(f, "cc 65 "); + } + + TypeProc *proc_type = &proc->type->Proc; + + if (proc_type->result_count == 0) { + ssa_fprintf(f, "void"); + } else { + ssa_print_type(f, m, proc_type->results); + } + + ssa_fprintf(f, " "); + ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_link_name)) != 0); + ssa_fprintf(f, "("); + + if (proc_type->param_count > 0) { + TypeTuple *params = &proc_type->params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + if (i > 0) { + ssa_fprintf(f, ", "); + } + ssa_print_type(f, m, e->type); + if (proc->body != NULL) { + ssa_fprintf(f, " %%%.*s", LIT(e->token.string)); + } + } + } + + ssa_fprintf(f, ") "); + + if (proc->tags & ProcTag_inline) { + ssa_fprintf(f, "alwaysinline "); + } + if (proc->tags & ProcTag_no_inline) { + ssa_fprintf(f, "noinline "); + } + + + if (proc->module->generate_debug_info && proc->entity != NULL) { + if (proc->body != NULL) { + ssaDebugInfo *di = *map_ssa_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); + GB_ASSERT(di->kind == ssaDebugInfo_Proc); + ssa_fprintf(f, "!dbg !%d ", di->id); + } + } + + + if (proc->body != NULL) { + // ssa_fprintf(f, "nounwind uwtable {\n"); + + ssa_fprintf(f, "{\n"); + for_array(i, proc->blocks) { + ssaBlock *block = proc->blocks.e[i]; + + if (i > 0) ssa_fprintf(f, "\n"); + ssa_print_block_name(f, block); + ssa_fprintf(f, ":\n"); + + for_array(j, block->instrs) { + ssaValue *value = block->instrs.e[j]; + ssa_print_instr(f, m, value); + } + } + ssa_fprintf(f, "}\n"); + } else { + ssa_fprintf(f, "\n"); + } + + for_array(i, proc->children) { + ssa_print_proc(f, m, proc->children.e[i]); + } +} + +void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) { + GB_ASSERT(v->kind == ssaValue_TypeName); + Type *bt = base_type(ssa_type(v)); + if (!is_type_struct(bt) && !is_type_union(bt)) { + return; + } + ssa_print_encoded_local(f, v->TypeName.name); + ssa_fprintf(f, " = type "); + ssa_print_type(f, m, base_type(v->TypeName.type)); + ssa_fprintf(f, "\n"); +} + +void ssa_print_llvm_ir(ssaGen *ssa) { + ssaModule *m = &ssa->module; + ssaFileBuffer buf = {0}, *f = &buf; + ssa_file_buffer_init(f, &ssa->output_file); + + if (m->layout.len > 0) { + ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); + } + + ssa_print_encoded_local(f, str_lit("..string")); + ssa_fprintf(f, " = type {i8*, "); + ssa_print_type(f, m, t_int); + ssa_fprintf(f, "} ; Basic_string\n"); + ssa_print_encoded_local(f, str_lit("..rawptr")); + ssa_fprintf(f, " = type i8* ; Basic_rawptr\n"); + + ssa_print_encoded_local(f, str_lit("..any")); + ssa_fprintf(f, " = type {"); + ssa_print_type(f, m, t_type_info_ptr); + ssa_fprintf(f, ", "); + ssa_print_type(f, m, t_rawptr); + ssa_fprintf(f, "} ; Basic_any\n"); + + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_TypeName) { + continue; + } + ssa_print_type_name(f, m, v); + } + + ssa_fprintf(f, "\n"); + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Proc) { + continue; + } + if (v->Proc.body == NULL) { + ssa_print_proc(f, m, &v->Proc); + } + } + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Proc) { + continue; + } + if (v->Proc.body != NULL) { + ssa_print_proc(f, m, &v->Proc); + } + } + + + for_array(member_index, m->members.entries) { + MapSsaValueEntry *entry = &m->members.entries.e[member_index]; + ssaValue *v = entry->value; + if (v->kind != ssaValue_Global) { + continue; + } + ssaValueGlobal *g = &v->Global; + Scope *scope = g->entity->scope; + bool in_global_scope = false; + if (scope != NULL) { + in_global_scope = scope->is_global || scope->is_init; + } + ssa_print_encoded_global(f, g->entity->token.string, in_global_scope); + ssa_fprintf(f, " = "); + if (g->is_thread_local) { + ssa_fprintf(f, "thread_local "); + } + + if (g->is_private) { + ssa_fprintf(f, "private "); + } + if (g->is_constant) { + if (g->is_unnamed_addr) { + ssa_fprintf(f, "unnamed_addr "); + } + ssa_fprintf(f, "constant "); + } else { + ssa_fprintf(f, "global "); + } + + + ssa_print_type(f, m, g->entity->type); + ssa_fprintf(f, " "); + if (g->value != NULL) { + ssa_print_value(f, m, g->value, g->entity->type); + } else { + ssa_fprintf(f, "zeroinitializer"); + } + ssa_fprintf(f, "\n"); + } + + +#if 0 + if (m->generate_debug_info) { + ssa_fprintf(f, "\n"); + ssa_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); + + for_array(di_index, m->debug_info.entries) { + MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[di_index]; + ssaDebugInfo *di = entry->value; + ssa_fprintf(f, "!%d = ", di->id); + + switch (di->kind) { + case ssaDebugInfo_CompileUnit: { + auto *cu = &di->CompileUnit; + ssaDebugInfo *file = *map_ssa_debug_info_get(&m->debug_info, hash_pointer(cu->file)); + ssa_fprintf(f, + "distinct !DICompileUnit(" + "language: DW_LANG_Go, " // Is this good enough? + "file: !%d, " + "producer: \"%.*s\", " + "flags: \"\", " + "runtimeVersion: 0, " + "isOptimized: false, " + "emissionKind: FullDebug" + ")", + file->id, LIT(cu->producer)); + + } break; + case ssaDebugInfo_File: + ssa_fprintf(f, "!DIFile(filename: \""); + ssa_print_escape_string(f, di->File.filename, false); + ssa_fprintf(f, "\", directory: \""); + ssa_print_escape_string(f, di->File.directory, false); + ssa_fprintf(f, "\")"); + break; + case ssaDebugInfo_Proc: + ssa_fprintf(f, "distinct !DISubprogram(" + "name: \"%.*s\", " + // "linkageName: \"\", " + "file: !%d, " + "line: %td, " + "isDefinition: true, " + "isLocal: false, " + "unit: !0" + ")", + LIT(di->Proc.name), + di->Proc.file->id, + di->Proc.pos.line); + break; + + case ssaDebugInfo_AllProcs: + ssa_fprintf(f, "!{"); + for_array(proc_index, di->AllProcs.procs) { + ssaDebugInfo *p = di->AllProcs.procs.e[proc_index]; + if (proc_index > 0) {ssa_fprintf(f, ",");} + ssa_fprintf(f, "!%d", p->id); + } + ssa_fprintf(f, "}"); + break; + } + + ssa_fprintf(f, "\n"); + } + } +#endif + ssa_file_buffer_destroy(f); +} diff --git a/src/string.c b/src/string.c new file mode 100644 index 000000000..b52b8886e --- /dev/null +++ b/src/string.c @@ -0,0 +1,422 @@ +gb_global gbArena string_buffer_arena = {0}; +gb_global gbAllocator string_buffer_allocator = {0}; + +void init_string_buffer_memory(void) { + // NOTE(bill): This should be enough memory for file systems + gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1)); + string_buffer_allocator = gb_arena_allocator(&string_buffer_arena); +} + + +// NOTE(bill): Used for UTF-8 strings +typedef struct String { + u8 * text; + isize len; +} String; +// NOTE(bill): used for printf style arguments +#define LIT(x) ((int)(x).len), (x).text + + +typedef struct String16 { + wchar_t *text; + isize len; +} String16; + + +gb_inline String make_string(u8 *text, isize len) { + String s; + s.text = text; + if (len < 0) { + len = gb_strlen(cast(char *)text); + } + s.len = len; + return s; +} + + +gb_inline String16 make_string16(wchar_t *text, isize len) { + String16 s; + s.text = text; + s.len = len; + return s; +} + + +gb_inline String make_string_c(char *text) { + return make_string(cast(u8 *)cast(void *)text, gb_strlen(text)); +} + +#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1) + + +gb_inline bool are_strings_equal(String a, String b) { + if (a.len == b.len) { + return gb_memcompare(a.text, b.text, a.len) == 0; + } + return false; +} + +gb_inline bool str_eq_ignore_case(String a, String b) { + if (a.len == b.len) { + for (isize i = 0; i < a.len; i++) { + char x = cast(char)a.text[i]; + char y = cast(char)b.text[i]; + if (gb_char_to_lower(x) != gb_char_to_lower(y)) + return false; + } + return true; + } + return false; +} + +int string_compare(String x, String y) { + if (x.len == y.len && + x.text == y.text) { + return 0; + } + + isize n = gb_min(x.len, y.len); + + isize fast = n/gb_size_of(isize) + 1; + isize offset = (fast-1)*gb_size_of(isize); + isize curr_block = 0; + if (n <= gb_size_of(isize)) { + fast = 0; + } + + isize *la = cast(isize *)x.text; + isize *lb = cast(isize *)y.text; + + for (; curr_block < fast; curr_block++) { + if (la[curr_block] ^ lb[curr_block]) { + for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) { + if (x.text[pos] ^ y.text[pos]) { + return cast(int)x.text[pos] - cast(int)y.text[pos]; + } + } + } + } + + for (; offset < n; offset++) { + if (x.text[offset] ^ y.text[offset]) { + return cast(int)x.text[offset] - cast(int)y.text[offset]; + } + } + + return 0; +} + +GB_COMPARE_PROC(string_cmp_proc) { + String x = *(String *)a; + String y = *(String *)b; + return string_compare(x, y); +} + + +// gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; } +// gb_inline bool operator !=(String a, String b) { return !operator==(a, b); } +// gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; } +// gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; } +// gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; } +// gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; } + +// template gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); } +// template gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); } +// template gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; } +// template gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; } + +gb_inline bool str_eq(String a, String b) { return are_strings_equal(a, b) != 0; } +gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); } +gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; } +gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; } +gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; } +gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; } + + + +gb_inline isize string_extension_position(String str) { + isize dot_pos = -1; + isize i = str.len; + bool seen_dot = false; + while (i --> 0) { + if (str.text[i] == GB_PATH_SEPARATOR) + break; + if (str.text[i] == '.') { + dot_pos = i; + break; + } + } + + return dot_pos; +} + +gb_inline bool string_has_extension(String str, String ext) { + if (str.len > ext.len+1) { + u8 *s = str.text+str.len - ext.len-1; + if (s[0] == '.') { + s++; + return gb_memcompare(s, ext.text, ext.len) == 0; + } + return false; + } + return false; +} + +bool string_contains_char(String s, u8 c) { + for (isize i = 0; i < s.len; i++) { + if (s.text[i] == c) + return true; + } + return false; +} + +// TODO(bill): Make this non-windows specific +String16 string_to_string16(gbAllocator a, String s) { + if (s.len < 1) { + return make_string16(NULL, 0); + } + + int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + cast(char *)s.text, s.len, NULL, 0); + if (len == 0) { + return make_string16(NULL, 0); + } + + wchar_t *text = gb_alloc_array(a, wchar_t, len+1); + + int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + cast(char *)s.text, s.len, text, len); + if (len1 == 0) { + gb_free(a, text); + return make_string16(NULL, 0); + } + text[len] = 0; + + return make_string16(text, len-1); +} + +String string16_to_string(gbAllocator a, String16 s) { + if (s.len < 1) { + return make_string(NULL, 0); + } + + int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + s.text, s.len, NULL, 0, + NULL, NULL); + if (len == 0) { + return make_string(NULL, 0); + } + + u8 *text = gb_alloc_array(a, u8, len+1); + + int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, + s.text, s.len, cast(char *)text, len, + NULL, NULL); + if (len1 == 0) { + gb_free(a, text); + return make_string(NULL, 0); + } + text[len] = 0; + + return make_string(text, len-1); +} + + + + + + + + + + + + + + + + + + +bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) { + if (s.text[0] == quote && + (quote == '$' || quote == '"')) { + return false; + } else if (s.text[0] >= 0x80) { + Rune r = -1; + isize size = gb_utf8_decode(s.text, s.len, &r); + *rune = r; + *multiple_bytes = true; + *tail_string = make_string(s.text+size, s.len-size); + return true; + } else if (s.text[0] != '\\') { + *rune = s.text[0]; + *tail_string = make_string(s.text+1, s.len-1); + return true; + } + + if (s.len <= 1) { + return false; + } + u8 c = s.text[1]; + s = make_string(s.text+2, s.len-2); + + switch (c) { + default: return false; + + case 'a': *rune = '\a'; break; + case 'b': *rune = '\b'; break; + case 'f': *rune = '\f'; break; + case 'n': *rune = '\n'; break; + case 'r': *rune = '\r'; break; + case 't': *rune = '\t'; break; + case 'v': *rune = '\v'; break; + case '\\': *rune = '\\'; break; + + + case '$': + case '"': + if (c != quote) { + return false; + } + *rune = c; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + i32 r = gb_digit_to_int(c); + if (s.len < 2) { + return false; + } + for (isize i = 0; i < 2; i++) { + i32 d = gb_digit_to_int(s.text[i]); + if (d < 0 || d > 7) { + return false; + } + r = (r<<3) | d; + } + s = make_string(s.text+2, s.len-2); + if (r > 0xff) { + return false; + } + *rune = r; + } break; + + case 'x': + case 'u': + case 'U': { + isize count = 0; + switch (c) { + case 'x': count = 2; break; + case 'u': count = 4; break; + case 'U': count = 8; break; + } + + Rune r = 0; + if (s.len < count) { + return false; + } + for (isize i = 0; i < count; i++) { + i32 d = gb_hex_digit_to_int(s.text[i]); + if (d < 0) { + return false; + } + r = (r<<4) | d; + } + s = make_string(s.text+count, s.len-count); + if (c == 'x') { + *rune = r; + break; + } + if (r > GB_RUNE_MAX) { + return false; + } + *rune = r; + *multiple_bytes = true; + } break; + } + *tail_string = s; + return true; +} + + +// 0 == failure +// 1 == original memory +// 2 == new allocation +i32 unquote_string(gbAllocator a, String *s_) { + GB_ASSERT(s_ != NULL); + String s = *s_; + isize n = s.len; + if (n < 2) + return 0; + u8 quote = s.text[0]; + if (quote != s.text[n-1]) + return 0; + s.text += 1; + s.len -= 2; + + if (quote == '`') { + if (string_contains_char(s, '`')) { + return 0; + } + *s_ = s; + return 1; + } + if (quote != '"' && quote != '$') + return 0; + + if (string_contains_char(s, '\n')) + return 0; + + if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) { + if (quote == '"') { + *s_ = s; + return 1; + } else if (quote == '$') { + Rune r = GB_RUNE_INVALID; + isize size = gb_utf8_decode(s.text, s.len, &r); + if ((size == s.len) && (r != -1 || size != 1)) { + *s_ = s; + return 1; + } + } + } + + + u8 rune_temp[4] = {0}; + isize buf_len = 3*s.len / 2; + u8 *buf = gb_alloc_array(a, u8, buf_len); + isize offset = 0; + while (s.len > 0) { + String tail_string = {0}; + Rune r = 0; + bool multiple_bytes = false; + bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string); + if (!success) { + gb_free(a, buf); + return 0; + } + s = tail_string; + + if (r < 0x80 || !multiple_bytes) { + buf[offset++] = cast(u8)r; + } else { + isize size = gb_utf8_encode_rune(rune_temp, r); + gb_memmove(buf+offset, rune_temp, size); + offset += size; + } + + if (quote == '$' && s.len != 0) { + gb_free(a, buf); + return 0; + } + } + *s_ = make_string(buf, offset); + return 2; +} diff --git a/src/timings.c b/src/timings.c new file mode 100644 index 000000000..a1eecc01a --- /dev/null +++ b/src/timings.c @@ -0,0 +1,105 @@ +typedef struct TimeStamp { + u64 start; + u64 finish; + String label; +} TimeStamp; + +typedef struct Timings { + TimeStamp total; + Array(TimeStamp) sections; + u64 freq; +} Timings; + + +u64 win32_time_stamp_time_now(void) { + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +u64 win32_time_stamp__freq(void) { + gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0}; + if (!win32_perf_count_freq.QuadPart) { + QueryPerformanceFrequency(&win32_perf_count_freq); + GB_ASSERT(win32_perf_count_freq.QuadPart != 0); + } + + return win32_perf_count_freq.QuadPart; +} + +u64 time_stamp_time_now(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp_time_now(); +#else +#error time_stamp_time_now +#endif +} + +u64 time_stamp__freq(void) { +#if defined(GB_SYSTEM_WINDOWS) + return win32_time_stamp__freq(); +#else +#error time_stamp__freq +#endif +} + +TimeStamp make_time_stamp(String label) { + TimeStamp ts = {0}; + ts.start = time_stamp_time_now(); + ts.label = label; + return ts; +} + +void timings_init(Timings *t, String label, isize buffer_size) { + array_init_reserve(&t->sections, heap_allocator(), buffer_size); + t->total = make_time_stamp(label); + t->freq = time_stamp__freq(); +} + +void timings_destroy(Timings *t) { + array_free(&t->sections); +} + +void timings__stop_current_section(Timings *t) { + if (t->sections.count > 0) { + t->sections.e[t->sections.count-1].finish = time_stamp_time_now(); + } +} + +void timings_start_section(Timings *t, String label) { + timings__stop_current_section(t); + array_add(&t->sections, make_time_stamp(label)); +} + +f64 time_stamp_as_ms(TimeStamp ts, u64 freq) { + GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label)); + return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq; +} + +void timings_print_all(Timings *t) { + timings__stop_current_section(t); + t->total.finish = time_stamp_time_now(); + + char const SPACES[] = " "; + + isize max_len = t->total.label.len; + for_array(i, t->sections) { + TimeStamp ts = t->sections.e[i]; + max_len = gb_max(max_len, ts.label.len); + } + + GB_ASSERT(max_len <= gb_size_of(SPACES)-1); + + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(t->total.label), + cast(int)(max_len-t->total.label.len), SPACES, + time_stamp_as_ms(t->total, t->freq)); + + for_array(i, t->sections) { + TimeStamp ts = t->sections.e[i]; + gb_printf("%.*s%.*s - %.3f ms\n", + LIT(ts.label), + cast(int)(max_len-ts.label.len), SPACES, + time_stamp_as_ms(ts, t->freq)); + } +} diff --git a/src/tokenizer.c b/src/tokenizer.c new file mode 100644 index 000000000..edf6e9721 --- /dev/null +++ b/src/tokenizer.c @@ -0,0 +1,816 @@ +#define TOKEN_KINDS \ + TOKEN_KIND(Token_Invalid, "Invalid"), \ + TOKEN_KIND(Token_EOF, "EOF"), \ + TOKEN_KIND(Token_Comment, "Comment"), \ +\ +TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \ + TOKEN_KIND(Token_Identifier, "Identifier"), \ + TOKEN_KIND(Token_Integer, "Integer"), \ + TOKEN_KIND(Token_Float, "Float"), \ + TOKEN_KIND(Token_Rune, "Rune"), \ + TOKEN_KIND(Token_String, "String"), \ +TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \ +\ +TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \ + TOKEN_KIND(Token_Eq, "="), \ + TOKEN_KIND(Token_Not, "!"), \ + TOKEN_KIND(Token_Hash, "#"), \ + TOKEN_KIND(Token_At, "@"), \ + TOKEN_KIND(Token_Pointer, "^"), \ + TOKEN_KIND(Token_Maybe, "?"), \ + TOKEN_KIND(Token_Add, "+"), \ + TOKEN_KIND(Token_Sub, "-"), \ + TOKEN_KIND(Token_Mul, "*"), \ + TOKEN_KIND(Token_Quo, "/"), \ + TOKEN_KIND(Token_Mod, "%"), \ + TOKEN_KIND(Token_And, "&"), \ + TOKEN_KIND(Token_Or, "|"), \ + TOKEN_KIND(Token_Xor, "~"), \ + TOKEN_KIND(Token_AndNot, "&~"), \ + TOKEN_KIND(Token_Shl, "<<"), \ + TOKEN_KIND(Token_Shr, ">>"), \ +\ + TOKEN_KIND(Token_as, "as"), \ + TOKEN_KIND(Token_transmute, "transmute"), \ + TOKEN_KIND(Token_down_cast, "down_cast"), \ + TOKEN_KIND(Token_union_cast, "union_cast"), \ +\ + TOKEN_KIND(Token_Prime, "'"), \ + TOKEN_KIND(Token_DoublePrime, "''"), \ +\ + TOKEN_KIND(Token_CmpAnd, "&&"), \ + TOKEN_KIND(Token_CmpOr, "||"), \ +\ +TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \ + TOKEN_KIND(Token_AddEq, "+="), \ + TOKEN_KIND(Token_SubEq, "-="), \ + TOKEN_KIND(Token_MulEq, "*="), \ + TOKEN_KIND(Token_QuoEq, "/="), \ + TOKEN_KIND(Token_ModEq, "%="), \ + TOKEN_KIND(Token_AndEq, "&="), \ + TOKEN_KIND(Token_OrEq, "|="), \ + TOKEN_KIND(Token_XorEq, "~="), \ + TOKEN_KIND(Token_AndNotEq, "&~="), \ + TOKEN_KIND(Token_ShlEq, "<<="), \ + TOKEN_KIND(Token_ShrEq, ">>="), \ + TOKEN_KIND(Token_CmpAndEq, "&&="), \ + TOKEN_KIND(Token_CmpOrEq, "||="), \ +TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \ + TOKEN_KIND(Token_Increment, "++"), \ + TOKEN_KIND(Token_Decrement, "--"), \ + TOKEN_KIND(Token_ArrowRight, "->"), \ + TOKEN_KIND(Token_ArrowLeft, "<-"), \ +\ +TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \ + TOKEN_KIND(Token_CmpEq, "=="), \ + TOKEN_KIND(Token_NotEq, "!="), \ + TOKEN_KIND(Token_Lt, "<"), \ + TOKEN_KIND(Token_Gt, ">"), \ + TOKEN_KIND(Token_LtEq, "<="), \ + TOKEN_KIND(Token_GtEq, ">="), \ +TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \ +\ + TOKEN_KIND(Token_OpenParen, "("), \ + TOKEN_KIND(Token_CloseParen, ")"), \ + TOKEN_KIND(Token_OpenBracket, "["), \ + TOKEN_KIND(Token_CloseBracket, "]"), \ + TOKEN_KIND(Token_OpenBrace, "{"), \ + TOKEN_KIND(Token_CloseBrace, "}"), \ + TOKEN_KIND(Token_Colon, ":"), \ + TOKEN_KIND(Token_Semicolon, ";"), \ + TOKEN_KIND(Token_Period, "."), \ + TOKEN_KIND(Token_Comma, ","), \ + TOKEN_KIND(Token_Ellipsis, ".."), \ + TOKEN_KIND(Token_RangeExclusive, "..<"), \ +TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \ +\ +TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ + TOKEN_KIND(Token_type, "type"), \ + TOKEN_KIND(Token_proc, "proc"), \ + TOKEN_KIND(Token_match, "match"), \ + TOKEN_KIND(Token_break, "break"), \ + TOKEN_KIND(Token_continue, "continue"), \ + TOKEN_KIND(Token_fallthrough, "fallthrough"), \ + TOKEN_KIND(Token_case, "case"), \ + TOKEN_KIND(Token_default, "default"), \ + TOKEN_KIND(Token_then, "then"), \ + TOKEN_KIND(Token_if, "if"), \ + TOKEN_KIND(Token_else, "else"), \ + TOKEN_KIND(Token_for, "for"), \ + TOKEN_KIND(Token_range, "range"), \ + TOKEN_KIND(Token_defer, "defer"), \ + TOKEN_KIND(Token_return, "return"), \ + TOKEN_KIND(Token_struct, "struct"), \ + TOKEN_KIND(Token_union, "union"), \ + TOKEN_KIND(Token_raw_union, "raw_union"), \ + TOKEN_KIND(Token_enum, "enum"), \ + TOKEN_KIND(Token_using, "using"), \ + TOKEN_KIND(Token_asm, "asm"), \ + TOKEN_KIND(Token_volatile, "volatile"), \ + TOKEN_KIND(Token_atomic, "atomic"), \ + TOKEN_KIND(Token_push_allocator, "push_allocator"), \ + TOKEN_KIND(Token_push_context, "push_context"), \ +TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \ + TOKEN_KIND(Token_Count, "") + +typedef enum TokenKind { +#define TOKEN_KIND(e, s) e + TOKEN_KINDS +#undef TOKEN_KIND +} TokenKind; + +String const token_strings[] = { +#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1} + TOKEN_KINDS +#undef TOKEN_KIND +}; + + +typedef struct TokenPos { + String file; + isize line; + isize column; +} TokenPos; + +i32 token_pos_cmp(TokenPos a, TokenPos b) { + if (a.line == b.line) { + if (a.column == b.column) { + isize min_len = gb_min(a.file.len, b.file.len); + return gb_memcompare(a.file.text, b.file.text, min_len); + } + return (a.column < b.column) ? -1 : +1; + } + + return (a.line < b.line) ? -1 : +1; +} + +bool token_pos_are_equal(TokenPos a, TokenPos b) { + return token_pos_cmp(a, b) == 0; +} + +// NOTE(bill): Text is UTF-8, thus why u8 and not char +typedef struct Token { + TokenKind kind; + String string; + TokenPos pos; +} Token; + +Token empty_token = {Token_Invalid}; +Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}}; + +Token make_token_ident(String s) { + Token t = {Token_Identifier, s}; + return t; +} + + +typedef struct ErrorCollector { + TokenPos prev; + i64 count; + i64 warning_count; + gbMutex mutex; +} ErrorCollector; + +gb_global ErrorCollector global_error_collector; + +void init_global_error_collector(void) { + gb_mutex_init(&global_error_collector.mutex); +} + + +void warning(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.warning_count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Warning: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + +void error(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + +void syntax_error(Token token, char *fmt, ...) { + gb_mutex_lock(&global_error_collector.mutex); + + global_error_collector.count++; + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { + va_list va; + + global_error_collector.prev = token.pos; + + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + } + + gb_mutex_unlock(&global_error_collector.mutex); +} + + +void compiler_error(char *fmt, ...) { + va_list va; + + va_start(va, fmt); + gb_printf_err("Internal Compiler Error: %s\n", + gb_bprintf_va(fmt, va)); + va_end(va); + gb_exit(1); +} + + + + + +gb_inline bool token_is_literal(Token t) { + return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1); +} +gb_inline bool token_is_operator(Token t) { + return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1); +} +gb_inline bool token_is_keyword(Token t) { + return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1); +} +gb_inline bool token_is_comparison(Token t) { + return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1); +} +gb_inline bool token_is_shift(Token t) { + return t.kind == Token_Shl || t.kind == Token_Shr; +} + +gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); } + + +typedef enum TokenizerInitError { + TokenizerInit_None, + + TokenizerInit_Invalid, + TokenizerInit_NotExists, + TokenizerInit_Permission, + TokenizerInit_Empty, + + TokenizerInit_Count, +} TokenizerInitError; + + +typedef struct Tokenizer { + String fullpath; + u8 *start; + u8 *end; + + Rune curr_rune; // current character + u8 * curr; // character pos + u8 * read_curr; // pos from start + u8 * line; // current line pos + isize line_count; + + isize error_count; + Array(String) allocated_strings; +} Tokenizer; + + +void tokenizer_err(Tokenizer *t, char *msg, ...) { + va_list va; + isize column = t->read_curr - t->line+1; + if (column < 1) + column = 1; + + gb_printf_err("%.*s(%td:%td) Syntax error: ", LIT(t->fullpath), t->line_count, column); + + va_start(va, msg); + gb_printf_err_va(msg, va); + va_end(va); + + gb_printf_err("\n"); + + t->error_count++; +} + +void advance_to_next_rune(Tokenizer *t) { + if (t->read_curr < t->end) { + Rune rune; + isize width = 1; + + t->curr = t->read_curr; + if (t->curr_rune == '\n') { + t->line = t->curr; + t->line_count++; + } + rune = *t->read_curr; + if (rune == 0) { + tokenizer_err(t, "Illegal character NUL"); + } else if (rune >= 0x80) { // not ASCII + width = gb_utf8_decode(t->read_curr, t->end-t->read_curr, &rune); + if (rune == GB_RUNE_INVALID && width == 1) + tokenizer_err(t, "Illegal UTF-8 encoding"); + else if (rune == GB_RUNE_BOM && t->curr-t->start > 0) + tokenizer_err(t, "Illegal byte order mark"); + } + t->read_curr += width; + t->curr_rune = rune; + } else { + t->curr = t->end; + if (t->curr_rune == '\n') { + t->line = t->curr; + t->line_count++; + } + t->curr_rune = GB_RUNE_EOF; + } +} + +TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { + TokenizerInitError err = TokenizerInit_None; + + char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); + memcpy(c_str, fullpath.text, fullpath.len); + c_str[fullpath.len] = '\0'; + + // TODO(bill): Memory map rather than copy contents + gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str); + gb_zero_item(t); + if (fc.data != NULL) { + t->start = cast(u8 *)fc.data; + t->line = t->read_curr = t->curr = t->start; + t->end = t->start + fc.size; + t->fullpath = fullpath; + t->line_count = 1; + + advance_to_next_rune(t); + if (t->curr_rune == GB_RUNE_BOM) { + advance_to_next_rune(t); // Ignore BOM at file beginning + } + + array_init(&t->allocated_strings, heap_allocator()); + } else { + gbFile f = {0}; + gbFileError file_err = gb_file_open(&f, c_str); + + switch (file_err) { + case gbFileError_Invalid: err = TokenizerInit_Invalid; break; + case gbFileError_NotExists: err = TokenizerInit_NotExists; break; + case gbFileError_Permission: err = TokenizerInit_Permission; break; + } + + if (err == TokenizerInit_None && gb_file_size(&f) == 0) { + err = TokenizerInit_Empty; + } + + gb_file_close(&f); + } + + gb_free(heap_allocator(), c_str); + return err; +} + +gb_inline void destroy_tokenizer(Tokenizer *t) { + if (t->start != NULL) { + gb_free(heap_allocator(), t->start); + } + for_array(i, t->allocated_strings) { + gb_free(heap_allocator(), t->allocated_strings.e[i].text); + } + array_free(&t->allocated_strings); +} + +void tokenizer_skip_whitespace(Tokenizer *t) { + while (rune_is_whitespace(t->curr_rune)) { + advance_to_next_rune(t); + } +} + +gb_inline i32 digit_value(Rune r) { + if (gb_char_is_digit(cast(char)r)) { + return r - '0'; + } else if (gb_is_between(cast(char)r, 'a', 'f')) { + return r - 'a' + 10; + } else if (gb_is_between(cast(char)r, 'A', 'F')) { + return r - 'A' + 10; + } + return 16; // NOTE(bill): Larger than highest possible +} + +gb_inline void scan_mantissa(Tokenizer *t, i32 base) { + // TODO(bill): Allow for underscores in numbers as a number separator + // TODO(bill): Is this a good idea? + // while (digit_value(t->curr_rune) < base || t->curr_rune == '_') + while (digit_value(t->curr_rune) < base) { + advance_to_next_rune(t); + } +} + + +Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { + Token token = {0}; + token.kind = Token_Integer; + token.string = make_string(t->curr, 1); + token.pos.file = t->fullpath; + token.pos.line = t->line_count; + token.pos.column = t->curr-t->line+1; + + if (seen_decimal_point) { + token.kind = Token_Float; + scan_mantissa(t, 10); + goto exponent; + } + + if (t->curr_rune == '0') { + u8 *prev = t->curr; + advance_to_next_rune(t); + if (t->curr_rune == 'b') { // Binary + advance_to_next_rune(t); + scan_mantissa(t, 2); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'o') { // Octal + advance_to_next_rune(t); + scan_mantissa(t, 8); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'd') { // Decimal + advance_to_next_rune(t); + scan_mantissa(t, 10); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else if (t->curr_rune == 'x') { // Hexadecimal + advance_to_next_rune(t); + scan_mantissa(t, 16); + if (t->curr - prev <= 2) + token.kind = Token_Invalid; + } else { + seen_decimal_point = false; + scan_mantissa(t, 10); + + if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') { + seen_decimal_point = true; + goto fraction; + } + } + + token.string.len = t->curr - token.string.text; + return token; + } + + scan_mantissa(t, 10); + +fraction: + if (t->curr_rune == '.') { + token.kind = Token_Float; + advance_to_next_rune(t); + scan_mantissa(t, 10); + } + +exponent: + if (t->curr_rune == 'e' || t->curr_rune == 'E') { + token.kind = Token_Float; + advance_to_next_rune(t); + if (t->curr_rune == '-' || t->curr_rune == '+') { + advance_to_next_rune(t); + } + scan_mantissa(t, 10); + } + + token.string.len = t->curr - token.string.text; + return token; +} + +// Quote == " for string +bool scan_escape(Tokenizer *t, Rune quote) { + isize len = 0; + u32 base = 0, max = 0, x = 0; + + Rune r = t->curr_rune; + if (r == 'a' || + r == 'b' || + r == 'f' || + r == 'n' || + r == 'r' || + r == 't' || + r == 'v' || + r == '\\' || + r == quote) { + advance_to_next_rune(t); + return true; + } else if (gb_is_between(r, '0', '7')) { + len = 3; base = 8; max = 255; + } else if (r == 'x') { + advance_to_next_rune(t); + len = 2; base = 16; max = 255; + } else if (r == 'u') { + advance_to_next_rune(t); + len = 4; base = 16; max = GB_RUNE_MAX; + } else if (r == 'U') { + advance_to_next_rune(t); + len = 8; base = 16; max = GB_RUNE_MAX; + } else { + if (t->curr_rune < 0) + tokenizer_err(t, "Escape sequence was not terminated"); + else + tokenizer_err(t, "Unknown escape sequence"); + return false; + } + + while (len --> 0) { + u32 d = cast(u32)digit_value(t->curr_rune); + if (d >= base) { + if (t->curr_rune < 0) + tokenizer_err(t, "Escape sequence was not terminated"); + else + tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune); + return false; + } + + x = x*base + d; + advance_to_next_rune(t); + } + + return true; +} + +gb_inline TokenKind token_kind_variant2(Tokenizer *t, TokenKind a, TokenKind b) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } + return a; +} + + +gb_inline TokenKind token_kind_variant3(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } + if (t->curr_rune == ch_c) { + advance_to_next_rune(t); + return c; + } + return a; +} + +gb_inline TokenKind token_kind_variant4(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c, Rune ch_d, TokenKind d) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return b; + } else if (t->curr_rune == ch_c) { + advance_to_next_rune(t); + return c; + } else if (t->curr_rune == ch_d) { + advance_to_next_rune(t); + return d; + } + return a; +} + + +gb_inline TokenKind token_kind_dub_eq(Tokenizer *t, Rune sing_rune, TokenKind sing, TokenKind sing_eq, TokenKind dub, TokenKind dub_eq) { + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return sing_eq; + } else if (t->curr_rune == sing_rune) { + advance_to_next_rune(t); + if (t->curr_rune == '=') { + advance_to_next_rune(t); + return dub_eq; + } + return dub; + } + return sing; +} + +Token tokenizer_get_token(Tokenizer *t) { + Token token = {0}; + Rune curr_rune; + + tokenizer_skip_whitespace(t); + token.string = make_string(t->curr, 1); + token.pos.file = t->fullpath; + token.pos.line = t->line_count; + token.pos.column = t->curr - t->line + 1; + + curr_rune = t->curr_rune; + if (rune_is_letter(curr_rune)) { + token.kind = Token_Identifier; + while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) { + advance_to_next_rune(t); + } + + token.string.len = t->curr - token.string.text; + + // NOTE(bill): All keywords are > 1 + if (token.string.len > 1) { + if (str_eq(token.string, token_strings[Token_as])) { + token.kind = Token_as; + } else if (str_eq(token.string, token_strings[Token_transmute])) { + token.kind = Token_transmute; + } else if (str_eq(token.string, token_strings[Token_down_cast])) { + token.kind = Token_down_cast; + } else if (str_eq(token.string, token_strings[Token_union_cast])) { + token.kind = Token_union_cast; + } else { + for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) { + if (str_eq(token.string, token_strings[k])) { + token.kind = cast(TokenKind)k; + break; + } + } + } + } + + } else if (gb_is_between(curr_rune, '0', '9')) { + token = scan_number_to_token(t, false); + } else { + advance_to_next_rune(t); + switch (curr_rune) { + case GB_RUNE_EOF: + token.kind = Token_EOF; + break; + + case '\'': + token.kind = Token_Prime; + if (t->curr_rune == '\'') { + advance_to_next_rune(t); + token.kind = Token_DoublePrime; + } + break; + + case '`': // Raw String Literal + case '"': // String Literal + { + Rune quote = curr_rune; + token.kind = Token_String; + if (curr_rune == '"') { + for (;;) { + Rune r = t->curr_rune; + if (r == '\n' || r < 0) { + tokenizer_err(t, "String literal not terminated"); + break; + } + advance_to_next_rune(t); + if (r == quote) + break; + if (r == '\\') + scan_escape(t, '"'); + } + } else { + for (;;) { + Rune r = t->curr_rune; + if (r < 0) { + tokenizer_err(t, "String literal not terminated"); + break; + } + advance_to_next_rune(t); + if (r == quote) + break; + } + } + token.string.len = t->curr - token.string.text; + i32 success = unquote_string(heap_allocator(), &token.string); + if (success > 0) { + if (success == 2) { + array_add(&t->allocated_strings, token.string); + } + return token; + } else { + tokenizer_err(t, "Invalid string literal"); + } + } break; + + case '.': + token.kind = Token_Period; // Default + if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number + token = scan_number_to_token(t, true); + } else if (t->curr_rune == '.') { // Could be an ellipsis + advance_to_next_rune(t); + token.kind = Token_Ellipsis; + if (t->curr_rune == '<') { + advance_to_next_rune(t); + token.kind = Token_RangeExclusive; + } + } + break; + + case '#': token.kind = Token_Hash; break; + case '@': token.kind = Token_At; break; + case '^': token.kind = Token_Pointer; break; + case '?': token.kind = Token_Maybe; break; + case ';': token.kind = Token_Semicolon; break; + case ',': token.kind = Token_Comma; break; + case '(': token.kind = Token_OpenParen; break; + case ')': token.kind = Token_CloseParen; break; + case '[': token.kind = Token_OpenBracket; break; + case ']': token.kind = Token_CloseBracket; break; + case '{': token.kind = Token_OpenBrace; break; + case '}': token.kind = Token_CloseBrace; break; + case ':': token.kind = Token_Colon; break; + + case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break; + case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break; + case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break; + case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break; + case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break; + case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Increment); break; + case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break; + case '/': { + if (t->curr_rune == '/') { + while (t->curr_rune != '\n') { + advance_to_next_rune(t); + } + token.kind = Token_Comment; + } else if (t->curr_rune == '*') { + isize comment_scope = 1; + advance_to_next_rune(t); + while (comment_scope > 0) { + if (t->curr_rune == '/') { + advance_to_next_rune(t); + if (t->curr_rune == '*') { + advance_to_next_rune(t); + comment_scope++; + } + } else if (t->curr_rune == '*') { + advance_to_next_rune(t); + if (t->curr_rune == '/') { + advance_to_next_rune(t); + comment_scope--; + } + } else { + advance_to_next_rune(t); + } + } + token.kind = Token_Comment; + } else { + token.kind = token_kind_variant2(t, Token_Quo, Token_QuoEq); + } + } break; + + case '<': + if (t->curr_rune == '-') { + token.kind = Token_ArrowLeft; + } else { + token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq); + } + break; + case '>': + token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); + break; + + case '&': + token.kind = Token_And; + if (t->curr_rune == '~') { + token.kind = Token_AndNot; + advance_to_next_rune(t); + if (t->curr_rune == '=') { + token.kind = Token_AndNotEq; + advance_to_next_rune(t); + } + } else { + token.kind = token_kind_dub_eq(t, '&', Token_And, Token_AndEq, Token_CmpAnd, Token_CmpAndEq); + } + break; + + case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break; + + default: + if (curr_rune != GB_RUNE_BOM) { + u8 str[4] = {0}; + int len = cast(int)gb_utf8_encode_rune(str, curr_rune); + tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune); + } + token.kind = Token_Invalid; + break; + } + } + + token.string.len = t->curr - token.string.text; + return token; +} diff --git a/src/unicode.c b/src/unicode.c new file mode 100644 index 000000000..5c9f91f46 --- /dev/null +++ b/src/unicode.c @@ -0,0 +1,66 @@ +#pragma warning(push) +#pragma warning(disable: 4245) + +// #include "utf8proc/utf8proc.h" +#include "utf8proc/utf8proc.c" + +#pragma warning(pop) + +bool rune_is_letter(Rune r) { + if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) || + r == '_') { + return true; + } + switch (utf8proc_category(r)) { + case UTF8PROC_CATEGORY_LU: + case UTF8PROC_CATEGORY_LL: + case UTF8PROC_CATEGORY_LT: + case UTF8PROC_CATEGORY_LM: + case UTF8PROC_CATEGORY_LO: + return true; + } + return false; +} + +bool rune_is_digit(Rune r) { + if (r < 0x80 && gb_is_between(r, '0', '9')) { + return true; + } + return utf8proc_category(r) == UTF8PROC_CATEGORY_ND; +} + +bool rune_is_whitespace(Rune r) { + switch (r) { + case ' ': + case '\t': + case '\n': + case '\r': + return true; + } + return false; +} + + +bool is_string_an_identifier(String s) { + if (s.len < 1) { + return false; + } + isize offset = 0; + while (offset < s.len) { + bool ok = false; + Rune r = -1; + isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r); + if (offset == 0) { + ok = rune_is_letter(r); + } else { + ok = rune_is_letter(r) || rune_is_digit(r); + } + + if (!ok) { + return false; + } + offset += size; + } + + return offset == s.len; +}