From e2734a2dc676d236757801973c6786d3d3e6bbee Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 5 Mar 2017 21:22:33 +0000 Subject: [PATCH] Begin work on the custom backend --- build.bat | 10 +- code/demo.odin | 14 +- core/_preload.odin | 37 +- core/mem.odin | 41 +- src/ir.c | 4 +- src/main.c | 8 +- src/ssa.c | 914 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 972 insertions(+), 56 deletions(-) create mode 100644 src/ssa.c diff --git a/build.bat b/build.bat index 4223b855f..890d87ecd 100644 --- a/build.bat +++ b/build.bat @@ -45,15 +45,9 @@ del *.ilk > NUL 2> NUL cl %compiler_settings% "src\main.c" ^ /link %linker_settings% -OUT:%exe_name% ^ && odin run code/demo.odin - rem && odin build code/Jaze/src/main.odin - rem && odin build_dll code/example.odin ^ - rem odin run code/demo.odin + rem && odin run code/Jaze/src/main.odin -rem pushd src\asm -rem nasm hellope.asm -fwin64 -o hellope.obj ^ -rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^ -rem && hellope.exe -rem popd +del *.obj > NUL 2> NUL :end_of_build diff --git a/code/demo.odin b/code/demo.odin index 2d03e2ec7..905ae53d8 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -9,19 +9,15 @@ #import "sync.odin"; main :: proc() { - // buf: [64]byte; - // // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64); - // x := 624.123; - // s := strconv.format_float(buf[..], x, 'f', 6, 64); - // fmt.println(s); - // fmt.printf("%3d\n", 102); + x := 2; + y := 3; + z := x+y; + fmt.println(z); +when false { s := new_slice(int, 0, 10); append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2); fmt.println(s); - - -when false { /* Version 0.1.1 diff --git a/core/_preload.odin b/core/_preload.odin index 953d38c43..3b3e60e0f 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -128,9 +128,6 @@ __debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap"; __trap :: proc() #foreign __llvm_core "llvm.trap"; read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter"; -__cpuid :: proc(level: u32, sig: ^u32) -> i32 #foreign __llvm_core "__get_cpuid"; - - @@ -350,6 +347,40 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline { } +__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr { + llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64"; + llvm_memset_64bit(data, cast(byte)value, len, 1, false); + return data; +} +__mem_zero :: proc(data: rawptr, len: int) -> rawptr { + return __mem_set(data, 0, len); +} +__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr { + // NOTE(bill): This _must_ be implemented like C's memmove + llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64"; + llvm_memmove_64bit(dst, src, len, 1, false); + return dst; +} +__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr { + // NOTE(bill): This _must_ be implemented like C's memcpy + llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64"; + llvm_memcpy_64bit(dst, src, len, 1, false); + return dst; +} + +__mem_compare :: proc(a, b: ^byte, n: int) -> int { + for i in 0..n { + match { + case (a+i)^ < (b+i)^: + return -1; + case (a+i)^ > (b+i)^: + return +1; + } + } + return 0; +} + + Raw_Any :: struct #ordered { type_info: ^Type_Info, data: rawptr, diff --git a/core/mem.odin b/core/mem.odin index b5e119921..1560ca410 100644 --- a/core/mem.odin +++ b/core/mem.odin @@ -6,41 +6,20 @@ swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32"; swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64"; -set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" { - llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64"; - llvm_memset_64bit(data, cast(byte)value, len, 1, false); - return data; +set :: proc(data: rawptr, value: i32, len: int) -> rawptr { + return __mem_set(data, value, len); } - -zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" { - return set(data, 0, len); +zero :: proc(data: rawptr, len: int) -> rawptr { + return __mem_zero(data, len); } - -copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" { - // NOTE(bill): This _must_ be implemented like C's memmove - llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64"; - llvm_memmove_64bit(dst, src, len, 1, false); - return dst; +copy :: proc(dst, src: rawptr, len: int) -> rawptr { + return __mem_copy(dst, src, len); } - -copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" { - // NOTE(bill): This _must_ be implemented like C's memcpy - llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64"; - llvm_memcpy_64bit(dst, src, len, 1, false); - return dst; +copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr { + return __mem_copy_non_overlapping(dst, src, len); } - -compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" { - n := min(a.count, b.count); - for i in 0..n { - match { - case a[i] < b[i]: - return -1; - case a[i] > b[i]: - return +1; - } - } - return 0; +compare :: proc(a, b: []byte) -> int { + return __mem_compare(a.data, b.data, min(a.count, b.count)); } diff --git a/src/ir.c b/src/ir.c index be138dd97..9678cfeef 100644 --- a/src/ir.c +++ b/src/ir.c @@ -4868,8 +4868,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { } } } else { // Tuple(s) - Array(irAddr) lvals; - irValueArray inits; + Array(irAddr) lvals = {0}; + irValueArray inits = {0}; array_init_reserve(&lvals, m->tmp_allocator, vd->names.count); array_init_reserve(&inits, m->tmp_allocator, vd->names.count); diff --git a/src/main.c b/src/main.c index 6ccd47cf5..5cb6e6e7c 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,8 @@ extern "C" { #endif +#define USE_CUSTOM_BACKEND false + #include "common.c" #include "timings.c" @@ -9,7 +11,7 @@ extern "C" { #include "tokenizer.c" #include "parser.c" #include "checker.c" -// #include "bytecode.c" +#include "ssa.c" #include "ir.c" #include "ir_opt.c" #include "ir_print.c" @@ -208,7 +210,7 @@ int main(int argc, char **argv) { #endif -#if 0 +#if USE_CUSTOM_BACKEND if (global_error_collector.count != 0) { return 1; } @@ -217,7 +219,7 @@ int main(int argc, char **argv) { return 1; } - if (!bc_generate(&checker.info)) { + if (!ssa_generate(&parser, &checker.info)) { return 1; } #else diff --git a/src/ssa.c b/src/ssa.c new file mode 100644 index 000000000..70723f098 --- /dev/null +++ b/src/ssa.c @@ -0,0 +1,914 @@ +typedef enum ssaOp ssaOp; +typedef struct ssaModule ssaModule; +typedef struct ssaValue ssaValue; +typedef struct ssaBlock ssaBlock; +typedef struct ssaProc ssaProc; +typedef struct ssaEdge ssaEdge; +typedef struct ssaRegister ssaRegister; +typedef enum ssaBlockKind ssaBlockKind; +typedef enum ssaBranchPredicition ssaBranchPredicition; + +String ssa_mangle_name(ssaModule *m, String path, Entity *e); + +#define MAP_TYPE ssaValue * +#define MAP_PROC map_ssa_value_ +#define MAP_NAME MapSsaValue +#include "map.c" + +typedef Array(ssaValue *) ssaValueArray; + +enum ssaOp { + ssaOp_Invalid, + + ssaOp_Unknown, + + ssaOp_SP, // Stack Pointer + ssaOp_SB, // Stack Base + ssaOp_Addr, // Address of something - special rules for certain types when loading and storing (e.g. Maps) + + ssaOp_Local, + ssaOp_Global, + ssaOp_Proc, + + ssaOp_Load, + ssaOp_Store, + ssaOp_Move, + ssaOp_Zero, // Zero initialize + + ssaOp_ArrayIndex, // Index for a fixed array + ssaOp_PtrIndex, // Index for a struct/tuple/etc + ssaOp_OffsetPtr, + + ssaOp_Phi, + ssaOp_Copy, + + // TODO(bill): calling conventions + ssaOp_CallOdin, + ssaOp_CallC, + ssaOp_CallStd, + ssaOp_CallFast, + + ssaOp_BoundsCheck, + ssaOp_SliceBoundsCheck, + + // Built in operations/procedures + ssaOp_Bswap16, + ssaOp_Bswap32, + ssaOp_Bswap64, + + ssaOp_Assume, + ssaOp_DebugTrap, + ssaOp_Trap, + ssaOp_ReadCycleCounter, + + + ssaOp_ConstBool, + ssaOp_ConstString, + ssaOp_ConstSlice, + ssaOp_ConstNil, + ssaOp_Const8, + ssaOp_Const16, + ssaOp_Const32, + ssaOp_Const64, + ssaOp_Const32F, + ssaOp_Const64F, + + // These should be all the operations I could possibly need for the mean time + ssaOp_Add8, + ssaOp_Add16, + ssaOp_Add32, + ssaOp_Add64, + ssaOp_AddPtr, + ssaOp_Add32F, + ssaOp_Add64F, + ssaOp_Sub8, + ssaOp_Sub16, + ssaOp_Sub32, + ssaOp_Sub64, + ssaOp_SubPtr, + ssaOp_Sub32F, + ssaOp_Sub64F, + ssaOp_Mul8, + ssaOp_Mul16, + ssaOp_Mul32, + ssaOp_Mul64, + ssaOp_Mul32F, + ssaOp_Mul64F, + ssaOp_Div8, + ssaOp_Div8U, + ssaOp_Div16, + ssaOp_Div16U, + ssaOp_Div32, + ssaOp_Div32U, + ssaOp_Div64, + ssaOp_Div64U, + ssaOp_Div32F, + ssaOp_Div64F, + ssaOp_Mod8, + ssaOp_Mod8U, + ssaOp_Mod16, + ssaOp_Mod16U, + ssaOp_Mod32, + ssaOp_Mod32U, + ssaOp_Mod64, + ssaOp_Mod64U, + + ssaOp_And8, + ssaOp_And16, + ssaOp_And32, + ssaOp_And64, + ssaOp_Or8, + ssaOp_Or16, + ssaOp_Or32, + ssaOp_Or64, + ssaOp_Xor8, + ssaOp_Xor16, + ssaOp_Xor32, + ssaOp_Xor64, + + ssaOp_Lsh8x8, + ssaOp_Lsh8x16, + ssaOp_Lsh8x32, + ssaOp_Lsh8x64, + ssaOp_Lsh16x8, + ssaOp_Lsh16x16, + ssaOp_Lsh16x32, + ssaOp_Lsh16x64, + ssaOp_Lsh32x8, + ssaOp_Lsh32x16, + ssaOp_Lsh32x32, + ssaOp_Lsh32x64, + ssaOp_Lsh64x8, + ssaOp_Lsh64x16, + ssaOp_Lsh64x32, + ssaOp_Lsh64x64, + ssaOp_Rsh8x8, + ssaOp_Rsh8x16, + ssaOp_Rsh8x32, + ssaOp_Rsh8x64, + ssaOp_Rsh16x8, + ssaOp_Rsh16x16, + ssaOp_Rsh16x32, + ssaOp_Rsh16x64, + ssaOp_Rsh32x8, + ssaOp_Rsh32x16, + ssaOp_Rsh32x32, + ssaOp_Rsh32x64, + ssaOp_Rsh64x8, + ssaOp_Rsh64x16, + ssaOp_Rsh64x32, + ssaOp_Rsh64x64, + ssaOp_Rsh8Ux8, + ssaOp_Rsh8Ux16, + ssaOp_Rsh8Ux32, + ssaOp_Rsh8Ux64, + ssaOp_Rsh16Ux8, + ssaOp_Rsh16Ux16, + ssaOp_Rsh16Ux32, + ssaOp_Rsh16Ux64, + ssaOp_Rsh32Ux8, + ssaOp_Rsh32Ux16, + ssaOp_Rsh32Ux32, + ssaOp_Rsh32Ux64, + ssaOp_Rsh64Ux8, + ssaOp_Rsh64Ux16, + ssaOp_Rsh64Ux32, + ssaOp_Rsh64Ux64, + + ssaOp_Eq8, + ssaOp_Eq16, + ssaOp_Eq32, + ssaOp_Eq64, + ssaOp_EqPtr, + ssaOp_Eq32F, + ssaOp_Eq64F, + ssaOp_Ne8, + ssaOp_Ne16, + ssaOp_Ne32, + ssaOp_Ne64, + ssaOp_NePtr, + ssaOp_Ne32F, + ssaOp_Ne64F, + ssaOp_Lt8, + ssaOp_Lt16, + ssaOp_Lt32, + ssaOp_Lt64, + ssaOp_LtPtr, + ssaOp_Lt32F, + ssaOp_Lt64F, + ssaOp_Gt8, + ssaOp_Gt16, + ssaOp_Gt32, + ssaOp_Gt64, + ssaOp_GtPtr, + ssaOp_Gt32F, + ssaOp_Gt64F, + ssaOp_Le8, + ssaOp_Le16, + ssaOp_Le32, + ssaOp_Le64, + ssaOp_LePtr, + ssaOp_Le32F, + ssaOp_Le64F, + ssaOp_Ge8, + ssaOp_Ge16, + ssaOp_Ge32, + ssaOp_Ge64, + ssaOp_GePtr, + ssaOp_Ge32F, + ssaOp_Ge64F, + + ssaOp_NotB, + ssaOp_EqB, + ssaOp_NeB, + + ssaOp_Neg8, + ssaOp_Neg16, + ssaOp_Neg32, + ssaOp_Neg64, + ssaOp_Neg32F, + ssaOp_Neg64F, + + ssaOp_Not8, + ssaOp_Not16, + ssaOp_Not32, + ssaOp_Not64, + + ssaOp_SignExt8to16, + ssaOp_SignExt8to32, + ssaOp_SignExt8to64, + ssaOp_SignExt16to32, + ssaOp_SignExt16to64, + ssaOp_SignExt32to64, + ssaOp_ZeroExt8to16, + ssaOp_ZeroExt8to32, + ssaOp_ZeroExt8to64, + ssaOp_ZeroExt16to32, + ssaOp_ZeroExt16to64, + ssaOp_ZeroExt32to64, + ssaOp_Trunc16to8, + ssaOp_Trunc32to8, + ssaOp_Trunc32to16, + ssaOp_Trunc64to8, + ssaOp_Trunc64to16, + ssaOp_Trunc64to32, + + ssaOp_Cvt32to32F, + ssaOp_Cvt32to64F, + ssaOp_Cvt64to32F, + ssaOp_Cvt64to64F, + ssaOp_Cvt32Fto32, + ssaOp_Cvt32Fto64, + ssaOp_Cvt64Fto32, + ssaOp_Cvt64Fto64, + ssaOp_Cvt32Fto64F, + ssaOp_Cvt64Fto32F, + ssaOp_Cvt32Uto32F, + ssaOp_Cvt32Uto64F, + ssaOp_Cvt32Fto32U, + ssaOp_Cvt64Fto32U, + ssaOp_Cvt64Uto32F, + ssaOp_Cvt64Uto64F, + ssaOp_Cvt32Fto64U, + ssaOp_Cvt64Fto64U, + + ssaOp_Count, +}; + +#define SSA_MAX_ARGS 4 + +struct ssaValue { + i32 id; // Unique identifier but the pointer could be used too + ssaOp op; // Operation that computes this value + Type * type; + ssaBlock * block; // Containing basic block + + i32 uses; + // Most values will only a few number of arguments + // Procedure calls may need a lot more so they will use the `var_args` parameter instead + ssaValue * args[SSA_MAX_ARGS]; + isize arg_count; + + ssaValueArray var_args; // Only used in procedure calls as the SSA_MAX_ARGS may be too small + + ExactValue exact_value; // Used for constants +}; + +enum ssaBlockKind { + ssaBlock_Invalid, + + // NOTE(bill): These are the generic block types and for more specific + // architectures, these could become conditions blocks like amd64 LT or EQ + ssaBlock_Entry, // Entry point + ssaBlock_Plain, + ssaBlock_If, + ssaBlock_Ret, + ssaBlock_RetJmp, // Stores return value and jumps to Ret block + ssaBlock_Exit, + + ssaBlock_Count, +}; + +enum ssaBranchPredicition { + ssaBranch_Unknown = 0, + ssaBranch_Likely = +1, + ssaBranch_Unlikely = -1, +}; + +// ssaEdge represents a control flow graph (CFG) edge +struct ssaEdge { + // Succs array: Block To + // Preds array: Block From + ssaBlock *block; + // Index of reverse edge + isize index; +}; + +typedef Array(ssaEdge) ssaEdgeArray; + +struct ssaBlock { + i32 id; // Unique identifier but the pointer could be used too + ssaBlockKind kind; + ssaProc * proc; // Containing procedure + + // Likely branch direction + ssaBranchPredicition likeliness; + + ssaValueArray values; + ssaEdgeArray preds; + ssaEdgeArray succs; +}; + +struct ssaProc { + ssaModule * module; // Parent module + String name; // Mangled name + Entity * entity; + DeclInfo * decl_info; + + Array(ssaBlock *) blocks; + ssaBlock * entry; // Entry block + ssaBlock * curr_block; + + i32 block_id; + i32 value_id; + MapSsaValue values; // Key: Entity * +}; + +struct ssaRegister { + i32 id; + i32 size; +}; + +struct ssaModule { + CheckerInfo * info; + gbAllocator allocator; + gbArena arena; + gbAllocator tmp_allocator; + gbArena tmp_arena; + + MapEntity min_dep_map; // Key: Entity * + MapSsaValue values; // Key: Entity * + // List of registers for the specific architecture + Array(ssaRegister) registers; + + ssaProc *proc; // current procedure + + Entity *entry_point_entity; + + u32 stmt_state_flags; + + Array(ssaProc *) procs; + ssaValueArray procs_to_generate; +}; + + + + + +ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind) { + ssaBlock *b = gb_alloc_item(p->module->allocator, ssaBlock); + b->id = p->block_id++; + b->kind = kind; + b->proc = p; + + array_init(&b->values, heap_allocator()); + array_init(&b->preds, heap_allocator()); + array_init(&b->succs, heap_allocator()); + array_add(&p->blocks, b); + return b; +} + +void ssa_clear_block(ssaProc *p, ssaBlock *b) { + GB_ASSERT(b->proc != NULL); + array_clear(&b->values); + array_clear(&b->preds); + array_clear(&b->succs); + b->proc = NULL; + b->kind = ssaBlock_Plain; +} + + +void ssa_start_block(ssaProc *p, ssaBlock *b) { + GB_ASSERT(p->curr_block == NULL); + p->curr_block = b; +} + +ssaBlock *ssa_end_block(ssaProc *p) { + ssaBlock *b = p->curr_block; + if (b == NULL) { + return NULL; + } + p->curr_block = NULL; + return b; +} + +void ssa_add_to_edge(ssaBlock *b, ssaBlock *c) { + isize i = b->succs.count; + isize j = b->preds.count; + ssaEdge s = {c, j}; + ssaEdge p = {b, i}; + array_add(&b->succs, s); + array_add(&b->preds, p); +} + + +ssaValue *ssa_new_value(ssaProc *p, ssaOp op, Type *t, ssaBlock *b) { + ssaValue *v = gb_alloc_item(p->module->allocator, ssaValue); + v->id = p->value_id++; + v->op = op; + v->type = t; + v->block = b; + array_add(&b->values, v); + return v; +} + +ssaValue *ssa_new_value0(ssaBlock *b, ssaOp op, Type *t) { + ssaValue *v = ssa_new_value(b->proc, op, t, b); + return v; +} +ssaValue *ssa_new_value0v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value) { + ssaValue *v = ssa_new_value0(b, op, t); + v->exact_value = exact_value; + return v; +} + +ssaValue *ssa_new_value1(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg) { + ssaValue *v = ssa_new_value(b->proc, op, t, b); + v->args[v->arg_count++] = arg; arg->uses++; + return v; +} +ssaValue *ssa_new_value1v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg) { + ssaValue *v = ssa_new_value1(b, op, t, arg); + v->exact_value = exact_value; + return v; +} + +ssaValue *ssa_new_value2(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1) { + ssaValue *v = ssa_new_value(b->proc, op, t, b); + v->args[v->arg_count++] = arg0; arg0->uses++; + v->args[v->arg_count++] = arg1; arg1->uses++; + return v; +} +ssaValue *ssa_new_value2v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1) { + ssaValue *v = ssa_new_value2(b, op, t, arg0, arg1); + v->exact_value = exact_value; + return v; +} + +ssaValue *ssa_new_value3(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { + ssaValue *v = ssa_new_value(b->proc, op, t, b); + v->args[v->arg_count++] = arg0; arg0->uses++; + v->args[v->arg_count++] = arg1; arg1->uses++; + v->args[v->arg_count++] = arg2; arg2->uses++; + return v; +} +ssaValue *ssa_new_value3v(ssaBlock *b, ssaOp op, Type *t, ExactValue exact_value, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2) { + ssaValue *v = ssa_new_value3(b, op, t, arg0, arg1, arg2); + v->exact_value = exact_value; + return v; +} + +ssaValue *ssa_new_value4(ssaBlock *b, ssaOp op, Type *t, ssaValue *arg0, ssaValue *arg1, ssaValue *arg2, ssaValue *arg3) { + ssaValue *v = ssa_new_value(b->proc, op, t, b); + v->args[v->arg_count++] = arg0; arg0->uses++; + v->args[v->arg_count++] = arg1; arg1->uses++; + v->args[v->arg_count++] = arg2; arg2->uses++; + v->args[v->arg_count++] = arg3; arg3->uses++; + return v; +} + +ssaValue *ssa_const_val(ssaProc *p, ssaOp op, Type *t, ExactValue exact_value) { + return ssa_new_value0v(p->curr_block, op, t, exact_value); +} + +ssaValue *ssa_const_bool (ssaProc *p, Type *t, bool c) { return ssa_const_val(p, ssaOp_ConstBool, t, exact_value_bool(c)); } +ssaValue *ssa_const_i8 (ssaProc *p, Type *t, i8 c) { return ssa_const_val(p, ssaOp_Const8, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i16 (ssaProc *p, Type *t, i16 c) { return ssa_const_val(p, ssaOp_Const16, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i32 (ssaProc *p, Type *t, i32 c) { return ssa_const_val(p, ssaOp_Const32, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_i64 (ssaProc *p, Type *t, i64 c) { return ssa_const_val(p, ssaOp_Const64, t, exact_value_integer(cast(i64)c)); } +ssaValue *ssa_const_f32 (ssaProc *p, Type *t, f32 c) { return ssa_const_val(p, ssaOp_Const32F, t, exact_value_float(c)); } +ssaValue *ssa_const_f64 (ssaProc *p, Type *t, f64 c) { return ssa_const_val(p, ssaOp_Const64F, t, exact_value_float(c)); } +ssaValue *ssa_const_string (ssaProc *p, Type *t, String c) { return ssa_const_val(p, ssaOp_ConstString, t, exact_value_string(c)); } +ssaValue *ssa_const_empty_string(ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstString, t, (ExactValue){0}); } +ssaValue *ssa_const_slice (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstSlice, t, (ExactValue){0}); } +ssaValue *ssa_const_nil (ssaProc *p, Type *t) { return ssa_const_val(p, ssaOp_ConstNil, t, (ExactValue){0}); } + +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; +} + + + + +ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_info) { + ssaProc *p = gb_alloc_item(m->allocator, ssaProc); + p->module = m; + p->name = name; + p->entity = entity; + p->decl_info = decl_info; + + array_init(&p->blocks, heap_allocator()); + map_ssa_value_init(&p->values, heap_allocator()); + + return p; +} + +ssaValue *ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) { + Type *t = make_type_pointer(p->module->allocator, e->type); + ssaValue *local = ssa_new_value0(p->entry, ssaOp_Local, t); + map_ssa_value_set(&p->values, hash_pointer(e), local); + map_ssa_value_set(&p->module->values, hash_pointer(e), local); + + ssaValue *addr = ssa_new_value1(p->curr_block, ssaOp_Addr, local->type, local); + ssa_new_value1(p->curr_block, ssaOp_Zero, t, addr); + return addr; +} +ssaValue *ssa_add_local_for_ident(ssaProc *p, AstNode *name) { + Entity **found = map_entity_get(&p->module->info->definitions, hash_pointer(name)); + if (found) { + Entity *e = *found; + return ssa_add_local(p, e, name); + } + return NULL; +} + +ssaValue *ssa_add_local_generated(ssaProc *p, Type *t) { + GB_ASSERT(t != NULL); + + Scope *scope = NULL; + if (p->curr_block) { + // scope = p->curr_block->scope; + } + Entity *e = make_entity_variable(p->module->allocator, scope, empty_token, t, false); + return ssa_add_local(p, e, NULL); +} + + + + + + + +void ssa_build_stmt(ssaProc *p, AstNode *node); +void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes); + + +ssaValue *ssa_build_addr(ssaProc *p, AstNode *node) { + return NULL; +} + +ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) { + expr = unparen_expr(expr); + + TypeAndValue *tv = map_tav_get(&p->module->info->types, hash_pointer(expr)); + GB_ASSERT_NOT_NULL(tv); + + if (tv->value.kind != ExactValue_Invalid) { + return NULL; + // return llir_add_module_constant(p->module, tv->type, tv->value); + } + + 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(&p->module->info->uses, hash_pointer(expr)); + if (e->kind == Entity_Builtin) { + Token token = ast_node_token(expr); + GB_PANIC("TODO(bill): ssa_build_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) { + GB_PANIC("TODO(bill): nil"); + return NULL; + } + + ssaValue **found = map_ssa_value_get(&p->module->values, hash_pointer(e)); + if (found) { + ssaValue *v = *found; + if (v->op == ssaOp_Proc) { + return v; + } + return v; + } + case_end; + } + + + return NULL; +} + + + +void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes) { + for_array(i, nodes) { + ssa_build_stmt(p, nodes.e[i]); + } +} + +void ssa_addr_store(ssaProc *p, ssaValue *addr, ssaValue *value) { + +} + + +ssaValue *ssa_emit_struct_ep(ssaProc *p, ssaValue *ptr, i32 index) { + GB_ASSERT(ptr->type != NULL); + GB_ASSERT(is_type_pointer(ptr->type)); + return NULL; +} + + +void ssa_build_stmt(ssaProc *p, AstNode *node) { + if (p->curr_block == NULL) { + ssaBlock *dead_block = ssa_new_block(p, ssaBlock_Plain); + ssa_start_block(p, dead_block); + } + + switch (node->kind) { + case_ast_node(es, EmptyStmt, node); + case_end; + + case_ast_node(bs, BlockStmt, node); + ssa_build_stmt_list(p, bs->stmts); + case_end; + + case_ast_node(vd, ValueDecl, node); + if (vd->is_var) { + ssaModule *m = p->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + if (vd->values.count == 0) { + for_array(i, vd->names) { + AstNode *name = vd->names.e[i]; + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_ident(p, name); + } + } + } else { + ssaValueArray lvals = {0}; + ssaValueArray inits = {0}; + 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]; + ssaValue *lval = NULL; + if (!ssa_is_blank_ident(name)) { + lval = ssa_add_local_for_ident(p, name); + } + + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + ssaValue *init = ssa_build_expr(p, vd->values.e[i]); + if (init == NULL || init->type == NULL) { + // TODO(bill): remove this + continue; + } + Type *t = base_type(init->type); + if (init->op == ssaOp_Addr && 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_ep(p, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + + for_array(i, inits) { + if (lvals.e[i] == NULL) { + continue; + } + ssa_addr_store(p, lvals.e[i], inits.e[i]); + } + } + + gb_temp_arena_memory_end(tmp); + } + case_end; + } +} + + +void ssa_build_proc(ssaModule *m, ssaProc *p) { + p->module = m; + m->proc = p; + + if (p->decl_info->proc_lit == NULL || + p->decl_info->proc_lit->kind != AstNode_ProcLit) { + return; + } + + ast_node(pl, ProcLit, p->decl_info->proc_lit); + if (pl->body == NULL) { + return; + } + p->entry = ssa_new_block(p, ssaBlock_Entry); + p->curr_block = ssa_new_block(p, ssaBlock_Plain); + + ssa_build_stmt(p, pl->body); +} + + +bool ssa_generate(Parser *parser, CheckerInfo *info) { + if (global_error_collector.count != 0) { + return false; + } + + ssaModule m = {0}; + { // Init ssaModule + m.info = info; + + isize token_count = parser->total_token_count; + isize arena_size = 4 * token_count * gb_max3(gb_size_of(ssaValue), gb_size_of(ssaBlock), gb_size_of(ssaProc)); + + gb_arena_init_from_allocator(&m.arena, heap_allocator(), arena_size); + gb_arena_init_from_allocator(&m.tmp_arena, heap_allocator(), arena_size); + m.tmp_allocator = gb_arena_allocator(&m.tmp_arena); + m.allocator = gb_arena_allocator(&m.arena); + + map_ssa_value_init(&m.values, heap_allocator()); + array_init(&m.registers, heap_allocator()); + array_init(&m.procs, heap_allocator()); + array_init(&m.procs_to_generate, heap_allocator()); + } + + isize global_variable_max_count = 0; + Entity *entry_point = NULL; + bool has_dll_main = false; + bool has_win_main = false; + + 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 && !e->scope->is_global) { + if (e->scope->is_init && str_eq(name, str_lit("main"))) { + entry_point = e; + } + if ((e->Procedure.tags & ProcTag_export) != 0 || + (e->Procedure.link_name.len > 0) || + (e->scope->is_file && e->Procedure.link_name.len > 0)) { + if (!has_dll_main && str_eq(name, str_lit("DllMain"))) { + has_dll_main = true; + } else if (!has_win_main && str_eq(name, str_lit("WinMain"))) { + has_win_main = true; + } + } + } + } + + + m.entry_point_entity = entry_point; + 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) { + if (e->kind == Entity_Procedure && (e->Procedure.tags & ProcTag_export) != 0) { + } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) { + // Handle later + } else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) { + } else { + name = ssa_mangle_name(&m, e->token.pos.file, e); + } + } + + + switch (e->kind) { + case Entity_TypeName: + break; + + case Entity_Variable: { + + } break; + + case Entity_Procedure: { + ast_node(pd, ProcLit, decl->proc_lit); + String original_name = name; + AstNode *body = pd->body; + if (e->Procedure.is_foreign) { + name = e->token.string; // NOTE(bill): Don't use the mangled name + } + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } else if (pd->link_name.len > 0) { + name = pd->link_name; + } + + if (e == entry_point) { + gb_printf("%.*s\n", LIT(name)); + + ssaProc *p = ssa_new_proc(&m, name, e, decl); + ssa_build_proc(&m, p); + } + + // ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + // p->Proc.tags = pd->tags; + + // ssa_module_add_value(m, 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; + } + } + + return true; +} + + + + + + +String ssa_mangle_name(ssaModule *m, String path, Entity *e) { + // NOTE(bill): prefix names not in the init scope + // TODO(bill): make robust and not just rely on the file's name + String name = e->token.string; + 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; + bool is_overloaded = check_is_entity_overloaded(e); + if (is_overloaded) { + max_len += 21; + } + + 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)); + if (is_overloaded) { + char *str = cast(char *)new_name + new_name_len-1; + isize len = max_len-new_name_len; + isize extra = gb_snprintf(str, len, "-%tu", cast(usize)cast(uintptr)e); + new_name_len += extra-1; + } + + return make_string(new_name, new_name_len-1); +}