diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-01-04 11:24:32 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-01-04 11:24:32 +0000 |
| commit | 915b5cdab7ae87ce38a6119f5c7e73aa0faaefa3 (patch) | |
| tree | b9548165db061f29208bc28f794d429781307a6f | |
| parent | c8f99b360f4c2c0f566c8e3b4ed9ab9a388687c3 (diff) | |
Rename llir -> ir
| -rw-r--r-- | build.bat | 8 | ||||
| -rw-r--r-- | code/demo.odin | 3 | ||||
| -rw-r--r-- | core/fmt.odin | 18 | ||||
| -rw-r--r-- | src/checker/checker.c | 10 | ||||
| -rw-r--r-- | src/checker/decl.c | 16 | ||||
| -rw-r--r-- | src/checker/expr.c | 14 | ||||
| -rw-r--r-- | src/checker/stmt.c | 13 | ||||
| -rw-r--r-- | src/ir.c | 5794 | ||||
| -rw-r--r-- | src/ir_opt.c (renamed from src/llir_opt.c) | 230 | ||||
| -rw-r--r-- | src/ir_print.c | 1515 | ||||
| -rw-r--r-- | src/llir.c | 5794 | ||||
| -rw-r--r-- | src/llir_print.c | 1515 | ||||
| -rw-r--r-- | src/main.c | 31 | ||||
| -rw-r--r-- | src/parser.c | 31 |
14 files changed, 7473 insertions, 7519 deletions
@@ -4,7 +4,7 @@ set exe_name=odin.exe :: Debug = 0, Release = 1 -set release_mode=1 +set release_mode=0 set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR- @@ -49,5 +49,11 @@ cl %compiler_settings% "src\main.c" ^ rem && odin build_dll code/example.odin ^ rem && odin run code/demo.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 + :end_of_build diff --git a/code/demo.odin b/code/demo.odin index 3680c8957..f1d3398f8 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,7 +1,5 @@ #import "fmt.odin"; - - main :: proc() { { Byte_Size :: enum f64 { @@ -58,3 +56,4 @@ main :: proc() { } } } + diff --git a/core/fmt.odin b/core/fmt.odin index 68da7e413..1e965fa0a 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -88,11 +88,7 @@ bprint_nl :: proc (buf: ^Buffer) { bprint_rune(buf, '\n'); } __NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"; bprint_bool :: proc(buffer: ^Buffer, b: bool) { - if b { - bprint_string(buffer, "true"); - } else { - bprint_string(buffer, "false"); - } + bprint_string(buffer, if b { give "true" } else { give "false" }); } bprint_pointer :: proc(buffer: ^Buffer, p: rawptr) #inline { @@ -100,9 +96,9 @@ bprint_pointer :: proc(buffer: ^Buffer, p: rawptr) #inline { bprint_u64(buffer, p as uint as u64); } -// bprint_f16 :: proc (buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 4); } -bprint_f32 :: proc (buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 7); } -bprint_f64 :: proc (buffer: ^Buffer, f: f64) #inline { print__f64(buffer, f as f64, 16); } +// bprint_f16 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 4); } +bprint_f32 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 7); } +bprint_f64 :: proc(buffer: ^Buffer, f: f64) #inline { print__f64(buffer, f as f64, 16); } bprint_u64 :: proc(buffer: ^Buffer, value: u64) { i := value; buf :[20]byte; @@ -189,11 +185,7 @@ bprint_type :: proc(buf: ^Buffer, ti: ^Type_Info) { case ti == type_info(int): bprint_string(buf, "int"); case ti == type_info(uint): bprint_string(buf, "uint"); default: - if info.signed { - bprint_string(buf, "i"); - } else { - bprint_string(buf, "u"); - } + bprint_string(buf, if info.signed { give "i" } else { give "u"}); bprint_u64(buf, 8*info.size as u64); } diff --git a/src/checker/checker.c b/src/checker/checker.c index e25d6263c..3a19ec69c 100644 --- a/src/checker/checker.c +++ b/src/checker/checker.c @@ -52,7 +52,7 @@ typedef struct DeclInfo { AstNode *type_expr; AstNode *init_expr; - AstNode *proc_decl; // AstNode_ProcDecl + AstNode *proc_lit; // AstNode_ProcLit u32 var_decl_tags; MapBool deps; // Key: Entity * @@ -306,9 +306,9 @@ bool decl_info_has_init(DeclInfo *d) { if (d->init_expr != NULL) { return true; } - if (d->proc_decl != NULL) { - switch (d->proc_decl->kind) { - case_ast_node(pd, ProcLit, d->proc_decl); + if (d->proc_lit != NULL) { + switch (d->proc_lit->kind) { + case_ast_node(pd, ProcLit, d->proc_lit); if (pd->body != NULL) { return true; } @@ -1219,7 +1219,7 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As d->init_expr = init; } else if (init != NULL && up_init->kind == AstNode_ProcLit) { e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags); - d->proc_decl = init; + d->proc_lit = init; } else { e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0}); d->type_expr = vd->type; diff --git a/src/checker/decl.c b/src/checker/decl.c index 312ae172c..36e6c440c 100644 --- a/src/checker/decl.c +++ b/src/checker/decl.c @@ -329,17 +329,17 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) { return true; } -void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { +void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) { GB_ASSERT(e->type == NULL); - if (d->proc_decl->kind != AstNode_ProcLit) { + if (d->proc_lit->kind != AstNode_ProcLit) { // TOOD(bill): Better error message - error_node(d->proc_decl, "Expected a procedure to check"); + error_node(d->proc_lit, "Expected a procedure to check"); return; } Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin); e->type = proc_type; - ast_node(pd, ProcLit, d->proc_decl); + ast_node(pd, ProcLit, d->proc_lit); check_open_scope(c, pd->type); check_procedure_type(c, proc_type, pd->type); @@ -380,7 +380,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { } if (proc_type->Proc.calling_convention != ProcCC_Odin) { - error_node(d->proc_decl, "An internal procedure may only have the Odin calling convention"); + error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention"); proc_type->Proc.calling_convention = ProcCC_Odin; } @@ -408,7 +408,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { Type *this_type = base_type(e->type); Type *other_type = base_type(f->type); if (!are_signatures_similar_enough(this_type, other_type)) { - error_node(d->proc_decl, + error_node(d->proc_lit, "Redeclaration of #foreign procedure `%.*s` with different type signatures\n" "\tat %.*s(%td:%td)", LIT(name), LIT(pos.file), pos.line, pos.column); @@ -433,7 +433,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { Entity *f = *found; TokenPos pos = f->token.pos; // TODO(bill): Better error message? - error_node(d->proc_decl, + error_node(d->proc_lit, "Non unique linking name for procedure `%.*s`\n" "\tother at %.*s(%td:%td)", LIT(name), LIT(pos.file), pos.line, pos.column); @@ -520,7 +520,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { check_type_decl(c, e, d->type_expr, named_type); break; case Entity_Procedure: - check_proc_decl(c, e, d); + check_proc_lit(c, e, d); break; } diff --git a/src/checker/expr.c b/src/checker/expr.c index 9c134507f..cf27f0ff4 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -112,7 +112,7 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie d->init_expr = init; } else if (init != NULL && up_init->kind == AstNode_ProcLit) { e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags); - d->proc_decl = init; + d->proc_lit = init; } else { e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0}); d->type_expr = vd->type; @@ -130,16 +130,6 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie check_arity_match(c, vd); } case_end; - - case_ast_node(gd, GenericDecl, node); - for_array(iota, gd->specs) { - AstNode *spec = gd->specs.e[iota]; - switch (spec->kind) { - case_ast_node(bd, BadDecl, spec); - case_end; - } - } - case_end; #if 0 case_ast_node(pd, ProcDecl, node); if (!ast_node_expect(pd->name, AstNode_Ident)) { @@ -150,7 +140,7 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie e->identifier = pd->name; DeclInfo *d = make_declaration_info(c->allocator, e->scope); - d->proc_decl = node; + d->proc_lit = node; add_entity_and_decl_info(c, pd->name, e, d); check_entity_decl(c, e, d, NULL, NULL); diff --git a/src/checker/stmt.c b/src/checker/stmt.c index 0d719b8ad..62764eeef 100644 --- a/src/checker/stmt.c +++ b/src/checker/stmt.c @@ -1295,18 +1295,5 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { // NOTE(bill): Handled elsewhere } case_end; - - case_ast_node(gd, GenericDecl, node); - for_array(spec_index, gd->specs) { - AstNode *spec = gd->specs.e[spec_index]; - switch (spec->kind) { - case_ast_node(bd, BadDecl, spec); - case_end; - default: - error(ast_node_token(spec), "Invalid specification in declaration: `%.*s`", LIT(ast_node_strings[spec->kind])); - break; - } - } - case_end; } } diff --git a/src/ir.c b/src/ir.c new file mode 100644 index 000000000..88c0f6b71 --- /dev/null +++ b/src/ir.c @@ -0,0 +1,5794 @@ +typedef struct irProcedure irProcedure; +typedef struct irBlock irBlock; +typedef struct irValue irValue; +typedef struct irDebugInfo irDebugInfo; + +typedef Array(irValue *) irValueArray; + +#define MAP_TYPE irValue * +#define MAP_PROC map_ir_value_ +#define MAP_NAME MapIrValue +#include "map.c" + +#define MAP_TYPE irDebugInfo * +#define MAP_PROC map_ir_debug_info_ +#define MAP_NAME MapIrDebugInfo +#include "map.c" + +typedef struct irModule { + CheckerInfo * info; + BuildContext *build_context; + 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 * + MapIrValue values; // Key: Entity * + MapIrValue members; // Key: String + MapString type_names; // Key: Type * + MapIrDebugInfo debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice + + Entity * entry_point_entity; + + Array(irProcedure *) procs; // NOTE(bill): All procedures with bodies + irValueArray procs_to_generate; // NOTE(bill): Procedures to generate +} irModule; + +// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) +typedef struct irDomNode { + irBlock * idom; // Parent (Immediate Dominator) + Array(irBlock *) children; + i32 pre, post; // Ordering in tree +} irDomNode; + + +typedef struct irBlock { + i32 index; + String label; + irProcedure *parent; + AstNode * node; // Can be NULL + Scope * scope; + isize scope_index; + irDomNode dom; + i32 gaps; + + irValueArray instrs; + irValueArray locals; + + Array(irBlock *) preds; + Array(irBlock *) succs; +} irBlock; + +typedef struct irTargetList irTargetList; +struct irTargetList { + irTargetList *prev; + irBlock * break_; + irBlock * continue_; + irBlock * fallthrough_; +}; + +typedef enum irDeferExitKind { + irDeferExit_Default, + irDeferExit_Return, + irDeferExit_Branch, +} irDeferExitKind; +typedef enum irDeferKind { + irDefer_Node, + irDefer_Instr, +} irDeferKind; + +typedef struct irDefer { + irDeferKind kind; + isize scope_index; + irBlock * block; + union { + AstNode *stmt; + // NOTE(bill): `instr` will be copied every time to create a new one + irValue *instr; + }; +} irDefer; + +struct irProcedure { + irProcedure * parent; + Array(irProcedure *) children; + + Entity * entity; + irModule * module; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; + + irValueArray params; + Array(irDefer) defer_stmts; + Array(irBlock *) blocks; + i32 scope_index; + irBlock * decl_block; + irBlock * entry_block; + irBlock * curr_block; + irTargetList * target_list; + irValueArray referrers; + + i32 local_count; + i32 instr_count; + i32 block_count; +}; + +#define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" +#define IR_TYPE_INFO_DATA_NAME "__$type_info_data" +#define IR_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" + + +#define IR_INSTR_KINDS \ + IR_INSTR_KIND(Comment, struct { String text; }) \ + IR_INSTR_KIND(Local, struct { \ + Entity * entity; \ + Type * type; \ + bool zero_initialized; \ + irValueArray referrers; \ + }) \ + IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \ + IR_INSTR_KIND(Store, struct { irValue *address, *value; }) \ + IR_INSTR_KIND(Load, struct { Type *type; irValue *address; }) \ + IR_INSTR_KIND(PtrOffset, struct { \ + irValue *address; \ + irValue *offset; \ + }) \ + IR_INSTR_KIND(ArrayElementPtr, struct { \ + irValue *address; \ + Type * result_type; \ + irValue *elem_index; \ + }) \ + IR_INSTR_KIND(StructElementPtr, struct { \ + irValue *address; \ + Type * result_type; \ + i32 elem_index; \ + }) \ + IR_INSTR_KIND(ArrayExtractValue, struct { \ + irValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + IR_INSTR_KIND(StructExtractValue, struct { \ + irValue *address; \ + Type * result_type; \ + i32 index; \ + }) \ + IR_INSTR_KIND(UnionTagPtr, struct { \ + irValue *address; \ + Type *type; /* ^int */ \ + }) \ + IR_INSTR_KIND(UnionTagValue, struct { \ + irValue *address; \ + Type *type; /* int */ \ + }) \ + IR_INSTR_KIND(Conv, struct { \ + irConvKind kind; \ + irValue *value; \ + Type *from, *to; \ + }) \ + IR_INSTR_KIND(Jump, struct { irBlock *block; }) \ + IR_INSTR_KIND(If, struct { \ + irValue *cond; \ + irBlock *true_block; \ + irBlock *false_block; \ + }) \ + IR_INSTR_KIND(Return, struct { irValue *value; }) \ + IR_INSTR_KIND(Select, struct { \ + irValue *cond; \ + irValue *true_value; \ + irValue *false_value; \ + }) \ + IR_INSTR_KIND(Phi, struct { irValueArray edges; Type *type; }) \ + IR_INSTR_KIND(Unreachable, i32) \ + IR_INSTR_KIND(UnaryOp, struct { \ + Type * type; \ + TokenKind op; \ + irValue *expr; \ + }) \ + IR_INSTR_KIND(BinaryOp, struct { \ + Type * type; \ + TokenKind op; \ + irValue *left, *right; \ + }) \ + IR_INSTR_KIND(Call, struct { \ + Type * type; /* return type */ \ + irValue *value; \ + irValue **args; \ + isize arg_count; \ + }) \ + IR_INSTR_KIND(VectorExtractElement, struct { \ + irValue *vector; \ + irValue *index; \ + }) \ + IR_INSTR_KIND(VectorInsertElement, struct { \ + irValue *vector; \ + irValue *elem; \ + irValue *index; \ + }) \ + IR_INSTR_KIND(VectorShuffle, struct { \ + irValue *vector; \ + i32 * indices; \ + i32 index_count; \ + Type * type; \ + }) \ + IR_INSTR_KIND(StartupRuntime, i32) \ + IR_INSTR_KIND(BoundsCheck, struct { \ + TokenPos pos; \ + irValue *index; \ + irValue *len; \ + }) \ + IR_INSTR_KIND(SliceBoundsCheck, struct { \ + TokenPos pos; \ + irValue *low; \ + irValue *high; \ + bool is_substring; \ + }) + +#define IR_CONV_KINDS \ + IR_CONV_KIND(trunc) \ + IR_CONV_KIND(zext) \ + IR_CONV_KIND(fptrunc) \ + IR_CONV_KIND(fpext) \ + IR_CONV_KIND(fptoui) \ + IR_CONV_KIND(fptosi) \ + IR_CONV_KIND(uitofp) \ + IR_CONV_KIND(sitofp) \ + IR_CONV_KIND(ptrtoint) \ + IR_CONV_KIND(inttoptr) \ + IR_CONV_KIND(bitcast) + +typedef enum irInstrKind { + irInstr_Invalid, +#define IR_INSTR_KIND(x, ...) GB_JOIN2(irInstr_, x), + IR_INSTR_KINDS +#undef IR_INSTR_KIND +} irInstrKind; + +String const ir_instr_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define IR_INSTR_KIND(x, ...) {cast(u8 *)#x, gb_size_of(#x)-1}, + IR_INSTR_KINDS +#undef IR_INSTR_KIND +}; + +typedef enum irConvKind { + irConv_Invalid, +#define IR_CONV_KIND(x) GB_JOIN2(irConv_, x), + IR_CONV_KINDS +#undef IR_CONV_KIND +} irConvKind; + +String const ir_conv_strings[] = { + {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, +#define IR_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}, + IR_CONV_KINDS +#undef IR_CONV_KIND +}; + +#define IR_INSTR_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(irInstr, k); + IR_INSTR_KINDS +#undef IR_INSTR_KIND + +typedef struct irInstr irInstr; +struct irInstr { + irInstrKind kind; + + irBlock *parent; + Type *type; + + union { +#define IR_INSTR_KIND(k, ...) GB_JOIN2(irInstr, k) k; + IR_INSTR_KINDS +#undef IR_INSTR_KIND + }; +}; + + +typedef enum irValueKind { + irValue_Invalid, + + irValue_Constant, + irValue_ConstantSlice, + irValue_Nil, + irValue_TypeName, + irValue_Global, + irValue_Param, + + irValue_Proc, + irValue_Block, + irValue_Instr, + + irValue_Count, +} irValueKind; + +typedef struct irValueConstant { + Type * type; + ExactValue value; +} irValueConstant; + +typedef struct irValueConstantSlice { + Type * type; + irValue *backing_array; + i64 count; +} irValueConstantSlice; + +typedef struct irValueNil { + Type *type; +} irValueNil; + +typedef struct irValueTypeName { + Type * type; + String name; +} irValueTypeName; + +typedef struct irValueGlobal { + Entity * entity; + Type * type; + irValue * value; + irValueArray referrers; + bool is_constant; + bool is_private; + bool is_thread_local; + bool is_unnamed_addr; +} irValueGlobal; + +typedef struct irValueParam { + irProcedure *parent; + Entity * entity; + Type * type; + irValueArray referrers; +} irValueParam; + +typedef struct irValue { + irValueKind kind; + i32 index; + union { + irValueConstant Constant; + irValueConstantSlice ConstantSlice; + irValueNil Nil; + irValueTypeName TypeName; + irValueGlobal Global; + irValueParam Param; + irProcedure Proc; + irBlock Block; + irInstr Instr; + }; +} irValue; + +gb_global irValue *v_zero = NULL; +gb_global irValue *v_one = NULL; +gb_global irValue *v_zero32 = NULL; +gb_global irValue *v_one32 = NULL; +gb_global irValue *v_two32 = NULL; +gb_global irValue *v_false = NULL; +gb_global irValue *v_true = NULL; + +typedef enum irAddrKind { + irAddr_Default, + irAddr_Vector, +} irAddrKind; + +typedef struct irAddr { + irValue * addr; + AstNode * expr; // NOTE(bill): Just for testing - probably remove later + irAddrKind kind; + union { + struct { irValue *index; } Vector; + }; +} irAddr; + +irAddr ir_make_addr(irValue *addr, AstNode *expr) { + irAddr v = {addr, expr}; + return v; +} +irAddr ir_make_addr_vector(irValue *addr, irValue *index, AstNode *expr) { + irAddr v = ir_make_addr(addr, expr); + v.kind = irAddr_Vector; + v.Vector.index = index; + return v; +} + + + +typedef enum irDebugEncoding { + irDebugBasicEncoding_Invalid = 0, + + irDebugBasicEncoding_address = 1, + irDebugBasicEncoding_boolean = 2, + irDebugBasicEncoding_float = 3, + irDebugBasicEncoding_signed = 4, + irDebugBasicEncoding_signed_char = 5, + irDebugBasicEncoding_unsigned = 6, + irDebugBasicEncoding_unsigned_char = 7, + + irDebugBasicEncoding_member = 13, + irDebugBasicEncoding_pointer_type = 15, + irDebugBasicEncoding_typedef = 22, + + irDebugBasicEncoding_array_type = 1, + irDebugBasicEncoding_enumeration_type = 4, + irDebugBasicEncoding_structure_type = 19, + irDebugBasicEncoding_union_type = 23, + +} irDebugEncoding; + +typedef enum irDebugInfoKind { + irDebugInfo_Invalid, + + irDebugInfo_CompileUnit, + irDebugInfo_File, + irDebugInfo_Scope, + irDebugInfo_Proc, + irDebugInfo_AllProcs, + + irDebugInfo_BasicType, // basic types + irDebugInfo_ProcType, + irDebugInfo_DerivedType, // pointer, typedef + irDebugInfo_CompositeType, // array, struct, enum, (raw_)union + irDebugInfo_Enumerator, // For irDebugInfo_CompositeType if enum + irDebugInfo_GlobalVariable, + irDebugInfo_LocalVariable, + + + irDebugInfo_Count, +} irDebugInfoKind; + +typedef struct irDebugInfo irDebugInfo; +struct irDebugInfo { + irDebugInfoKind kind; + i32 id; + + union { + struct { + AstFile * file; + String producer; + irDebugInfo *all_procs; + } CompileUnit; + struct { + AstFile *file; + String filename; + String directory; + } File; + struct { + irDebugInfo *parent; + irDebugInfo *file; + TokenPos pos; + Scope * scope; // Actual scope + } Scope; + struct { + Entity * entity; + String name; + irDebugInfo *file; + TokenPos pos; + } Proc; + struct { + Array(irDebugInfo *) procs; + } AllProcs; + + + struct { + String name; + i32 size; + i32 align; + irDebugEncoding encoding; + } BasicType; + struct { + irDebugInfo * return_type; + Array(irDebugInfo *) param_types; + } ProcType; + struct { + irDebugInfo * base_type; + irDebugEncoding encoding; + } DerivedType; + struct { + irDebugEncoding encoding; + String name; + String identifier; + irDebugInfo * file; + TokenPos pos; + i32 size; + i32 align; + Array(irDebugInfo *) elements; + } CompositeType; + struct { + String name; + i64 value; + } Enumerator; + struct { + String name; + String linkage_name; + irDebugInfo *scope; + irDebugInfo *file; + TokenPos pos; + irValue *variable; + irDebugInfo *declaration; + } GlobalVariable; + struct { + String name; + irDebugInfo *scope; + irDebugInfo *file; + TokenPos pos; + i32 arg; // Non-zero if proc parameter + irDebugInfo *type; + } LocalVariable; + }; +}; + +typedef struct irGen { + irModule module; + gbFile output_file; + bool opt_called; +} irGen; + +irValue *ir_lookup_member(irModule *m, String name) { + irValue **v = map_ir_value_get(&m->members, hash_string(name)); + if (v != NULL) { + return *v; + } + return NULL; +} + + +Type *ir_type(irValue *value); +Type *ir_instr_type(irInstr *instr) { + switch (instr->kind) { + case irInstr_Local: + return instr->Local.type; + case irInstr_Load: + return instr->Load.type; + case irInstr_StructElementPtr: + return instr->StructElementPtr.result_type; + case irInstr_ArrayElementPtr: + return instr->ArrayElementPtr.result_type; + case irInstr_PtrOffset: + return ir_type(instr->PtrOffset.address); + case irInstr_Phi: + return instr->Phi.type; + case irInstr_ArrayExtractValue: + return instr->ArrayExtractValue.result_type; + case irInstr_StructExtractValue: + return instr->StructExtractValue.result_type; + case irInstr_UnionTagPtr: + return instr->UnionTagPtr.type; + case irInstr_UnionTagValue: + return instr->UnionTagValue.type; + case irInstr_UnaryOp: + return instr->UnaryOp.type; + case irInstr_BinaryOp: + return instr->BinaryOp.type; + case irInstr_Conv: + return instr->Conv.to; + case irInstr_Select: + return ir_type(instr->Select.true_value); + case irInstr_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 irInstr_VectorExtractElement: { + Type *vt = ir_type(instr->VectorExtractElement.vector); + Type *bt = base_vector_type(vt); + GB_ASSERT(!is_type_vector(bt)); + return bt; + } break; + case irInstr_VectorInsertElement: + return ir_type(instr->VectorInsertElement.vector); + case irInstr_VectorShuffle: + return instr->VectorShuffle.type; + } + return NULL; +} + +Type *ir_type(irValue *value) { + switch (value->kind) { + case irValue_Constant: + return value->Constant.type; + case irValue_ConstantSlice: + return value->ConstantSlice.type; + case irValue_Nil: + return value->Nil.type; + case irValue_TypeName: + return value->TypeName.type; + case irValue_Global: + return value->Global.type; + case irValue_Param: + return value->Param.type; + case irValue_Proc: + return value->Proc.type; + case irValue_Instr: + return ir_instr_type(&value->Instr); + } + return NULL; +} + +Type *ir_addr_type(irAddr lval) { + if (lval.addr != NULL) { + Type *t = ir_type(lval.addr); + GB_ASSERT(is_type_pointer(t)); + return type_deref(t); + } + return NULL; +} + + + +bool ir_is_blank_ident(AstNode *node) { + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + return is_blank_ident(i->string); + } + return false; +} + + +irInstr *ir_get_last_instr(irBlock *block) { + if (block != NULL) { + isize len = block->instrs.count; + if (len > 0) { + irValue *v = block->instrs.e[len-1]; + GB_ASSERT(v->kind == irValue_Instr); + return &v->Instr; + } + } + return NULL; + +} + +bool ir_is_instr_terminating(irInstr *i) { + if (i != NULL) { + switch (i->kind) { + case irInstr_Return: + case irInstr_Unreachable: + return true; + } + } + + return false; +} + + +void ir_add_edge(irBlock *from, irBlock *to) { + array_add(&from->succs, to); + array_add(&to->preds, from); +} + +void ir_set_instr_parent(irValue *instr, irBlock *parent) { + if (instr->kind == irValue_Instr) { + instr->Instr.parent = parent; + } +} + +irValueArray *ir_value_referrers(irValue *v) { + switch (v->kind) { + case irValue_Global: + return &v->Global.referrers; + case irValue_Param: + return &v->Param.referrers; + case irValue_Proc: { + if (v->Proc.parent != NULL) { + return &v->Proc.referrers; + } + return NULL; + } + case irValue_Instr: { + irInstr *i = &v->Instr; + switch (i->kind) { + case irInstr_Local: + return &i->Local.referrers; + } + } break; + } + + return NULL; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Make +// +//////////////////////////////////////////////////////////////// + +void ir_module_add_value (irModule *m, Entity *e, irValue *v); +irValue *ir_emit_zero_init (irProcedure *p, irValue *address); +irValue *ir_emit_comment (irProcedure *p, String text); +irValue *ir_emit_store (irProcedure *p, irValue *address, irValue *value); +irValue *ir_emit_load (irProcedure *p, irValue *address); +void ir_emit_jump (irProcedure *proc, irBlock *block); +irValue *ir_emit_conv (irProcedure *proc, irValue *value, Type *t); +irValue *ir_type_info (irProcedure *proc, Type *type); +irValue *ir_build_expr (irProcedure *proc, AstNode *expr); +void ir_build_stmt (irProcedure *proc, AstNode *node); +irValue *ir_build_cond (irProcedure *proc, AstNode *cond, irBlock *true_block, irBlock *false_block); +void ir_build_defer_stmt (irProcedure *proc, irDefer d); +irAddr ir_build_addr (irProcedure *proc, AstNode *expr); +void ir_build_proc (irValue *value, irProcedure *parent); +void ir_gen_global_type_name(irModule *m, Entity *e, String name); + + + + +irValue *ir_alloc_value(gbAllocator a, irValueKind kind) { + irValue *v = gb_alloc_item(a, irValue); + v->kind = kind; + return v; +} +irValue *ir_alloc_instr(irProcedure *proc, irInstrKind kind) { + irValue *v = ir_alloc_value(proc->module->allocator, irValue_Instr); + v->Instr.kind = kind; + proc->instr_count++; + return v; +} +irDebugInfo *ir_alloc_debug_info(gbAllocator a, irDebugInfoKind kind) { + irDebugInfo *di = gb_alloc_item(a, irDebugInfo); + di->kind = kind; + return di; +} + + + + +irValue *ir_make_value_type_name(gbAllocator a, String name, Type *type) { + irValue *v = ir_alloc_value(a, irValue_TypeName); + v->TypeName.name = name; + v->TypeName.type = type; + return v; +} + +irValue *ir_make_value_global(gbAllocator a, Entity *e, irValue *value) { + irValue *v = ir_alloc_value(a, irValue_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; +} +irValue *ir_make_value_param(gbAllocator a, irProcedure *parent, Entity *e) { + irValue *v = ir_alloc_value(a, irValue_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; +} +irValue *ir_make_value_nil(gbAllocator a, Type *type) { + irValue *v = ir_alloc_value(a, irValue_Nil); + v->Nil.type = type; + return v; +} + + + +irValue *ir_make_instr_local(irProcedure *p, Entity *e, bool zero_initialized) { + irValue *v = ir_alloc_instr(p, irInstr_Local); + irInstr *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 + ir_module_add_value(p->module, e, v); + return v; +} + + +irValue *ir_make_instr_store(irProcedure *p, irValue *address, irValue *value) { + irValue *v = ir_alloc_instr(p, irInstr_Store); + irInstr *i = &v->Instr; + i->Store.address = address; + i->Store.value = value; + return v; +} + +irValue *ir_make_instr_zero_init(irProcedure *p, irValue *address) { + irValue *v = ir_alloc_instr(p, irInstr_ZeroInit); + irInstr *i = &v->Instr; + i->ZeroInit.address = address; + return v; +} + +irValue *ir_make_instr_load(irProcedure *p, irValue *address) { + irValue *v = ir_alloc_instr(p, irInstr_Load); + irInstr *i = &v->Instr; + i->Load.address = address; + i->Load.type = type_deref(ir_type(address)); + return v; +} + +irValue *ir_make_instr_array_element_ptr(irProcedure *p, irValue *address, irValue *elem_index) { + irValue *v = ir_alloc_instr(p, irInstr_ArrayElementPtr); + irInstr *i = &v->Instr; + Type *t = ir_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(ir_type(address)), + "%s", type_to_string(ir_type(address))); + return v; +} +irValue *ir_make_instr_struct_element_ptr(irProcedure *p, irValue *address, i32 elem_index, Type *result_type) { + irValue *v = ir_alloc_instr(p, irInstr_StructElementPtr); + irInstr *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(ir_type(address)), + "%s", type_to_string(ir_type(address))); + return v; +} +irValue *ir_make_instr_ptr_offset(irProcedure *p, irValue *address, irValue *offset) { + irValue *v = ir_alloc_instr(p, irInstr_PtrOffset); + irInstr *i = &v->Instr; + i->PtrOffset.address = address; + i->PtrOffset.offset = offset; + + GB_ASSERT_MSG(is_type_pointer(ir_type(address)), + "%s", type_to_string(ir_type(address))); + GB_ASSERT_MSG(is_type_integer(ir_type(offset)), + "%s", type_to_string(ir_type(address))); + + return v; +} + + + +irValue *ir_make_instr_array_extract_value(irProcedure *p, irValue *address, i32 index) { + irValue *v = ir_alloc_instr(p, irInstr_ArrayExtractValue); + irInstr *i = &v->Instr; + i->ArrayExtractValue.address = address; + i->ArrayExtractValue.index = index; + Type *t = base_type(ir_type(address)); + GB_ASSERT(is_type_array(t)); + i->ArrayExtractValue.result_type = t->Array.elem; + return v; +} + +irValue *ir_make_instr_struct_extract_value(irProcedure *p, irValue *address, i32 index, Type *result_type) { + irValue *v = ir_alloc_instr(p, irInstr_StructExtractValue); + irInstr *i = &v->Instr; + i->StructExtractValue.address = address; + i->StructExtractValue.index = index; + i->StructExtractValue.result_type = result_type; + return v; +} + +irValue *ir_make_instr_union_tag_ptr(irProcedure *p, irValue *address) { + irValue *v = ir_alloc_instr(p, irInstr_UnionTagPtr); + irInstr *i = &v->Instr; + i->UnionTagPtr.address = address; + i->UnionTagPtr.type = t_int_ptr; + return v; +} + +irValue *ir_make_instr_union_tag_value(irProcedure *p, irValue *address) { + irValue *v = ir_alloc_instr(p, irInstr_UnionTagValue); + irInstr *i = &v->Instr; + i->UnionTagValue.address = address; + i->UnionTagValue.type = t_int_ptr; + return v; +} + +irValue *ir_make_instr_unary_op(irProcedure *p, TokenKind op, irValue *expr, Type *type) { + irValue *v = ir_alloc_instr(p, irInstr_UnaryOp); + irInstr *i = &v->Instr; + i->UnaryOp.op = op; + i->UnaryOp.expr = expr; + i->UnaryOp.type = type; + return v; +} + + +irValue *ir_make_instr_binary_op(irProcedure *p, TokenKind op, irValue *left, irValue *right, Type *type) { + irValue *v = ir_alloc_instr(p, irInstr_BinaryOp); + irInstr *i = &v->Instr; + i->BinaryOp.op = op; + i->BinaryOp.left = left; + i->BinaryOp.right = right; + i->BinaryOp.type = type; + return v; +} + +irValue *ir_make_instr_jump(irProcedure *p, irBlock *block) { + irValue *v = ir_alloc_instr(p, irInstr_Jump); + irInstr *i = &v->Instr; + i->Jump.block = block; + return v; +} +irValue *ir_make_instr_if(irProcedure *p, irValue *cond, irBlock *true_block, irBlock *false_block) { + irValue *v = ir_alloc_instr(p, irInstr_If); + irInstr *i = &v->Instr; + i->If.cond = cond; + i->If.true_block = true_block; + i->If.false_block = false_block; + return v; +} + + +irValue *ir_make_instr_phi(irProcedure *p, irValueArray edges, Type *type) { + irValue *v = ir_alloc_instr(p, irInstr_Phi); + irInstr *i = &v->Instr; + i->Phi.edges = edges; + i->Phi.type = type; + return v; +} + +irValue *ir_make_instr_unreachable(irProcedure *p) { + irValue *v = ir_alloc_instr(p, irInstr_Unreachable); + return v; +} + +irValue *ir_make_instr_return(irProcedure *p, irValue *value) { + irValue *v = ir_alloc_instr(p, irInstr_Return); + v->Instr.Return.value = value; + return v; +} + +irValue *ir_make_instr_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) { + irValue *v = ir_alloc_instr(p, irInstr_Select); + v->Instr.Select.cond = cond; + v->Instr.Select.true_value = t; + v->Instr.Select.false_value = f; + return v; +} + +irValue *ir_make_instr_call(irProcedure *p, irValue *value, irValue **args, isize arg_count, Type *result_type) { + irValue *v = ir_alloc_instr(p, irInstr_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; +} + +irValue *ir_make_instr_conv(irProcedure *p, irConvKind kind, irValue *value, Type *from, Type *to) { + irValue *v = ir_alloc_instr(p, irInstr_Conv); + v->Instr.Conv.kind = kind; + v->Instr.Conv.value = value; + v->Instr.Conv.from = from; + v->Instr.Conv.to = to; + return v; +} + +irValue *ir_make_instr_extract_element(irProcedure *p, irValue *vector, irValue *index) { + irValue *v = ir_alloc_instr(p, irInstr_VectorExtractElement); + v->Instr.VectorExtractElement.vector = vector; + v->Instr.VectorExtractElement.index = index; + return v; +} + +irValue *ir_make_instr_insert_element(irProcedure *p, irValue *vector, irValue *elem, irValue *index) { + irValue *v = ir_alloc_instr(p, irInstr_VectorInsertElement); + v->Instr.VectorInsertElement.vector = vector; + v->Instr.VectorInsertElement.elem = elem; + v->Instr.VectorInsertElement.index = index; + return v; +} + +irValue *ir_make_instr_vector_shuffle(irProcedure *p, irValue *vector, i32 *indices, isize index_count) { + irValue *v = ir_alloc_instr(p, irInstr_VectorShuffle); + v->Instr.VectorShuffle.vector = vector; + v->Instr.VectorShuffle.indices = indices; + v->Instr.VectorShuffle.index_count = index_count; + + Type *vt = base_type(ir_type(vector)); + v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); + + return v; +} + +irValue *ir_make_instr_comment(irProcedure *p, String text) { + irValue *v = ir_alloc_instr(p, irInstr_Comment); + v->Instr.Comment.text = text; + return v; +} + +irValue *ir_make_instr_bounds_check(irProcedure *p, TokenPos pos, irValue *index, irValue *len) { + irValue *v = ir_alloc_instr(p, irInstr_BoundsCheck); + v->Instr.BoundsCheck.pos = pos; + v->Instr.BoundsCheck.index = index; + v->Instr.BoundsCheck.len = len; + return v; +} +irValue *ir_make_instr_slice_bounds_check(irProcedure *p, TokenPos pos, irValue *low, irValue *high, bool is_substring) { + irValue *v = ir_alloc_instr(p, irInstr_SliceBoundsCheck); + v->Instr.SliceBoundsCheck.pos = pos; + v->Instr.SliceBoundsCheck.low = low; + v->Instr.SliceBoundsCheck.high = high; + v->Instr.SliceBoundsCheck.is_substring = is_substring; + return v; +} + + + +irValue *ir_make_value_constant(gbAllocator a, Type *type, ExactValue value) { + irValue *v = ir_alloc_value(a, irValue_Constant); + v->Constant.type = type; + v->Constant.value = value; + return v; +} + + +irValue *ir_make_value_constant_slice(gbAllocator a, Type *type, irValue *backing_array, i64 count) { + irValue *v = ir_alloc_value(a, irValue_ConstantSlice); + v->ConstantSlice.type = type; + v->ConstantSlice.backing_array = backing_array; + v->ConstantSlice.count = count; + return v; +} + +irValue *ir_make_const_int(gbAllocator a, i64 i) { + return ir_make_value_constant(a, t_int, make_exact_value_integer(i)); +} +irValue *ir_make_const_i32(gbAllocator a, i64 i) { + return ir_make_value_constant(a, t_i32, make_exact_value_integer(i)); +} +irValue *ir_make_const_i64(gbAllocator a, i64 i) { + return ir_make_value_constant(a, t_i64, make_exact_value_integer(i)); +} +irValue *ir_make_const_bool(gbAllocator a, bool b) { + return ir_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); +} +irValue *ir_make_const_string(gbAllocator a, String s) { + return ir_make_value_constant(a, t_string, make_exact_value_string(s)); +} + +irValue *ir_make_value_procedure(gbAllocator a, irModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { + irValue *v = ir_alloc_value(a, irValue_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; +} + +irBlock *ir_add_block(irProcedure *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])); + } + } + + irValue *v = ir_alloc_value(proc->module->allocator, irValue_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()); + + irBlock *block = &v->Block; + + array_add(&proc->blocks, block); + proc->block_count++; + + return block; +} + + + + + +irDefer ir_add_defer_node(irProcedure *proc, isize scope_index, AstNode *stmt) { + irDefer d = {irDefer_Node}; + d.scope_index = scope_index; + d.block = proc->curr_block; + d.stmt = stmt; + array_add(&proc->defer_stmts, d); + return d; +} + + +irDefer ir_add_defer_instr(irProcedure *proc, isize scope_index, irValue *instr) { + irDefer d = {irDefer_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; +} + + + +irValue *ir_add_module_constant(irModule *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 ir_make_value_nil(a, type); + } + Type *elem = base_type(type)->Slice.elem; + Type *t = make_type_array(a, elem, count); + irValue *backing_array = ir_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); + irValue *g = ir_make_value_global(a, e, backing_array); + ir_module_add_value(m, e, g); + map_ir_value_set(&m->members, hash_string(name), g); + + return ir_make_value_constant_slice(a, type, g, count); + } + + return ir_make_value_constant(a, type, value); +} + +irValue *ir_add_global_string_array(irModule *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); + irValue *g = ir_make_value_global(a, entity, ir_add_module_constant(m, type, ev)); + g->Global.is_private = true; + // g->Global.is_unnamed_addr = true; + // g->Global.is_constant = true; + + ir_module_add_value(m, entity, g); + map_ir_value_set(&m->members, hash_string(name), g); + + return g; +} + + + + +irValue *ir_add_local(irProcedure *proc, Entity *e) { + irBlock *b = proc->decl_block; // all variables must be in the first block + irValue *instr = ir_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) { + ir_emit_zero_init(proc, instr); + // } + + return instr; +} + +irValue *ir_add_local_for_identifier(irProcedure *proc, AstNode *name, bool zero_initialized) { + Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(name)); + if (found) { + Entity *e = *found; + ir_emit_comment(proc, e->token.string); + return ir_add_local(proc, e); + } + return NULL; +} + +irValue *ir_add_local_generated(irProcedure *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 ir_add_local(proc, e); +} + +irValue *ir_add_param(irProcedure *proc, Entity *e) { + irValue *v = ir_make_value_param(proc->module->allocator, proc, e); +#if 1 + irValue *l = ir_add_local(proc, e); + ir_emit_store(proc, l, v); +#else + ir_module_add_value(proc->module, e, v); +#endif + return v; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Debug +// +//////////////////////////////////////////////////////////////// + +irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(file != NULL); + irDebugInfo *di = ir_alloc_debug_info(proc->module->allocator, irDebugInfo_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_ir_debug_info_set(&proc->module->debug_info, hash_pointer(file), di); + return di; +} + + +irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String name, irDebugInfo *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(entity != NULL); + irDebugInfo *di = ir_alloc_debug_info(proc->module->allocator, irDebugInfo_Proc); + di->Proc.entity = entity; + di->Proc.name = name; + di->Proc.file = file; + di->Proc.pos = entity->token.pos; + + map_ir_debug_info_set(&proc->module->debug_info, hash_pointer(entity), di); + return di; +} + +//////////////////////////////////////////////////////////////// +// +// @Emit +// +//////////////////////////////////////////////////////////////// + + +irValue *ir_emit(irProcedure *proc, irValue *instr) { + GB_ASSERT(instr->kind == irValue_Instr); + irBlock *b = proc->curr_block; + instr->Instr.parent = b; + if (b != NULL) { + irInstr *i = ir_get_last_instr(b); + if (!ir_is_instr_terminating(i)) { + array_add(&b->instrs, instr); + } + } + return instr; +} +irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) { + return ir_emit(p, ir_make_instr_store(p, address, value)); +} +irValue *ir_emit_load(irProcedure *p, irValue *address) { + return ir_emit(p, ir_make_instr_load(p, address)); +} +irValue *ir_emit_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) { + return ir_emit(p, ir_make_instr_select(p, cond, t, f)); +} + +irValue *ir_emit_zero_init(irProcedure *p, irValue *address) { + return ir_emit(p, ir_make_instr_zero_init(p, address)); +} + +irValue *ir_emit_comment(irProcedure *p, String text) { + return ir_emit(p, ir_make_instr_comment(p, text)); +} + + +irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) { + Type *pt = base_type(ir_type(value)); + GB_ASSERT(pt->kind == Type_Proc); + Type *results = pt->Proc.results; + return ir_emit(p, ir_make_instr_call(p, value, args, arg_count, results)); +} + +irValue *ir_emit_global_call(irProcedure *proc, char *name_, irValue **args, isize arg_count) { + String name = make_string_c(name_); + irValue **found = map_ir_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); + irValue *gp = *found; + return ir_emit_call(proc, gp, args, arg_count); +} + + + +void ir_emit_defer_stmts(irProcedure *proc, irDeferExitKind kind, irBlock *block) { + isize count = proc->defer_stmts.count; + isize i = count; + while (i --> 0) { + irDefer d = proc->defer_stmts.e[i]; + if (kind == irDeferExit_Default) { + if (proc->scope_index == d.scope_index && + d.scope_index > 1) { + ir_build_defer_stmt(proc, d); + array_pop(&proc->defer_stmts); + continue; + } else { + break; + } + } else if (kind == irDeferExit_Return) { + ir_build_defer_stmt(proc, d); + } else if (kind == irDeferExit_Branch) { + GB_ASSERT(block != NULL); + isize lower_limit = block->scope_index+1; + if (lower_limit < d.scope_index) { + ir_build_defer_stmt(proc, d); + } + } + } +} + + +void ir_open_scope(irProcedure *proc) { + proc->scope_index++; +} + +void ir_close_scope(irProcedure *proc, irDeferExitKind kind, irBlock *block) { + ir_emit_defer_stmts(proc, kind, block); + GB_ASSERT(proc->scope_index > 0); + proc->scope_index--; +} + + + +void ir_emit_unreachable(irProcedure *proc) { + ir_emit(proc, ir_make_instr_unreachable(proc)); +} + +void ir_emit_return(irProcedure *proc, irValue *v) { + ir_emit_defer_stmts(proc, irDeferExit_Return, NULL); + ir_emit(proc, ir_make_instr_return(proc, v)); +} + +void ir_emit_jump(irProcedure *proc, irBlock *target_block) { + irBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ir_emit(proc, ir_make_instr_jump(proc, target_block)); + ir_add_edge(b, target_block); + proc->curr_block = NULL; +} + +void ir_emit_if(irProcedure *proc, irValue *cond, irBlock *true_block, irBlock *false_block) { + irBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ir_emit(proc, ir_make_instr_if(proc, cond, true_block, false_block)); + ir_add_edge(b, true_block); + ir_add_edge(b, false_block); + proc->curr_block = NULL; +} + +void ir_emit_startup_runtime(irProcedure *proc) { + GB_ASSERT(proc->parent == NULL && str_eq(proc->name, str_lit("main"))); + ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime)); +} + + + + +irValue *ir_addr_store(irProcedure *proc, irAddr addr, irValue *value) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == irAddr_Vector) { + irValue *v = ir_emit_load(proc, addr.addr); + Type *elem_type = base_type(ir_type(v))->Vector.elem; + irValue *elem = ir_emit_conv(proc, value, elem_type); + irValue *out = ir_emit(proc, ir_make_instr_insert_element(proc, v, elem, addr.Vector.index)); + return ir_emit_store(proc, addr.addr, out); + } else { + irValue *v = ir_emit_conv(proc, value, ir_addr_type(addr)); + return ir_emit_store(proc, addr.addr, v); + } +} +irValue *ir_addr_load(irProcedure *proc, irAddr addr) { + if (addr.addr == NULL) { + GB_PANIC("Illegal addr load"); + return NULL; + } + + if (addr.kind == irAddr_Vector) { + irValue *v = ir_emit_load(proc, addr.addr); + return ir_emit(proc, ir_make_instr_extract_element(proc, v, addr.Vector.index)); + } + Type *t = base_type(ir_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 ir_emit_load(proc, addr.addr); +} + + + + +irValue *ir_emit_ptr_offset(irProcedure *proc, irValue *ptr, irValue *offset) { + offset = ir_emit_conv(proc, offset, t_int); + return ir_emit(proc, ir_make_instr_ptr_offset(proc, ptr, offset)); +} + +irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *right, Type *type) { + Type *t_left = ir_type(left); + Type *t_right = ir_type(right); + + if (op == Token_Add) { + if (is_type_pointer(t_left)) { + irValue *ptr = ir_emit_conv(proc, left, type); + irValue *offset = right; + return ir_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(ir_type(right))) { + irValue *ptr = ir_emit_conv(proc, right, type); + irValue *offset = left; + return ir_emit_ptr_offset(proc, ptr, offset); + } + } else if (op == Token_Sub) { + if (is_type_pointer(t_left) && is_type_integer(t_right)) { + // ptr - int + irValue *ptr = ir_emit_conv(proc, left, type); + irValue *offset = right; + return ir_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; + irModule *m = proc->module; + irValue *x = ir_emit_conv(proc, left, type); + irValue *y = ir_emit_conv(proc, right, type); + irValue *diff = ir_emit_arith(proc, op, x, y, type); + irValue *elem_size = ir_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); + return ir_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` + irValue *neg = ir_add_module_constant(proc->module, type, make_exact_value_integer(-1)); + op = Token_Xor; + right = ir_emit_arith(proc, op, right, neg, type); + GB_ASSERT(right->Instr.kind == irInstr_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 = ir_emit_conv(proc, left, type); + right = ir_emit_conv(proc, right, type); + break; + } + + return ir_emit(proc, ir_make_instr_binary_op(proc, op, left, right, type)); +} + +irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) { + Type *a = base_type(ir_type(left)); + Type *b = base_type(ir_type(right)); + + GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)); + + if (are_types_identical(a, b)) { + // NOTE(bill): No need for a conversion + } else if (left->kind == irValue_Constant || left->kind == irValue_Nil) { + left = ir_emit_conv(proc, left, ir_type(right)); + } else if (right->kind == irValue_Constant || right->kind == irValue_Nil) { + right = ir_emit_conv(proc, right, ir_type(left)); + } + + Type *result = t_bool; + if (is_type_vector(a)) { + result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); + } + return ir_emit(proc, ir_make_instr_binary_op(proc, op_kind, left, right, result)); +} + +irValue *ir_emit_array_ep(irProcedure *proc, irValue *s, irValue *index) { + GB_ASSERT(index != NULL); + Type *st = base_type(type_deref(ir_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 = ir_emit_conv(proc, index, t_i32); + return ir_emit(proc, ir_make_instr_array_element_ptr(proc, s, index)); +} + +irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index) { + return ir_emit_array_ep(proc, s, ir_make_const_i32(proc->module->allocator, index)); +} + +irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) { + Type *t = ir_type(u); + GB_ASSERT(is_type_pointer(t) && + is_type_union(type_deref(t))); + GB_ASSERT(are_types_identical(t, ir_type(u))); + return ir_emit(proc, ir_make_instr_union_tag_ptr(proc, u)); +} + +irValue *ir_emit_union_tag_value(irProcedure *proc, irValue *u) { + Type *t = ir_type(u); + GB_ASSERT(is_type_union(t)); + GB_ASSERT(are_types_identical(t, ir_type(u))); + return ir_emit(proc, ir_make_instr_union_tag_value(proc, u)); +} + + + +irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { + gbAllocator a = proc->module->allocator; + Type *t = base_type(type_deref(ir_type(s))); + Type *result_type = NULL; + irValue *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(ir_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + gep = ir_make_instr_struct_element_ptr(proc, s, index, result_type); + return ir_emit(proc, gep); +} + + + +irValue *ir_emit_array_ev(irProcedure *proc, irValue *s, i32 index) { + Type *st = base_type(ir_type(s)); + GB_ASSERT(is_type_array(st)); + return ir_emit(proc, ir_make_instr_array_extract_value(proc, s, index)); +} + +irValue *ir_emit_struct_ev(irProcedure *proc, irValue *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(ir_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(ir_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + return ir_emit(proc, ir_make_instr_struct_extract_value(proc, s, index, result_type)); +} + + +irValue *ir_emit_deep_field_gep(irProcedure *proc, Type *type, irValue *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 = ir_emit_load(proc, e); + e = ir_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 = ir_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + } else if (type->kind == Type_Record) { + type = type->Record.fields[index]->type; + e = ir_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 = ir_emit_struct_ep(proc, e, index); + } break; + + case Basic_string: + e = ir_emit_struct_ep(proc, e, index); + break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else if (type->kind == Type_Slice) { + e = ir_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Vector) { + e = ir_emit_array_epi(proc, e, index); + } else if (type->kind == Type_Array) { + e = ir_emit_array_epi(proc, e, index); + } else { + GB_PANIC("un-gep-able type"); + } + } + + return e; +} + + +irValue *ir_emit_deep_field_ev(irProcedure *proc, Type *type, irValue *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 = ir_emit_load(proc, e); + e = ir_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 = ir_emit_conv(proc, e, type); + } else { + e = ir_emit_struct_ev(proc, e, index); + } + } + + return e; +} + + + + +irValue *ir_array_elem(irProcedure *proc, irValue *array) { + return ir_emit_array_ep(proc, array, v_zero32); +} +irValue *ir_array_len(irProcedure *proc, irValue *array) { + Type *t = ir_type(array); + GB_ASSERT(t->kind == Type_Array); + return ir_make_const_int(proc->module->allocator, t->Array.count); +} +irValue *ir_array_cap(irProcedure *proc, irValue *array) { + return ir_array_len(proc, array); +} + +irValue *ir_slice_elem(irProcedure *proc, irValue *slice) { + Type *t = ir_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ir_emit_struct_ev(proc, slice, 0); +} +irValue *ir_slice_len(irProcedure *proc, irValue *slice) { + Type *t = ir_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ir_emit_struct_ev(proc, slice, 1); +} + +irValue *ir_string_elem(irProcedure *proc, irValue *string) { + Type *t = ir_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ir_emit_struct_ev(proc, string, 0); +} +irValue *ir_string_len(irProcedure *proc, irValue *string) { + Type *t = ir_type(string); + GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t)); + return ir_emit_struct_ev(proc, string, 1); +} + + + +irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base, irValue *low, irValue *high) { + // TODO(bill): array bounds checking for slice creation + // TODO(bill): check that low < high <= max + gbAllocator a = proc->module->allocator; + Type *bt = base_type(ir_type(base)); + + if (low == NULL) { + low = v_zero; + } + if (high == NULL) { + switch (bt->kind) { + case Type_Array: high = ir_array_len(proc, base); break; + case Type_Slice: high = ir_slice_len(proc, base); break; + case Type_Pointer: high = v_one; break; + } + } + + irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); + + irValue *elem = NULL; + switch (bt->kind) { + case Type_Array: elem = ir_array_elem(proc, base); break; + case Type_Slice: elem = ir_slice_elem(proc, base); break; + case Type_Pointer: elem = ir_emit_load(proc, base); break; + } + + elem = ir_emit_ptr_offset(proc, elem, low); + + irValue *slice = ir_add_local_generated(proc, slice_type); + + irValue *gep = NULL; + gep = ir_emit_struct_ep(proc, slice, 0); + ir_emit_store(proc, gep, elem); + + gep = ir_emit_struct_ep(proc, slice, 1); + ir_emit_store(proc, gep, len); + + return slice; +} + +irValue *ir_emit_string(irProcedure *proc, irValue *elem, irValue *len) { + irValue *str = ir_add_local_generated(proc, t_string); + irValue *str_elem = ir_emit_struct_ep(proc, str, 0); + irValue *str_len = ir_emit_struct_ep(proc, str, 1); + ir_emit_store(proc, str_elem, elem); + ir_emit_store(proc, str_len, len); + return ir_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(""); +} + +irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) { + return ir_emit(proc, ir_make_instr_conv(proc, irConv_bitcast, data, ir_type(data), type)); +} + + +irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { + Type *src_type = ir_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(base_enum_type(src_type)); + Type *dst = base_type(base_enum_type(t)); + + if (value->kind == irValue_Constant) { + if (is_type_any(dst)) { + irValue *default_value = ir_add_local_generated(proc, default_type(src_type)); + ir_emit_store(proc, default_value, value); + return ir_emit_conv(proc, ir_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` + irValue *i = ir_add_module_constant(proc->module, t_uint, ev); + return ir_emit(proc, ir_make_instr_conv(proc, irConv_inttoptr, i, t_uint, dst)); + } + return ir_add_module_constant(proc->module, t, ev); + } + } + + if (are_types_identical(src, dst)) { + return value; + } + + if (is_type_maybe(dst)) { + irValue *maybe = ir_add_local_generated(proc, dst); + irValue *val = ir_emit_struct_ep(proc, maybe, 0); + irValue *set = ir_emit_struct_ep(proc, maybe, 1); + ir_emit_store(proc, val, value); + ir_emit_store(proc, set, v_true); + return ir_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; + } + + irConvKind kind = irConv_trunc; + if (dz >= sz) { + kind = irConv_zext; + } + return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst)); + } + + // boolean -> integer + if (is_type_boolean(src) && is_type_integer(dst)) { + return ir_emit(proc, ir_make_instr_conv(proc, irConv_zext, value, src, dst)); + } + + // integer -> boolean + if (is_type_integer(src) && is_type_boolean(dst)) { + return ir_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); + irConvKind kind = irConv_fptrunc; + if (dz >= sz) { + kind = irConv_fpext; + } + return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst)); + } + + // float <-> integer + if (is_type_float(src) && is_type_integer(dst)) { + irConvKind kind = irConv_fptosi; + if (is_type_unsigned(dst)) { + kind = irConv_fptoui; + } + return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst)); + } + if (is_type_integer(src) && is_type_float(dst)) { + irConvKind kind = irConv_sitofp; + if (is_type_unsigned(src)) { + kind = irConv_uitofp; + } + return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst)); + } + + // Pointer <-> int + if (is_type_pointer(src) && is_type_int_or_uint(dst)) { + return ir_emit(proc, ir_make_instr_conv(proc, irConv_ptrtoint, value, src, dst)); + } + if (is_type_int_or_uint(src) && is_type_pointer(dst)) { + return ir_emit(proc, ir_make_instr_conv(proc, irConv_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)) { + ir_emit_comment(proc, str_lit("union - child to parent")); + gbAllocator allocator = proc->module->allocator; + irValue *parent = ir_add_local_generated(proc, t); + irValue *tag = ir_make_const_int(allocator, i); + ir_emit_store(proc, ir_emit_union_tag_ptr(proc, parent), tag); + + irValue *data = ir_emit_conv(proc, parent, t_rawptr); + + Type *tag_type = src_type; + Type *tag_type_ptr = make_type_pointer(allocator, tag_type); + irValue *underlying = ir_emit_bitcast(proc, data, tag_type_ptr); + ir_emit_store(proc, underlying, value); + + return ir_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) { + ir_emit_comment(proc, str_lit("cast - polymorphism")); + if (src_is_ptr) { + value = ir_emit_load(proc, value); + } + return ir_emit_deep_field_ev(proc, sb, value, sel); + } + } + } + } + + + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ir_emit_bitcast(proc, value, dst); + } + + + + // proc <-> proc + if (is_type_proc(src) && is_type_proc(dst)) { + return ir_emit_bitcast(proc, value, dst); + } + + // pointer -> proc + if (is_type_pointer(src) && is_type_proc(dst)) { + return ir_emit_bitcast(proc, value, dst); + } + // proc -> pointer + if (is_type_proc(src) && is_type_pointer(dst)) { + return ir_emit_bitcast(proc, value, dst); + } + + + + // []byte/[]u8 <-> string + if (is_type_u8_slice(src) && is_type_string(dst)) { + irValue *elem = ir_slice_elem(proc, value); + irValue *len = ir_slice_len(proc, value); + return ir_emit_string(proc, elem, len); + } + if (is_type_string(src) && is_type_u8_slice(dst)) { + irValue *elem = ir_string_elem(proc, value); + irValue *elem_ptr = ir_add_local_generated(proc, ir_type(elem)); + ir_emit_store(proc, elem_ptr, elem); + + irValue *len = ir_string_len(proc, value); + irValue *slice = ir_add_local_slice(proc, dst, elem_ptr, v_zero, len); + return ir_emit_load(proc, slice); + } + + if (is_type_vector(dst)) { + Type *dst_elem = dst->Vector.elem; + value = ir_emit_conv(proc, value, dst_elem); + irValue *v = ir_add_local_generated(proc, t); + v = ir_emit_load(proc, v); + v = ir_emit(proc, ir_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 = ir_emit(proc, ir_make_instr_vector_shuffle(proc, v, indices, index_count)); + return v; + } + + if (is_type_any(dst)) { + irValue *result = ir_add_local_generated(proc, t_any); + + if (is_type_untyped_nil(src)) { + return ir_emit_load(proc, result); + } + + irValue *data = NULL; + if (value->kind == irValue_Instr && + value->Instr.kind == irInstr_Load) { + // NOTE(bill): Addreirble value + data = value->Instr.Load.address; + } else { + // NOTE(bill): Non-addreirble value + data = ir_add_local_generated(proc, src_type); + ir_emit_store(proc, data, value); + } + GB_ASSERT(is_type_pointer(ir_type(data))); + GB_ASSERT(is_type_typed(src_type)); + data = ir_emit_conv(proc, data, t_rawptr); + + + irValue *ti = ir_type_info(proc, src_type); + + irValue *gep0 = ir_emit_struct_ep(proc, result, 0); + irValue *gep1 = ir_emit_struct_ep(proc, result, 1); + ir_emit_store(proc, gep0, ti); + ir_emit_store(proc, gep1, data); + + return ir_emit_load(proc, result); + } + + if (is_type_untyped_nil(src) && type_has_nil(dst)) { + return ir_make_value_nil(proc->module->allocator, t); + } + + + gb_printf_err("ir_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 ir_is_type_aggregate(Type *t) { + t = 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 ir_is_type_aggregate(t->Named.base); + } + + return false; +} + +irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) { + Type *src_type = ir_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; + } + + irModule *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 (ir_is_type_aggregate(src) || ir_is_type_aggregate(dst)) { + irValue *s = ir_add_local_generated(proc, src); + ir_emit_store(proc, s, value); + + irValue *d = ir_emit_bitcast(proc, s, make_type_pointer(m->allocator, dst)); + return ir_emit_load(proc, d); + } + + // TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM + + return ir_emit_bitcast(proc, value, dst); +} + +irValue *ir_emit_down_cast(irProcedure *proc, irValue *value, Type *t) { + GB_ASSERT(is_type_pointer(ir_type(value))); + gbAllocator allocator = proc->module->allocator; + + String field_name = check_down_cast_name(t, type_deref(ir_type(value))); + GB_ASSERT(field_name.len > 0); + Selection sel = lookup_field(proc->module->allocator, t, field_name, false); + irValue *bytes = ir_emit_conv(proc, value, t_u8_ptr); + + i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); + irValue *offset = ir_make_const_int(allocator, -offset_); + irValue *head = ir_emit_ptr_offset(proc, bytes, offset); + return ir_emit_conv(proc, head, t); +} + +irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *tuple) { + GB_ASSERT(tuple->kind == Type_Tuple); + gbAllocator a = proc->module->allocator; + + Type *src_type = ir_type(value); + bool is_ptr = is_type_pointer(src_type); + + irValue *v = ir_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); + + irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value)); + irValue *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 = ir_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + irBlock *ok_block = ir_add_block(proc, NULL, "union_cast.ok"); + irBlock *end_block = ir_add_block(proc, NULL, "union_cast.end"); + irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ir_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + irValue *gep0 = ir_emit_struct_ep(proc, v, 0); + irValue *gep1 = ir_emit_struct_ep(proc, v, 1); + + irValue *data = ir_emit_conv(proc, value, dst_ptr); + ir_emit_store(proc, gep0, data); + ir_emit_store(proc, gep1, v_true); + + ir_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); + + irValue *tag = ir_emit_union_tag_value(proc, value); + irValue *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 = ir_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + // HACK(bill): This is probably not very efficient + irValue *union_copy = ir_add_local_generated(proc, src_type); + ir_emit_store(proc, union_copy, value); + + irBlock *ok_block = ir_add_block(proc, NULL, "union_cast.ok"); + irBlock *end_block = ir_add_block(proc, NULL, "union_cast.end"); + irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ir_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + irValue *gep0 = ir_emit_struct_ep(proc, v, 0); + irValue *gep1 = ir_emit_struct_ep(proc, v, 1); + + irValue *data = ir_emit_load(proc, ir_emit_conv(proc, union_copy, dst_ptr)); + ir_emit_store(proc, gep0, data); + ir_emit_store(proc, gep1, v_true); + + ir_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } + return ir_emit_load(proc, v); +} + + +isize ir_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; +} + +irValue *ir_type_info(irProcedure *proc, Type *type) { + irValue **found = map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + irValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + type = default_type(type); + + i32 entry_index = ir_type_info_index(info, type); + + // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); + + return ir_emit_array_ep(proc, type_info_data, ir_make_const_i32(proc->module->allocator, entry_index)); +} + + + +irValue *ir_emit_logical_binary_expr(irProcedure *proc, AstNode *expr) { + ast_node(be, BinaryExpr, expr); +#if 0 + irBlock *true_ = ir_add_block(proc, NULL, "logical.cmp.true"); + irBlock *false_ = ir_add_block(proc, NULL, "logical.cmp.false"); + irBlock *done = ir_add_block(proc, NULL, "logical.cmp.done"); + + irValue *result = ir_add_local_generated(proc, t_bool); + ir_build_cond(proc, expr, true_, false_); + + proc->curr_block = true_; + ir_emit_store(proc, result, v_true); + ir_emit_jump(proc, done); + + proc->curr_block = false_; + ir_emit_store(proc, result, v_false); + ir_emit_jump(proc, done); + + proc->curr_block = done; + + return ir_emit_load(proc, result); +#else + irBlock *rhs = ir_add_block(proc, NULL, "logical.cmp.rhs"); + irBlock *done = ir_add_block(proc, NULL, "logical.cmp.done"); + + Type *type = type_of_expr(proc->module->info, expr); + type = default_type(type); + + irValue *short_circuit = NULL; + if (be->op.kind == Token_CmpAnd) { + ir_build_cond(proc, be->left, rhs, done); + short_circuit = v_false; + } else if (be->op.kind == Token_CmpOr) { + ir_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 ir_build_expr(proc, be->right); + } + + irValueArray 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, ir_build_expr(proc, be->right)); + ir_emit_jump(proc, done); + proc->curr_block = done; + + return ir_emit(proc, ir_make_instr_phi(proc, edges, type)); +#endif +} + + +void ir_emit_bounds_check(irProcedure *proc, Token token, irValue *index, irValue *len) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + index = ir_emit_conv(proc, index, t_int); + len = ir_emit_conv(proc, len, t_int); + + ir_emit(proc, ir_make_instr_bounds_check(proc, token.pos, index, len)); + + // gbAllocator a = proc->module->allocator; + // irValue **args = gb_alloc_array(a, irValue *, 5); + // args[0] = ir_emit_global_string(proc, token.pos.file); + // args[1] = ir_make_const_int(a, token.pos.line); + // args[2] = ir_make_const_int(a, token.pos.column); + // args[3] = ir_emit_conv(proc, index, t_int); + // args[4] = ir_emit_conv(proc, len, t_int); + + // ir_emit_global_call(proc, "__bounds_check_error", args, 5); +} + +void ir_emit_slice_bounds_check(irProcedure *proc, Token token, irValue *low, irValue *high, bool is_substring) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + low = ir_emit_conv(proc, low, t_int); + high = ir_emit_conv(proc, high, t_int); + + ir_emit(proc, ir_make_instr_slice_bounds_check(proc, token.pos, low, high, is_substring)); +} + + +//////////////////////////////////////////////////////////////// +// +// @Build +// +//////////////////////////////////////////////////////////////// + + +void ir_push_target_list(irProcedure *proc, irBlock *break_, irBlock *continue_, irBlock *fallthrough_) { + irTargetList *tl = gb_alloc_item(proc->module->allocator, irTargetList); + tl->prev = proc->target_list; + tl->break_ = break_; + tl->continue_ = continue_; + tl->fallthrough_ = fallthrough_; + proc->target_list = tl; +} + +void ir_pop_target_list(irProcedure *proc) { + proc->target_list = proc->target_list->prev; +} + + +void ir_mangle_sub_type_name(irModule *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); + ir_gen_global_type_name(m, field, child); +} + +void ir_gen_global_type_name(irModule *m, Entity *e, String name) { + irValue *t = ir_make_value_type_name(m->allocator, name, e->type); + ir_module_add_value(m, e, t); + map_ir_value_set(&m->members, hash_string(name), t); + + if (is_type_union(e->type)) { + Type *bt = base_type(e->type); + TypeRecord *s = &bt->Record; + // NOTE(bill): Zeroth entry is null (for `match type` stmts) + for (isize j = 1; j < s->field_count; j++) { + ir_mangle_sub_type_name(m, s->fields[j], name); + } + } +} + + + + +void ir_build_defer_stmt(irProcedure *proc, irDefer d) { + irBlock *b = ir_add_block(proc, NULL, "defer"); + // NOTE(bill): The prev block may defer injection before it's terminator + irInstr *last_instr = ir_get_last_instr(proc->curr_block); + if (last_instr == NULL || !ir_is_instr_terminating(last_instr)) { + ir_emit_jump(proc, b); + } + proc->curr_block = b; + ir_emit_comment(proc, str_lit("defer")); + if (d.kind == irDefer_Node) { + ir_build_stmt(proc, d.stmt); + } else if (d.kind == irDefer_Instr) { + // NOTE(bill): Need to make a new copy + irValue *instr = cast(irValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(irValue)); + ir_emit(proc, instr); + } +} + + + +irValue *ir_find_global_variable(irProcedure *proc, String name) { + irValue **value = map_ir_value_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); + return *value; +} + +irValue *ir_find_implicit_value_backing(irProcedure *proc, ImplicitValueId id) { + Entity *e = proc->module->info->implicit_values[id]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + Entity *backing = e->ImplicitValue.backing; + irValue **value = map_ir_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; +} + +void ir_build_stmt_list(irProcedure *proc, AstNodeArray stmts); + +irValue *ir_build_single_expr(irProcedure *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): ir_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 ir_make_value_nil(proc->module->allocator, tv->type); + } else if (e->kind == Entity_ImplicitValue) { + return ir_emit_load(proc, ir_find_implicit_value_backing(proc, e->ImplicitValue.id)); + } + + irValue **found = map_ir_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + irValue *v = *found; + if (v->kind == irValue_Proc) { + return v; + } + // if (e->kind == Entity_Variable && e->Variable.param) { + // return v; + // } + return ir_emit_load(proc, v); + } else if (e != NULL && e->kind == Entity_Variable) { + return ir_addr_load(proc, ir_build_addr(proc, expr)); + } + GB_PANIC("NULL value for expression from identifier: %.*s", LIT(i->string)); + return NULL; + case_end; + + case_ast_node(re, RunExpr, expr); + // TODO(bill): Run Expression + return ir_build_single_expr(proc, re->expr, tv); + case_end; + + case_ast_node(de, DerefExpr, expr); + return ir_addr_load(proc, ir_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 ir_addr_load(proc, ir_build_addr(proc, expr)); + case_end; + + case_ast_node(be, BlockExpr, expr); + ir_emit_comment(proc, str_lit("BlockExpr")); + ir_open_scope(proc); + + AstNodeArray stmts = be->stmts; + stmts.count--; + ir_build_stmt_list(proc, stmts); + + AstNode *give_stmt = be->stmts.e[be->stmts.count-1]; + GB_ASSERT(give_stmt->kind == AstNode_ExprStmt); + AstNode *give_expr = give_stmt->ExprStmt.expr; + GB_ASSERT(give_expr->kind == AstNode_GiveExpr); + irValue *value = ir_build_expr(proc, give_expr); + + ir_close_scope(proc, irDeferExit_Default, NULL); + + return value; + case_end; + + case_ast_node(ie, IfExpr, expr); + ir_emit_comment(proc, str_lit("IfExpr")); + if (ie->init != NULL) { + irBlock *init = ir_add_block(proc, expr, "if.init"); + ir_emit_jump(proc, init); + proc->curr_block = init; + ir_build_stmt(proc, ie->init); + } + + irValueArray edges = {0}; + array_init_reserve(&edges, proc->module->allocator, 2); + + GB_ASSERT(ie->else_expr != NULL); + irBlock *then = ir_add_block(proc, expr, "if.then"); + irBlock *done = ir_add_block(proc, expr, "if.done"); // NOTE(bill): Append later + irBlock *else_ = ir_add_block(proc, ie->else_expr, "if.else"); + + irValue *cond = ir_build_cond(proc, ie->cond, then, else_); + proc->curr_block = then; + + ir_open_scope(proc); + array_add(&edges, ir_build_expr(proc, ie->body)); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_emit_jump(proc, done); + proc->curr_block = else_; + + ir_open_scope(proc); + array_add(&edges, ir_build_expr(proc, ie->else_expr)); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_emit_jump(proc, done); + proc->curr_block = done; + + Type *type = type_of_expr(proc->module->info, expr); + + return ir_emit(proc, ir_make_instr_phi(proc, edges, type)); + case_end; + + case_ast_node(ge, GiveExpr, expr); + ir_emit_comment(proc, str_lit("GiveExpr")); + + irValue *v = NULL; + Type *give_type = type_of_expr(proc->module->info, expr); + GB_ASSERT(give_type != NULL); + if (give_type->kind != Type_Tuple) { + v = ir_emit_conv(proc, ir_build_expr(proc, ge->results.e[0]), give_type); + } else { + TypeTuple *tuple = &give_type->Tuple; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + irValueArray results; + array_init_reserve(&results, proc->module->tmp_allocator, tuple->variable_count); + + for_array(res_index, ge->results) { + irValue *res = ir_build_expr(proc, ge->results.e[res_index]); + Type *t = ir_type(res); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + irValue *v = ir_emit_struct_ev(proc, res, i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + + v = ir_add_local_generated(proc, give_type); + for_array(i, results) { + Entity *e = tuple->variables[i]; + irValue *res = ir_emit_conv(proc, results.e[i], e->type); + irValue *field = ir_emit_struct_ep(proc, v, i); + ir_emit_store(proc, field, res); + } + v = ir_emit_load(proc, v); + + gb_temp_arena_memory_end(tmp); + } + + return v; + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: + return ir_emit_ptr_offset(proc, ir_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer + + case Token_Maybe: + return ir_emit_conv(proc, ir_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); + + case Token_Add: + return ir_build_expr(proc, ue->expr); + + case Token_Not: // Boolean not + case Token_Xor: // Bitwise not + case Token_Sub: // Bitwise not + return ir_emit(proc, ir_make_instr_unary_op(proc, ue->op.kind, ir_build_expr(proc, ue->expr), tv->type)); + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + irValue *left = ir_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: { + irValue *right = ir_build_expr(proc, be->right); + return ir_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: { + irValue *right = ir_build_expr(proc, be->right); + irValue *cmp = ir_emit_comp(proc, be->op.kind, left, right); + return ir_emit_conv(proc, cmp, type); + } break; + + case Token_CmpAnd: + case Token_CmpOr: + return ir_emit_logical_binary_expr(proc, expr); + + case Token_as: + ir_emit_comment(proc, str_lit("cast - as")); + return ir_emit_conv(proc, left, type); + + case Token_transmute: + ir_emit_comment(proc, str_lit("cast - transmute")); + return ir_emit_transmute(proc, left, type); + + case Token_down_cast: + ir_emit_comment(proc, str_lit("cast - down_cast")); + return ir_emit_down_cast(proc, left, type); + + case Token_union_cast: + ir_emit_comment(proc, str_lit("cast - union_cast")); + return ir_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); + irValue *value = ir_make_value_procedure(proc->module->allocator, + proc->module, NULL, type, pl->type, pl->body, name); + + value->Proc.tags = pl->tags; + value->Proc.parent = proc; + + array_add(&proc->children, &value->Proc); + array_add(&proc->module->procs_to_generate, value); + + return value; + case_end; + + + case_ast_node(cl, CompoundLit, expr); + return ir_emit_load(proc, ir_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 ir_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 ir_type_info(proc, t); + } break; + + case BuiltinProc_new: { + ir_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); + + irValue **args = gb_alloc_array(allocator, irValue *, 2); + args[0] = ir_make_const_int(allocator, s); + args[1] = ir_make_const_int(allocator, a); + irValue *call = ir_emit_global_call(proc, "alloc_align", args, 2); + irValue *v = ir_emit_conv(proc, call, ptr_type); + return v; + } break; + + case BuiltinProc_new_slice: { + ir_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); + + irValue *elem_size = ir_make_const_int(allocator, s); + irValue *elem_align = ir_make_const_int(allocator, a); + + irValue *count = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t_int); + + ir_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, count, false); + + irValue *slice_size = ir_emit_arith(proc, Token_Mul, elem_size, count, t_int); + + irValue **args = gb_alloc_array(allocator, irValue *, 2); + args[0] = slice_size; + args[1] = elem_align; + irValue *call = ir_emit_global_call(proc, "alloc_align", args, 2); + + irValue *ptr = ir_emit_conv(proc, call, ptr_type); + irValue *slice = ir_add_local_generated(proc, slice_type); + + irValue *gep0 = ir_emit_struct_ep(proc, slice, 0); + irValue *gep1 = ir_emit_struct_ep(proc, slice, 1); + ir_emit_store(proc, gep0, ptr); + ir_emit_store(proc, gep1, count); + return ir_emit_load(proc, slice); + } break; + + case BuiltinProc_assert: { + ir_emit_comment(proc, str_lit("assert")); + irValue *cond = ir_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_boolean(ir_type(cond))); + + cond = ir_emit_comp(proc, Token_CmpEq, cond, v_false); + irBlock *err = ir_add_block(proc, NULL, "builtin.assert.err"); + irBlock *done = ir_add_block(proc, NULL, "builtin.assert.done"); + + ir_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); + + + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, 4); + args[0] = ir_make_const_string(proc->module->allocator, pos.file); + args[1] = ir_make_const_int(proc->module->allocator, pos.line); + args[2] = ir_make_const_int(proc->module->allocator, pos.column); + args[3] = ir_make_const_string(proc->module->allocator, expr_str); + ir_emit_global_call(proc, "__assert", args, 4); + + ir_emit_jump(proc, done); + proc->curr_block = done; + + return NULL; + } break; + + case BuiltinProc_panic: { + ir_emit_comment(proc, str_lit("panic")); + irValue *msg = ir_build_expr(proc, ce->args.e[0]); + GB_ASSERT(is_type_string(ir_type(msg))); + + Token token = ast_node_token(ce->args.e[0]); + TokenPos pos = token.pos; + + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, 4); + args[0] = ir_make_const_string(proc->module->allocator, pos.file); + args[1] = ir_make_const_int(proc->module->allocator, pos.line); + args[2] = ir_make_const_int(proc->module->allocator, pos.column); + args[3] = msg; + ir_emit_global_call(proc, "__assert", args, 4); + + return NULL; + } break; + + + case BuiltinProc_copy: { + ir_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]; + irValue *dst_slice = ir_build_expr(proc, dst_node); + irValue *src_slice = ir_build_expr(proc, src_node); + Type *slice_type = base_type(ir_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); + + irValue *dst = ir_emit_conv(proc, ir_slice_elem(proc, dst_slice), t_rawptr); + irValue *src = ir_emit_conv(proc, ir_slice_elem(proc, src_slice), t_rawptr); + + irValue *len_dst = ir_slice_len(proc, dst_slice); + irValue *len_src = ir_slice_len(proc, src_slice); + + irValue *cond = ir_emit_comp(proc, Token_Lt, len_dst, len_src); + irValue *len = ir_emit_select(proc, cond, len_dst, len_src); + + irValue *elem_size = ir_make_const_int(proc->module->allocator, size_of_elem); + irValue *byte_count = ir_emit_arith(proc, Token_Mul, len, elem_size, t_int); + + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, 3); + args[0] = dst; + args[1] = src; + args[2] = byte_count; + + ir_emit_global_call(proc, "__mem_copy", args, 3); + + return len; + } break; + #if 0 + case BuiltinProc_append: { + ir_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]; + irValue *slice_ptr = ir_build_expr(proc, sptr_node); + irValue *slice = ir_emit_load(proc, slice_ptr); + + irValue *elem = ir_slice_elem(proc, slice); + irValue *len = ir_slice_len(proc, slice); + irValue *cap = ir_slice_cap(proc, slice); + + Type *elem_type = type_deref(ir_type(elem)); + + irValue *item_value = ir_build_expr(proc, item_node); + item_value = ir_emit_conv(proc, item_value, elem_type); + + irValue *item = ir_add_local_generated(proc, elem_type); + ir_emit_store(proc, item, item_value); + + + // NOTE(bill): Check if can append is possible + irValue *cond = ir_emit_comp(proc, Token_Lt, len, cap); + irBlock *able = ir_add_block(proc, NULL, "builtin.append.able"); + irBlock *done = ir_add_block(proc, NULL, "builtin.append.done"); + + ir_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); + irValue *byte_count = ir_make_const_int(proc->module->allocator, item_size); + + irValue *offset = ir_emit_ptr_offset(proc, elem, len); + offset = ir_emit_conv(proc, offset, t_rawptr); + + item = ir_emit_ptr_offset(proc, item, v_zero); + item = ir_emit_conv(proc, item, t_rawptr); + + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, 3); + args[0] = offset; + args[1] = item; + args[2] = byte_count; + + ir_emit_global_call(proc, "__mem_copy", args, 3); + + // Increment slice length + irValue *new_len = ir_emit_arith(proc, Token_Add, len, v_one, t_int); + irValue *gep = ir_emit_struct_ep(proc, slice_ptr, 1); + ir_emit_store(proc, gep, new_len); + + ir_emit_jump(proc, done); + proc->curr_block = done; + + return ir_emit_conv(proc, cond, t_bool); + } break; + #endif + + case BuiltinProc_swizzle: { + ir_emit_comment(proc, str_lit("swizzle")); + irValue *vector = ir_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 ir_emit(proc, ir_make_instr_vector_shuffle(proc, vector, indices, index_count)); + + } break; + + case BuiltinProc_slice_ptr: { + ir_emit_comment(proc, str_lit("slice_ptr")); + irValue *ptr = ir_build_expr(proc, ce->args.e[0]); + irValue *count = ir_build_expr(proc, ce->args.e[1]); + count = ir_emit_conv(proc, count, t_int); + + Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr))); + irValue *slice = ir_add_local_generated(proc, slice_type); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), ptr); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), count); + return ir_emit_load(proc, slice); + } break; + + case BuiltinProc_min: { + ir_emit_comment(proc, str_lit("min")); + Type *t = type_of_expr(proc->module->info, expr); + irValue *x = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[0]), t); + irValue *y = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t); + irValue *cond = ir_emit_comp(proc, Token_Lt, x, y); + return ir_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_max: { + ir_emit_comment(proc, str_lit("max")); + Type *t = type_of_expr(proc->module->info, expr); + irValue *x = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[0]), t); + irValue *y = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t); + irValue *cond = ir_emit_comp(proc, Token_Gt, x, y); + return ir_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_abs: { + ir_emit_comment(proc, str_lit("abs")); + gbAllocator a = proc->module->allocator; + + irValue *x = ir_build_expr(proc, ce->args.e[0]); + Type *original_type = ir_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 = ir_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 + */ + + irValue *m = ir_emit_arith(proc, Token_Shr, + x, + ir_make_value_constant(a, t, make_exact_value_integer(sz-1)), + t); + irValue *b = ir_emit_arith(proc, Token_Xor, x, m, t); + irValue *v = ir_emit_arith(proc, Token_Sub, b, m, t); + + if (is_type_float(t)) { + v = ir_emit_bitcast(proc, v, original_type); + } + return v; + } break; + + case BuiltinProc_clamp: { + ir_emit_comment(proc, str_lit("clamp")); + Type *t = type_of_expr(proc->module->info, expr); + irValue *x = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[0]), t); + irValue *min = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t); + irValue *max = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[2]), t); + irValue *cond; + cond = ir_emit_comp(proc, Token_Gt, min, x); + x = ir_emit_select(proc, cond, min, x); + cond = ir_emit_comp(proc, Token_Lt, max, x); + x = ir_emit_select(proc, cond, max, x); + return x; + } break; + } + } + } + + // NOTE(bill): Regular call + irValue *value = ir_build_expr(proc, ce->proc); + GB_ASSERT(value != NULL); + Type *proc_type_ = base_type(ir_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++; + } + } + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, arg_count); + bool variadic = proc_type_->Proc.variadic; + bool vari_expand = ce->ellipsis.pos.line != 0; + + for_array(i, ce->args) { + irValue *a = ir_build_expr(proc, ce->args.e[i]); + Type *at = ir_type(a); + if (at->kind == Type_Tuple) { + for (isize i = 0; i < at->Tuple.variable_count; i++) { + Entity *e = at->Tuple.variables[i]; + irValue *v = ir_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] = ir_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] = ir_emit_conv(proc, args[i], variadic_type); + } + } + } else { + for (isize i = 0; i < arg_count; i++) { + args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type); + } + } + + if (variadic && !vari_expand) { + ir_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; + irValue *slice = ir_add_local_generated(proc, slice_type); + isize slice_len = arg_count+1 - type->param_count; + + if (slice_len > 0) { + irValue *base_array = ir_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++) { + irValue *addr = ir_emit_array_epi(proc, base_array, j); + ir_emit_store(proc, addr, args[i]); + } + + irValue *base_elem = ir_emit_array_epi(proc, base_array, 0); + irValue *slice_elem = ir_emit_struct_ep(proc, slice, 0); + ir_emit_store(proc, slice_elem, base_elem); + irValue *len = ir_make_const_int(allocator, slice_len); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 2), len); + } + + arg_count = type->param_count; + args[arg_count-1] = ir_emit_load(proc, slice); + } + + return ir_emit_call(proc, value, args, arg_count); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + return ir_emit_load(proc, ir_build_addr(proc, expr).addr); + case_end; + + case_ast_node(se, SliceExpr, expr); + return ir_emit_load(proc, ir_build_addr(proc, expr).addr); + case_end; + + case_ast_node(ie, IndexExpr, expr); + return ir_emit_load(proc, ir_build_addr(proc, expr).addr); + case_end; + } + + GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); + return NULL; +} + + +irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { + expr = unparen_expr(expr); + + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT_NOT_NULL(tv); + + if (tv->value.kind != ExactValue_Invalid) { + return ir_add_module_constant(proc->module, tv->type, tv->value); + } + + irValue *value = NULL; + if (tv->mode == Addressing_Variable) { + value = ir_addr_load(proc, ir_build_addr(proc, expr)); + } else { + value = ir_build_single_expr(proc, expr, tv); + } + + return value; +} + +irValue *ir_add_using_variable(irProcedure *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); + irValue **pv = map_ir_value_get(&proc->module->values, hash_pointer(parent)); + irValue *v = NULL; + if (pv != NULL) { + v = *pv; + } else { + v = ir_build_addr(proc, e->using_expr).addr; + } + GB_ASSERT(v != NULL); + irValue *var = ir_emit_deep_field_gep(proc, parent->type, v, sel); + map_ir_value_set(&proc->module->values, hash_pointer(e), var); + return var; +} + +bool ir_is_elem_const(irModule *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; +} + +irAddr ir_build_addr_from_entity(irProcedure *proc, Entity *e, AstNode *expr) { + GB_ASSERT(e != NULL); + GB_ASSERT(e->kind != Entity_Constant); + + irValue *v = NULL; + irValue **found = map_ir_value_get(&proc->module->values, hash_pointer(e)); + if (found) { + v = *found; + } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + v = ir_add_using_variable(proc, e); + } else if (e->kind == Entity_ImplicitValue) { + // TODO(bill): Should a copy be made? + v = ir_find_implicit_value_backing(proc, e->ImplicitValue.id); + } + + if (v == NULL) { + GB_PANIC("Unknown value: %.*s, entity: %p %.*s\n", LIT(e->token.string), e, LIT(entity_strings[e->kind])); + } + + return ir_make_addr(v, expr); +} + +irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { + switch (expr->kind) { + case_ast_node(i, Ident, expr); + if (ir_is_blank_ident(expr)) { + irAddr val = {0}; + return val; + } + + Entity *e = entity_of_ident(proc->module->info, expr); + return ir_build_addr_from_entity(proc, e, expr); + case_end; + + case_ast_node(pe, ParenExpr, expr); + return ir_build_addr(proc, unparen_expr(expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + ir_emit_comment(proc, str_lit("SelectorExpr")); + AstNode *sel = unparen_expr(se->selector); + GB_ASSERT(sel->kind == AstNode_Ident); + String selector = sel->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 ir_build_addr(proc, unparen_expr(se->selector)); + } else { + Selection sel = lookup_field(proc->module->allocator, type, selector, false); + GB_ASSERT(sel.entity != NULL); + + irValue *a = ir_build_addr(proc, se->expr).addr; + a = ir_emit_deep_field_gep(proc, type, a, sel); + return ir_make_addr(a, expr); + } + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: { + return ir_build_addr(proc, ue->expr); + } + default: + GB_PANIC("Invalid unary expression for ir_build_addr"); + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + switch (be->op.kind) { + case Token_as: { + ir_emit_comment(proc, str_lit("Cast - as")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + irValue *v = ir_add_local_generated(proc, type); + ir_emit_store(proc, v, ir_emit_conv(proc, ir_build_expr(proc, be->left), type)); + return ir_make_addr(v, expr); + } + case Token_transmute: { + ir_emit_comment(proc, str_lit("Cast - transmute")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + irValue *v = ir_add_local_generated(proc, type); + ir_emit_store(proc, v, ir_emit_transmute(proc, ir_build_expr(proc, be->left), type)); + return ir_make_addr(v, expr); + } + default: + GB_PANIC("Invalid binary expression for ir_build_addr: %.*s\n", LIT(be->op.string)); + break; + } + case_end; + + case_ast_node(ie, IndexExpr, expr); + ir_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); + + irValue *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); + irValue *e = ir_build_addr(proc, ie->expr).addr; + using_addr = ir_emit_deep_field_gep(proc, t, e, sel); + + t = using_field->type; + } + } + + + switch (t->kind) { + case Type_Vector: { + irValue *vector = NULL; + if (using_addr != NULL) { + vector = using_addr; + } else { + vector = ir_build_addr(proc, ie->expr).addr; + if (deref) { + vector = ir_emit_load(proc, vector); + } + } + irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); + irValue *len = ir_make_const_int(a, t->Vector.count); + ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ir_make_addr_vector(vector, index, expr); + } break; + + case Type_Array: { + irValue *array = NULL; + if (using_addr != NULL) { + array = using_addr; + } else { + array = ir_build_addr(proc, ie->expr).addr; + if (deref) { + array = ir_emit_load(proc, array); + } + } + irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); + irValue *elem = ir_emit_array_ep(proc, array, index); + irValue *len = ir_make_const_int(a, t->Vector.count); + ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ir_make_addr(elem, expr); + } break; + + case Type_Slice: { + irValue *slice = NULL; + if (using_addr != NULL) { + slice = ir_emit_load(proc, using_addr); + } else { + slice = ir_build_expr(proc, ie->expr); + if (deref) { + slice = ir_emit_load(proc, slice); + } + } + irValue *elem = ir_slice_elem(proc, slice); + irValue *len = ir_slice_len(proc, slice); + irValue *index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); + ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + irValue *v = ir_emit_ptr_offset(proc, elem, index); + return ir_make_addr(v, expr); + + } break; + + case Type_Basic: { // Basic_string + TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr)); + irValue *str; + irValue *elem; + irValue *len; + irValue *index; + + if (using_addr != NULL) { + str = ir_emit_load(proc, using_addr); + } else { + str = ir_build_expr(proc, ie->expr); + if (deref) { + str = ir_emit_load(proc, str); + } + } + elem = ir_string_elem(proc, str); + len = ir_string_len(proc, str); + + index = ir_emit_conv(proc, ir_build_expr(proc, ie->index), t_int); + ir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + + return ir_make_addr(ir_emit_ptr_offset(proc, elem, index), expr); + } break; + } + case_end; + + case_ast_node(se, SliceExpr, expr); + ir_emit_comment(proc, str_lit("SliceExpr")); + gbAllocator a = proc->module->allocator; + irValue *low = v_zero; + irValue *high = NULL; + + if (se->low != NULL) low = ir_build_expr(proc, se->low); + if (se->high != NULL) high = ir_build_expr(proc, se->high); + irValue *addr = ir_build_addr(proc, se->expr).addr; + irValue *base = ir_emit_load(proc, addr); + Type *type = base_type(ir_type(base)); + + if (is_type_pointer(type)) { + type = type_deref(type); + addr = base; + base = ir_emit_load(proc, base); + } + + // TODO(bill): Cleanup like mad! + + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + + if (high == NULL) high = ir_slice_len(proc, base); + + ir_emit_slice_bounds_check(proc, se->open, low, high, false); + + irValue *elem = ir_emit_ptr_offset(proc, ir_slice_elem(proc, base), low); + irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); + irValue *slice = ir_add_local_generated(proc, slice_type); + + irValue *gep0 = ir_emit_struct_ep(proc, slice, 0); + irValue *gep1 = ir_emit_struct_ep(proc, slice, 1); + ir_emit_store(proc, gep0, elem); + ir_emit_store(proc, gep1, len); + + return ir_make_addr(slice, expr); + } + + case Type_Array: { + Type *slice_type = make_type_slice(a, type->Array.elem); + + if (high == NULL) high = ir_array_len(proc, base); + + ir_emit_slice_bounds_check(proc, se->open, low, high, false); + + irValue *elem = ir_emit_ptr_offset(proc, ir_array_elem(proc, addr), low); + irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int); + irValue *slice = ir_add_local_generated(proc, slice_type); + + irValue *gep0 = ir_emit_struct_ep(proc, slice, 0); + irValue *gep1 = ir_emit_struct_ep(proc, slice, 1); + ir_emit_store(proc, gep0, elem); + ir_emit_store(proc, gep1, len); + + return ir_make_addr(slice, expr); + } + + case Type_Basic: { + GB_ASSERT(type == t_string); + if (high == NULL) { + high = ir_string_len(proc, base); + } + + ir_emit_slice_bounds_check(proc, se->open, low, high, true); + + irValue *elem, *len; + len = ir_emit_arith(proc, Token_Sub, high, low, t_int); + + elem = ir_string_elem(proc, base); + elem = ir_emit_ptr_offset(proc, elem, low); + + irValue *str = ir_add_local_generated(proc, t_string); + irValue *gep0 = ir_emit_struct_ep(proc, str, 0); + irValue *gep1 = ir_emit_struct_ep(proc, str, 1); + ir_emit_store(proc, gep0, elem); + ir_emit_store(proc, gep1, len); + + return ir_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? + irValue *addr = ir_build_expr(proc, de->expr); + addr = ir_emit_ptr_offset(proc, addr, v_zero); + return ir_make_addr(addr, expr); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + ir_emit_comment(proc, str_lit("DemaybeExpr")); + irValue *maybe = ir_build_expr(proc, de->expr); + Type *t = default_type(type_of_expr(proc->module->info, expr)); + GB_ASSERT(is_type_tuple(t)); + + irValue *result = ir_add_local_generated(proc, t); + ir_emit_store(proc, result, maybe); + + return ir_make_addr(result, expr); + case_end; + + case_ast_node(ce, CallExpr, expr); + irValue *e = ir_build_expr(proc, expr); + irValue *v = ir_add_local_generated(proc, ir_type(e)); + ir_emit_store(proc, v, e); + return ir_make_addr(v, expr); + case_end; + + + case_ast_node(cl, CompoundLit, expr); + ir_emit_comment(proc, str_lit("CompoundLit")); + Type *type = type_of_expr(proc->module->info, expr); + Type *bt = base_type(type); + irValue *v = ir_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: { + irValue *result = ir_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + for_array(index, cl->elems) { + AstNode *elem = cl->elems.e[index]; + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irValue *field_elem = ir_build_expr(proc, elem); + Type *t = ir_type(field_elem); + GB_ASSERT(t->kind != Type_Tuple); + irValue *ev = ir_emit_conv(proc, field_elem, et); + irValue *i = ir_make_const_int(proc->module->allocator, index); + result = ir_emit(proc, ir_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; + } + irValue *sv = ir_emit(proc, ir_make_instr_vector_shuffle(proc, result, indices, index_count)); + ir_emit_store(proc, v, sv); + return ir_make_addr(v, expr); + } + ir_emit_store(proc, v, result); + } break; + + case Type_Record: { + GB_ASSERT(is_type_struct(bt)); + TypeRecord *st = &bt->Record; + if (cl->elems.count > 0) { + ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(field_index, cl->elems) { + AstNode *elem = cl->elems.e[field_index]; + + irValue *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 (ir_is_elem_const(proc->module, elem, field->type)) { + continue; + } + + field_expr = ir_build_expr(proc, elem); + + GB_ASSERT(ir_type(field_expr)->kind != Type_Tuple); + + Type *ft = field->type; + irValue *fv = ir_emit_conv(proc, field_expr, ft); + irValue *gep = ir_emit_struct_ep(proc, v, index); + ir_emit_store(proc, gep, fv); + } + } + } break; + case Type_Array: { + if (cl->elems.count > 0) { + ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irValue *field_expr = ir_build_expr(proc, elem); + Type *t = ir_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + irValue *ev = ir_emit_conv(proc, field_expr, et); + irValue *gep = ir_emit_array_epi(proc, v, i); + ir_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); + irValue *slice = ir_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + GB_ASSERT(slice->kind == irValue_ConstantSlice); + + irValue *data = ir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems.e[i]; + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + + irValue *field_expr = ir_build_expr(proc, elem); + Type *t = ir_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + irValue *ev = ir_emit_conv(proc, field_expr, elem_type); + irValue *offset = ir_emit_ptr_offset(proc, data, ir_make_const_int(proc->module->allocator, i)); + ir_emit_store(proc, offset, ev); + } + + irValue *gep0 = ir_emit_struct_ep(proc, v, 0); + irValue *gep1 = ir_emit_struct_ep(proc, v, 1); + irValue *gep2 = ir_emit_struct_ep(proc, v, 1); + + ir_emit_store(proc, gep0, data); + ir_emit_store(proc, gep1, ir_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + ir_emit_store(proc, gep2, ir_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + } + } break; + } + + return ir_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 ir_make_addr(NULL, NULL); +} + +void ir_build_assign_op(irProcedure *proc, irAddr lhs, irValue *value, TokenKind op) { + irValue *old_value = ir_addr_load(proc, lhs); + Type *type = ir_type(old_value); + + irValue *change = value; + if (is_type_pointer(type) && is_type_integer(ir_type(value))) { + change = ir_emit_conv(proc, value, default_type(ir_type(value))); + } else { + change = ir_emit_conv(proc, value, type); + } + irValue *new_value = ir_emit_arith(proc, op, old_value, change, type); + ir_addr_store(proc, lhs, new_value); +} + +irValue *ir_build_cond(irProcedure *proc, AstNode *cond, irBlock *true_block, irBlock *false_block) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + return ir_build_cond(proc, pe->expr, true_block, false_block); + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + return ir_build_cond(proc, ue->expr, false_block, true_block); + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + irBlock *block = ir_add_block(proc, NULL, "cmp.and"); + ir_build_cond(proc, be->left, block, false_block); + proc->curr_block = block; + return ir_build_cond(proc, be->right, true_block, false_block); + } else if (be->op.kind == Token_CmpOr) { + irBlock *block = ir_add_block(proc, NULL, "cmp.or"); + ir_build_cond(proc, be->left, true_block, block); + proc->curr_block = block; + return ir_build_cond(proc, be->right, true_block, false_block); + } + case_end; + } + + irValue *v = ir_build_expr(proc, cond); + v = ir_emit_conv(proc, v, t_bool); + ir_emit_if(proc, v, true_block, false_block); + return v; +} + + + + +void ir_build_stmt_list(irProcedure *proc, AstNodeArray stmts) { + for_array(i, stmts) { + ir_build_stmt(proc, stmts.e[i]); + } +} + +void ir_build_stmt_internal(irProcedure *proc, AstNode *node); +void ir_build_stmt(irProcedure *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; + } + + ir_build_stmt_internal(proc, node); + + proc->module->stmt_state_flags = prev_stmt_state_flags; +} + +void ir_build_when_stmt(irProcedure *proc, AstNodeWhenStmt *ws) { + irValue *cond = ir_build_expr(proc, ws->cond); + GB_ASSERT(cond->kind == irValue_Constant && + is_type_boolean(ir_type(cond))); + + GB_ASSERT(cond->Constant.value.kind == ExactValue_Bool); + if (cond->Constant.value.value_bool) { + ir_build_stmt_list(proc, ws->body->BlockStmt.stmts); + } else if (ws->else_stmt) { + switch (ws->else_stmt->kind) { + case AstNode_BlockStmt: + ir_build_stmt_list(proc, ws->else_stmt->BlockStmt.stmts); + break; + case AstNode_WhenStmt: + ir_build_when_stmt(proc, &ws->else_stmt->WhenStmt); + break; + default: + GB_PANIC("Invalid `else` statement in `when` statement"); + break; + } + } +} + +void ir_emit_increment(irProcedure *proc, irValue *addr) { + GB_ASSERT(is_type_pointer(ir_type(addr))); + Type *type = type_deref(ir_type(addr)); + ir_emit_store(proc, addr, ir_emit_arith(proc, Token_Add, ir_emit_load(proc, addr), v_one, type)); + +} + +void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, + irValue **val_, irValue **idx_, irBlock **loop_, irBlock **done_) { + irValue *count = NULL; + Type *expr_type = base_type(ir_type(expr)); + switch (expr_type->kind) { + case Type_Array: + count = ir_make_const_int(proc->module->allocator, expr_type->Array.count); + break; + case Type_Slice: + count = ir_slice_len(proc, expr); + break; + default: + GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type)); + break; + } + + irValue *val = NULL; + irValue *idx = NULL; + irBlock *loop = NULL; + irBlock *done = NULL; + irBlock *body = NULL; + + irValue *index = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, index, ir_make_const_int(proc->module->allocator, -1)); + + loop = ir_add_block(proc, NULL, "for.index.loop"); + ir_emit_jump(proc, loop); + proc->curr_block = loop; + + irValue *incr = ir_emit_arith(proc, Token_Add, ir_emit_load(proc, index), v_one, t_int); + ir_emit_store(proc, index, incr); + + body = ir_add_block(proc, NULL, "for.index.body"); + done = ir_add_block(proc, NULL, "for.index.done"); + irValue *cond = ir_emit_comp(proc, Token_Lt, incr, count); + ir_emit_if(proc, cond, body, done); + proc->curr_block = body; + + idx = ir_emit_load(proc, index); + if (val_type != NULL) { + switch (expr_type->kind) { + case Type_Array: { + val = ir_emit_load(proc, ir_emit_array_ep(proc, expr, idx)); + } break; + case Type_Slice: { + irValue *elem = ir_slice_elem(proc, expr); + val = ir_emit_load(proc, ir_emit_ptr_offset(proc, elem, idx)); + } break; + default: + GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type)); + break; + } + } + + if (val_) *val_ = val; + if (idx_) *idx_ = idx; + if (loop_) *loop_ = loop; + if (done_) *done_ = done; +} + + +void ir_build_range_string(irProcedure *proc, irValue *expr, Type *val_type, + irValue **val_, irValue **idx_, irBlock **loop_, irBlock **done_) { + irValue *count = v_zero; + Type *expr_type = base_type(ir_type(expr)); + switch (expr_type->kind) { + case Type_Basic: + count = ir_string_len(proc, expr); + break; + default: + GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type)); + break; + } + + irValue *val = NULL; + irValue *idx = NULL; + irBlock *loop = NULL; + irBlock *done = NULL; + irBlock *body = NULL; + + irValue *index = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, index, v_zero); + + irValue *offset_ = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, index, v_zero); + + loop = ir_add_block(proc, NULL, "for.string.loop"); + ir_emit_jump(proc, loop); + proc->curr_block = loop; + + + + body = ir_add_block(proc, NULL, "for.string.body"); + done = ir_add_block(proc, NULL, "for.string.done"); + + irValue *offset = ir_emit_load(proc, offset_); + + irValue *cond = ir_emit_comp(proc, Token_Lt, offset, count); + ir_emit_if(proc, cond, body, done); + proc->curr_block = body; + + + irValue *str_elem = ir_emit_ptr_offset(proc, ir_string_elem(proc, expr), offset); + irValue *str_len = ir_emit_arith(proc, Token_Sub, count, offset, t_int); + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, 1); + args[0] = ir_emit_string(proc, str_elem, str_len); + irValue *rune_and_len = ir_emit_global_call(proc, "__string_decode_rune", args, 1); + irValue *len = ir_emit_struct_ev(proc, rune_and_len, 1); + ir_emit_store(proc, offset_, ir_emit_arith(proc, Token_Add, offset, len, t_int)); + + + idx = ir_emit_load(proc, index); + if (val_type != NULL) { + val = ir_emit_struct_ev(proc, rune_and_len, 0); + } + ir_emit_increment(proc, index); + + if (val_) *val_ = val; + if (idx_) *idx_ = idx; + if (loop_) *loop_ = loop; + if (done_) *done_ = done; +} + +void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type *val_type, + irValue **val_, irValue **idx_, irBlock **loop_, irBlock **done_) { + // TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration? + // If `lower` is changed, should `val` do so or is that not typical behaviour? + + irValue *lower = ir_build_expr(proc, node->left); + irValue *upper = NULL; + + irValue *val = NULL; + irValue *idx = NULL; + irBlock *loop = NULL; + irBlock *done = NULL; + irBlock *body = NULL; + + if (val_type == NULL) { + val_type = ir_type(lower); + } + irValue *value = ir_add_local_generated(proc, val_type); + ir_emit_store(proc, value, lower); + + irValue *index = ir_add_local_generated(proc, t_int); + ir_emit_store(proc, index, ir_make_const_int(proc->module->allocator, 0)); + + loop = ir_add_block(proc, NULL, "for.interval.loop"); + ir_emit_jump(proc, loop); + proc->curr_block = loop; + + body = ir_add_block(proc, NULL, "for.interval.body"); + done = ir_add_block(proc, NULL, "for.interval.done"); + + upper = ir_build_expr(proc, node->right); + + irValue *cond = ir_emit_comp(proc, Token_Lt, ir_emit_load(proc, value), upper); + ir_emit_if(proc, cond, body, done); + proc->curr_block = body; + + if (value != NULL) { + val = ir_emit_load(proc, value); + } + idx = ir_emit_load(proc, index); + + ir_emit_increment(proc, value); + ir_emit_increment(proc, index); + + if (val_) *val_ = val; + if (idx_) *idx_ = idx; + if (loop_) *loop_ = loop; + if (done_) *done_ = done; +} + + +void ir_build_stmt_internal(irProcedure *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_ValueDecl) { + ir_build_stmt(proc, decl); + } + case_end; + + case_ast_node(ws, WhenStmt, node); + ir_build_when_stmt(proc, ws); + case_end; + + case_ast_node(vd, ValueDecl, node); + if (vd->is_var) { + irModule *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 (!ir_is_blank_ident(name)) { + ir_add_local_for_identifier(proc, name, true); + } + } + } else { // Tuple(s) + Array(irAddr) lvals; + irValueArray 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]; + irAddr lval = ir_make_addr(NULL, NULL); + if (!ir_is_blank_ident(name)) { + ir_add_local_for_identifier(proc, name, false); + lval = ir_build_addr(proc, name); + } + + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + irValue *init = ir_build_expr(proc, vd->values.e[i]); + Type *t = ir_type(init); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + irValue *v = ir_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; + } + irValue *v = ir_emit_conv(proc, inits.e[i], ir_addr_type(lvals.e[i])); + ir_addr_store(proc, lvals.e[i], v); + } + } + + gb_temp_arena_memory_end(tmp); + } else { + for_array(i, vd->names) { + AstNode *ident = vd->names.e[i]; + GB_ASSERT(ident->kind == AstNode_Ident); + Entity *e = entity_of_ident(proc->module->info, ident); + GB_ASSERT(e != NULL); + switch (e->kind) { + case Entity_TypeName: { + // NOTE(bill): Generate a new name + // parent_proc.name-guid + String ts_name = e->token.string; + isize name_len = proc->name.len + 1 + ts_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(ts_name), guid); + String name = make_string(name_text, name_len-1); + + irValue *value = ir_make_value_type_name(proc->module->allocator, + name, e->type); + map_string_set(&proc->module->type_names, hash_pointer(e->type), name); + ir_gen_global_type_name(proc->module, e, name); + } break; + case Entity_Procedure: { + DeclInfo **decl_info = map_decl_info_get(&proc->module->info->entities, hash_pointer(e)); + GB_ASSERT(decl_info != NULL); + DeclInfo *dl = *decl_info; + ast_node(pd, ProcLit, dl->proc_lit); + if (pd->body != NULL) { + CheckerInfo *info = proc->module->info; + + 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 = e->token.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); + + + irValue *value = ir_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; + + ir_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; + + // FFI - Foreign function interace + String original_name = e->token.string; + String name = original_name; + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } + + irValue *value = ir_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + + ir_module_add_value(proc->module, e, value); + ir_build_proc(value, proc); + + if (value->Proc.tags & ProcTag_foreign) { + HashKey key = hash_string(name); + irValue **prev_value = map_ir_value_get(&proc->module->members, key); + if (prev_value == NULL) { + // NOTE(bill): Don't do mutliple declarations in the IR + map_ir_value_set(&proc->module->members, key, value); + } + } else { + array_add(&proc->children, &value->Proc); + } + } + } break; + } + } + } + case_end; + + case_ast_node(as, AssignStmt, node); + ir_emit_comment(proc, str_lit("AssignStmt")); + + irModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + + switch (as->op.kind) { + case Token_Eq: { + Array(irAddr) lvals; + array_init(&lvals, m->tmp_allocator); + + for_array(i, as->lhs) { + AstNode *lhs = as->lhs.e[i]; + irAddr lval = {0}; + if (!ir_is_blank_ident(lhs)) { + lval = ir_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]; + irValue *init = ir_build_expr(proc, rhs); + ir_addr_store(proc, lvals.e[0], init); + } else { + irValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + irValue *init = ir_build_expr(proc, as->rhs.e[i]); + array_add(&inits, init); + } + + for_array(i, inits) { + ir_addr_store(proc, lvals.e[i], inits.e[i]); + } + } + } else { + irValueArray inits; + array_init_reserve(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + irValue *init = ir_build_expr(proc, as->rhs.e[i]); + Type *t = ir_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]; + irValue *v = ir_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + for_array(i, inits) { + ir_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 + + irAddr lhs = ir_build_addr(proc, as->lhs.e[0]); + irValue *value = ir_build_expr(proc, as->rhs.e[0]); + ir_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 + ir_build_expr(proc, es->expr); + case_end; + + case_ast_node(bs, BlockStmt, node); + ir_open_scope(proc); + ir_build_stmt_list(proc, bs->stmts); + ir_close_scope(proc, irDeferExit_Default, NULL); + case_end; + + case_ast_node(ds, DeferStmt, node); + ir_emit_comment(proc, str_lit("DeferStmt")); + isize scope_index = proc->scope_index; + if (ds->stmt->kind == AstNode_BlockStmt) { + scope_index--; + } + ir_add_defer_node(proc, scope_index, ds->stmt); + case_end; + + case_ast_node(rs, ReturnStmt, node); + ir_emit_comment(proc, str_lit("ReturnStmt")); + irValue *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 = ir_emit_conv(proc, ir_build_expr(proc, rs->results.e[0]), e->type); + } else { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + + irValueArray results; + array_init_reserve(&results, proc->module->tmp_allocator, return_count); + + for_array(res_index, rs->results) { + irValue *res = ir_build_expr(proc, rs->results.e[res_index]); + Type *t = ir_type(res); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + irValue *v = ir_emit_struct_ev(proc, res, i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + + Type *ret_type = proc->type->Proc.results; + v = ir_add_local_generated(proc, ret_type); + for_array(i, results) { + Entity *e = return_type_tuple->variables[i]; + irValue *res = ir_emit_conv(proc, results.e[i], e->type); + irValue *field = ir_emit_struct_ep(proc, v, i); + ir_emit_store(proc, field, res); + } + + v = ir_emit_load(proc, v); + + gb_temp_arena_memory_end(tmp); + } + ir_emit_return(proc, v); + + case_end; + + case_ast_node(is, IfStmt, node); + ir_emit_comment(proc, str_lit("IfStmt")); + if (is->init != NULL) { + irBlock *init = ir_add_block(proc, node, "if.init"); + ir_emit_jump(proc, init); + proc->curr_block = init; + ir_build_stmt(proc, is->init); + } + irBlock *then = ir_add_block(proc, node, "if.then"); + irBlock *done = ir_add_block(proc, node, "if.done"); // NOTE(bill): Append later + irBlock *else_ = done; + if (is->else_stmt != NULL) { + else_ = ir_add_block(proc, is->else_stmt, "if.else"); + } + + ir_build_cond(proc, is->cond, then, else_); + proc->curr_block = then; + + ir_open_scope(proc); + ir_build_stmt(proc, is->body); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_emit_jump(proc, done); + + if (is->else_stmt != NULL) { + proc->curr_block = else_; + + ir_open_scope(proc); + ir_build_stmt(proc, is->else_stmt); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_emit_jump(proc, done); + } + proc->curr_block = done; + case_end; + + case_ast_node(ws, WhileStmt, node); + ir_emit_comment(proc, str_lit("WhileStmt")); + if (ws->init != NULL) { + irBlock *init = ir_add_block(proc, node, "while.init"); + ir_emit_jump(proc, init); + proc->curr_block = init; + ir_build_stmt(proc, ws->init); + } + irBlock *body = ir_add_block(proc, node, "while.body"); + irBlock *done = ir_add_block(proc, node, "while.done"); // NOTE(bill): Append later + + irBlock *loop = body; + + if (ws->cond != NULL) { + loop = ir_add_block(proc, node, "while.loop"); + } + ir_emit_jump(proc, loop); + proc->curr_block = loop; + if (loop != body) { + ir_build_cond(proc, ws->cond, body, done); + proc->curr_block = body; + } + + ir_push_target_list(proc, done, loop, NULL); + + ir_open_scope(proc); + ir_build_stmt(proc, ws->body); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_pop_target_list(proc); + ir_emit_jump(proc, loop); + + proc->curr_block = done; + case_end; + + + case_ast_node(rs, ForStmt, node); + ir_emit_comment(proc, str_lit("ForStmt")); + + Type *val_type = NULL; + Type *idx_type = NULL; + if (rs->value != NULL && !ir_is_blank_ident(rs->value)) { + val_type = type_of_expr(proc->module->info, rs->value); + } + if (rs->index != NULL && !ir_is_blank_ident(rs->index)) { + idx_type = type_of_expr(proc->module->info, rs->index); + } + + if (val_type != NULL) { + ir_add_local_for_identifier(proc, rs->value, true); + } + if (idx_type != NULL) { + ir_add_local_for_identifier(proc, rs->index, true); + } + + irValue *val = NULL; + irValue *index = NULL; + irBlock *loop = NULL; + irBlock *done = NULL; + + if (rs->expr->kind == AstNode_IntervalExpr) { + ir_build_range_interval(proc, &rs->expr->IntervalExpr, val_type, &val, &index, &loop, &done); + } else { + Type *expr_type = type_of_expr(proc->module->info, rs->expr); + Type *et = base_type(type_deref(expr_type)); + bool deref = is_type_pointer(expr_type); + switch (et->kind) { + case Type_Array: { + irValue *array = ir_build_addr(proc, rs->expr).addr; + if (deref) { + array = ir_emit_load(proc, array); + } + ir_build_range_indexed(proc, array, val_type, &val, &index, &loop, &done); + } break; + case Type_Slice: { + irValue *slice = ir_build_expr(proc, rs->expr); + if (deref) { + slice = ir_emit_load(proc, slice); + } + ir_build_range_indexed(proc, slice, val_type, &val, &index, &loop, &done); + } break; + case Type_Basic: { + irValue *string = ir_build_expr(proc, rs->expr); + if (deref) { + string = ir_emit_load(proc, string); + } + if (is_type_untyped(expr_type)) { + irValue *s = ir_add_local_generated(proc, t_string); + ir_emit_store(proc, s, string); + string = ir_emit_load(proc, s); + } + ir_build_range_string(proc, string, val_type, &val, &index, &loop, &done); + } break; + default: + GB_PANIC("Cannot range over %s", type_to_string(expr_type)); + break; + } + } + + irAddr val_addr = {0}; + irAddr idx_addr = {0}; + if (val_type != NULL) { + val_addr = ir_build_addr(proc, rs->value); + } + if (idx_type != NULL) { + idx_addr = ir_build_addr(proc, rs->index); + } + if (val_type != NULL) { + ir_addr_store(proc, val_addr, val); + } + if (idx_type != NULL) { + ir_addr_store(proc, idx_addr, index); + } + + ir_push_target_list(proc, done, loop, NULL); + + ir_open_scope(proc); + ir_build_stmt(proc, rs->body); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_pop_target_list(proc); + ir_emit_jump(proc, loop); + proc->curr_block = done; + case_end; + + case_ast_node(ms, MatchStmt, node); + ir_emit_comment(proc, str_lit("MatchStmt")); + if (ms->init != NULL) { + ir_build_stmt(proc, ms->init); + } + irValue *tag = v_true; + if (ms->tag != NULL) { + tag = ir_build_expr(proc, ms->tag); + } + irBlock *done = ir_add_block(proc, node, "match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + AstNodeArray default_stmts = {0}; + irBlock *default_fall = NULL; + irBlock *default_block = NULL; + + irBlock *fall = NULL; + bool append_fall = false; + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts.e[i]; + irBlock *body = fall; + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + if (cc->list.count == 0) { + body = ir_add_block(proc, clause, "match.dflt.body"); + } else { + body = ir_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 = ir_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; + } + + irBlock *next_cond = NULL; + for_array(j, cc->list) { + AstNode *expr = cc->list.e[j]; + next_cond = ir_add_block(proc, clause, "match.case.next"); + + irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, ir_build_expr(proc, expr)); + ir_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + } + proc->curr_block = body; + + ir_push_target_list(proc, done, NULL, fall); + ir_open_scope(proc); + ir_build_stmt_list(proc, cc->stmts); + ir_close_scope(proc, irDeferExit_Default, body); + ir_pop_target_list(proc); + + ir_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ir_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ir_push_target_list(proc, done, NULL, default_fall); + ir_open_scope(proc); + ir_build_stmt_list(proc, default_stmts); + ir_close_scope(proc, irDeferExit_Default, default_block); + ir_pop_target_list(proc); + } + + ir_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + + case_ast_node(ms, TypeMatchStmt, node); + ir_emit_comment(proc, str_lit("TypeMatchStmt")); + gbAllocator allocator = proc->module->allocator; + + irValue *parent = ir_build_expr(proc, ms->tag); + bool is_union_ptr = false; + bool is_any = false; + GB_ASSERT(check_valid_type_match_type(ir_type(parent), &is_union_ptr, &is_any)); + + irValue *tag_index = NULL; + irValue *union_data = NULL; + if (is_union_ptr) { + ir_emit_comment(proc, str_lit("get union's tag")); + tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent)); + union_data = ir_emit_conv(proc, parent, t_rawptr); + } + + irBlock *start_block = ir_add_block(proc, node, "type-match.case.first"); + ir_emit_jump(proc, start_block); + proc->curr_block = start_block; + + irBlock *done = ir_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}; + irBlock *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 = ir_add_block(proc, clause, "type-match.dflt.body"); + continue; + } + + + irBlock *body = ir_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)); + + irBlock *next_cond = NULL; + irValue *cond = NULL; + + if (is_union_ptr) { + Type *bt = type_deref(tag_var_entity->type); + irValue *index = NULL; + Type *ut = base_type(type_deref(ir_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 = ir_make_const_int(allocator, field_index); + break; + } + } + GB_ASSERT(index != NULL); + + irValue *tag_var = ir_add_local(proc, tag_var_entity); + irValue *data_ptr = ir_emit_conv(proc, union_data, tag_var_entity->type); + ir_emit_store(proc, tag_var, data_ptr); + + cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index); + } else if (is_any) { + Type *type = tag_var_entity->type; + irValue *any_data = ir_emit_struct_ev(proc, parent, 1); + irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); + ir_module_add_value(proc->module, tag_var_entity, data); + + irValue *any_ti = ir_emit_struct_ev(proc, parent, 0); + irValue *case_ti = ir_type_info(proc, type); + cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); + } else { + GB_PANIC("Invalid type for type match statement"); + } + + next_cond = ir_add_block(proc, clause, "type-match.case.next"); + ir_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + + proc->curr_block = body; + + ir_push_target_list(proc, done, NULL, NULL); + ir_open_scope(proc); + ir_build_stmt_list(proc, cc->stmts); + ir_close_scope(proc, irDeferExit_Default, body); + ir_pop_target_list(proc); + + ir_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ir_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ir_push_target_list(proc, done, NULL, NULL); + ir_open_scope(proc); + ir_build_stmt_list(proc, default_stmts); + ir_close_scope(proc, irDeferExit_Default, default_block); + ir_pop_target_list(proc); + } + + ir_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + case_ast_node(bs, BranchStmt, node); + irBlock *block = NULL; + switch (bs->token.kind) { + case Token_break: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->break_; + } + break; + case Token_continue: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->continue_; + } + break; + case Token_fallthrough: + for (irTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->fallthrough_; + } + break; + } + if (block != NULL) { + ir_emit_defer_stmts(proc, irDeferExit_Branch, block); + } + switch (bs->token.kind) { + case Token_break: ir_emit_comment(proc, str_lit("break")); break; + case Token_continue: ir_emit_comment(proc, str_lit("continue")); break; + case Token_fallthrough: ir_emit_comment(proc, str_lit("fallthrough")); break; + } + ir_emit_jump(proc, block); + ir_emit_unreachable(proc); + case_end; + + + + case_ast_node(pa, PushAllocator, node); + ir_emit_comment(proc, str_lit("PushAllocator")); + ir_open_scope(proc); + + irValue *context_ptr = ir_find_implicit_value_backing(proc, ImplicitValue_context); + irValue *prev_context = ir_add_local_generated(proc, t_context); + ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr)); + + ir_add_defer_instr(proc, proc->scope_index, ir_make_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context))); + + irValue *gep = ir_emit_struct_ep(proc, context_ptr, 1); + ir_emit_store(proc, gep, ir_build_expr(proc, pa->expr)); + + ir_build_stmt(proc, pa->body); + + ir_close_scope(proc, irDeferExit_Default, NULL); + case_end; + + + case_ast_node(pa, PushContext, node); + ir_emit_comment(proc, str_lit("PushContext")); + ir_open_scope(proc); + + irValue *context_ptr = ir_find_implicit_value_backing(proc, ImplicitValue_context); + irValue *prev_context = ir_add_local_generated(proc, t_context); + ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr)); + + ir_add_defer_instr(proc, proc->scope_index, ir_make_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context))); + + ir_emit_store(proc, context_ptr, ir_build_expr(proc, pa->expr)); + + ir_build_stmt(proc, pa->body); + + ir_close_scope(proc, irDeferExit_Default, NULL); + case_end; + + + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Procedure +// +//////////////////////////////////////////////////////////////// + +void ir_number_proc_registers(irProcedure *proc) { + i32 reg_index = 0; + for_array(i, proc->blocks) { + irBlock *b = proc->blocks.e[i]; + b->index = i; + for_array(j, b->instrs) { + irValue *value = b->instrs.e[j]; + GB_ASSERT(value->kind == irValue_Instr); + irInstr *instr = &value->Instr; + if (ir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + continue; + } + value->index = reg_index; + reg_index++; + } + } +} + +void ir_begin_procedure_body(irProcedure *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 = ir_add_block(proc, proc->type_expr, "decls"); + proc->entry_block = ir_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]; + if (!str_eq(e->token.string, str_lit("")) && + !str_eq(e->token.string, str_lit("_"))) { + irValue *param = ir_add_param(proc, e); + array_add(&proc->params, param); + } + } + } +} + + +void ir_end_procedure_body(irProcedure *proc) { + if (proc->type->Proc.result_count == 0) { + ir_emit_return(proc, NULL); + } + + if (proc->curr_block->instrs.count == 0) { + ir_emit_unreachable(proc); + } + + proc->curr_block = proc->decl_block; + ir_emit_jump(proc, proc->entry_block); + + ir_number_proc_registers(proc); +} + + +void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { + if (parent == NULL) { + if (str_eq(proc->name, str_lit("main"))) { + ir_emit_startup_runtime(proc); + } + } +} + +void ir_build_proc(irValue *value, irProcedure *parent) { + irProcedure *proc = &value->Proc; + + proc->parent = parent; + + if (proc->entity != NULL) { + irModule *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; + irDebugInfo *di_file = NULL; + + irDebugInfo **di_file_found = map_ir_debug_info_get(&m->debug_info, hash_pointer(f)); + if (di_file_found) { + di_file = *di_file_found; + GB_ASSERT(di_file->kind == irDebugInfo_File); + } else { + di_file = ir_add_debug_info_file(proc, f); + } + + ir_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; + } + + + ir_begin_procedure_body(proc); + ir_insert_code_before_proc(proc, parent); + ir_build_stmt(proc, proc->body); + ir_end_procedure_body(proc); + + proc->module->stmt_state_flags = prev_stmt_state_flags; + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Module +// +//////////////////////////////////////////////////////////////// + + + +void ir_module_add_value(irModule *m, Entity *e, irValue *v) { + map_ir_value_set(&m->values, hash_pointer(e), v); +} + +void ir_init_module(irModule *m, Checker *c, BuildContext *build_context) { + // 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(irValue); + 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; + m->build_context = build_context; + + map_ir_value_init(&m->values, heap_allocator()); + map_ir_value_init(&m->members, heap_allocator()); + map_ir_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(IR_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)); + irValue *g = ir_make_value_global(m->allocator, e, NULL); + g->Global.is_private = true; + ir_module_add_value(m, e, g); + map_ir_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(IR_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)); + irValue *g = ir_make_value_global(m->allocator, e, NULL); + ir_module_add_value(m, e, g); + map_ir_value_set(&m->members, hash_string(name), g); + } + } + + { + irDebugInfo *di = ir_alloc_debug_info(m->allocator, irDebugInfo_CompileUnit); + di->CompileUnit.file = m->info->files.entries.e[0].value; // Zeroth is the init file + di->CompileUnit.producer = str_lit("odin"); + + map_ir_debug_info_set(&m->debug_info, hash_pointer(m), di); + } +} + +void ir_destroy_module(irModule *m) { + map_ir_value_destroy(&m->values); + map_ir_value_destroy(&m->members); + map_string_destroy(&m->type_names); + map_ir_debug_info_destroy(&m->debug_info); + array_free(&m->procs_to_generate); + gb_arena_free(&m->arena); +} + + + +//////////////////////////////////////////////////////////////// +// +// @Code Generation +// +//////////////////////////////////////////////////////////////// + + +bool ir_gen_init(irGen *s, Checker *c, BuildContext *build_context) { + if (global_error_collector.count != 0) { + return false; + } + + isize tc = c->parser->total_token_count; + if (tc < 2) { + return false; + } + + ir_init_module(&s->module, c, build_context); + 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 ir_gen_destroy(irGen *s) { + ir_destroy_module(&s->module); + gb_file_close(&s->output_file); +} + +String ir_mangle_name(irGen *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 + + irModule *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); +} + +irValue *ir_get_type_info_ptr(irProcedure *proc, irValue *type_info_data, Type *type) { + i32 index = cast(i32)ir_type_info_index(proc->module->info, type); + // gb_printf_err("%d %s\n", index, type_to_string(type)); + irValue *ptr = ir_emit_array_epi(proc, type_info_data, index); + return ir_emit_bitcast(proc, ptr, t_type_info_ptr); +} + +irValue *ir_type_info_member_offset(irProcedure *proc, irValue *data, isize count, i32 *index) { + irValue *offset = ir_emit_array_epi(proc, data, *index); + *index += count; + return offset; +} + +void ir_gen_tree(irGen *s) { + irModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + + if (v_zero == NULL) { + v_zero = ir_make_const_int (m->allocator, 0); + v_one = ir_make_const_int (m->allocator, 1); + v_zero32 = ir_make_const_i32 (m->allocator, 0); + v_one32 = ir_make_const_i32 (m->allocator, 1); + v_two32 = ir_make_const_i32 (m->allocator, 2); + v_false = ir_make_const_bool(m->allocator, false); + v_true = ir_make_const_bool(m->allocator, true); + } + + 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; + } + } + } + } + + typedef struct irGlobalVariable { + irValue *var, *init; + DeclInfo *decl; + } irGlobalVariable; + Array(irGlobalVariable) global_variables; + array_init_reserve(&global_variables, m->tmp_allocator, global_variable_max_count); + + 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) { + } else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) { + } else { + name = ir_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); + ir_gen_global_type_name(m, e, name); + break; + + case Entity_Variable: { + irValue *g = ir_make_value_global(a, e, NULL); + if (decl->var_decl_tags & VarDeclTag_thread_local) { + g->Global.is_thread_local = true; + } + irGlobalVariable 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 = ir_add_module_constant(m, tav->type, v); + // } + } + } + } + + if (g->Global.value == NULL) { + array_add(&global_variables, var); + } + + ir_module_add_value(m, e, g); + map_ir_value_set(&m->members, hash_string(name), g); + } 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; + } + + irValue *p = ir_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + p->Proc.tags = pd->tags; + + ir_module_add_value(m, e, p); + HashKey hash_name = hash_string(name); + if (map_ir_value_get(&m->members, hash_name) == NULL) { + map_ir_value_set(&m->members, hash_name, p); + } + } break; + } + } + + for_array(i, m->members.entries) { + MapIrValueEntry *entry = &m->members.entries.e[i]; + irValue *v = entry->value; + if (v->kind == irValue_Proc) { + ir_build_proc(v, NULL); + } + } + + irDebugInfo *compile_unit = m->debug_info.entries.e[0].value; + GB_ASSERT(compile_unit->kind == irDebugInfo_CompileUnit); + irDebugInfo *all_procs = ir_alloc_debug_info(m->allocator, irDebugInfo_AllProcs); + + isize all_proc_max_count = 0; + for_array(i, m->debug_info.entries) { + MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + irDebugInfo *di = entry->value; + di->id = i; + if (di->kind == irDebugInfo_Proc) { + all_proc_max_count++; + } + } + + array_init_reserve(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); + map_ir_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) { + MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[i]; + irDebugInfo *di = entry->value; + di->id = i; + if (di->kind == irDebugInfo_Proc) { + array_add(&all_procs->AllProcs.procs, di); + } + } + +#if defined(GB_SYSTEM_WINDOWS) + if (m->build_context->is_dll && !has_dll_main) { + // DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32 + String name = str_lit("DllMain"); + Type *proc_params = make_type_tuple(a); + Type *proc_results = make_type_tuple(a); + + Scope *proc_scope = gb_alloc_item(a, Scope); + + proc_params->Tuple.variables = gb_alloc_array(a, Entity *, 3); + proc_params->Tuple.variable_count = 3; + + proc_results->Tuple.variables = gb_alloc_array(a, Entity *, 1); + proc_results->Tuple.variable_count = 1; + + proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); + proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, make_token_ident(str_lit("reason")), t_i32, false); + proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); + + proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false); + + + Type *proc_type = make_type_proc(a, proc_scope, + proc_params, 3, + proc_results, 1, false, ProcCC_Std); + + AstNode *body = gb_alloc_item(a, AstNode); + Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); + irValue *p = ir_make_value_procedure(a, m, e, proc_type, NULL, body, name); + + map_ir_value_set(&m->values, hash_pointer(e), p); + map_ir_value_set(&m->members, hash_string(name), p); + + irProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + e->Procedure.link_name = name; + + ir_begin_procedure_body(proc); + + // NOTE(bill): https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx + // DLL_PROCESS_ATTACH == 1 + + irAddr reason_addr = ir_build_addr_from_entity(proc, proc_params->Tuple.variables[1], NULL); + irValue *cond = ir_emit_comp(proc, Token_CmpEq, ir_addr_load(proc, reason_addr), v_one32); + irBlock *then = ir_add_block(proc, NULL, "if.then"); + irBlock *done = ir_add_block(proc, NULL, "if.done"); // NOTE(bill): Append later + ir_emit_if(proc, cond, then, done); + proc->curr_block = then; + ir_emit_global_call(proc, "main", NULL, 0); + ir_emit_jump(proc, done); + proc->curr_block = done; + + ir_emit_return(proc, v_one32); + + + ir_end_procedure_body(proc); + } +#endif +#if defined(GB_SYSTEM_WINDOWS) + if (!m->build_context->is_dll && !has_win_main) { + // WinMain :: proc(inst, prev: rawptr, cmd_line: ^byte, cmd_show: i32) -> i32 + String name = str_lit("WinMain"); + Type *proc_params = make_type_tuple(a); + Type *proc_results = make_type_tuple(a); + + Scope *proc_scope = gb_alloc_item(a, Scope); + + proc_params->Tuple.variables = gb_alloc_array(a, Entity *, 4); + proc_params->Tuple.variable_count = 4; + + proc_results->Tuple.variables = gb_alloc_array(a, Entity *, 1); + proc_results->Tuple.variable_count = 1; + + proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); + proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); + proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_u8_ptr, false); + proc_params->Tuple.variables[3] = make_entity_param(a, proc_scope, blank_token, t_i32, false); + + proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false); + + + Type *proc_type = make_type_proc(a, proc_scope, + proc_params, 4, + proc_results, 1, false, ProcCC_Std); + + AstNode *body = gb_alloc_item(a, AstNode); + Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); + irValue *p = ir_make_value_procedure(a, m, e, proc_type, NULL, body, name); + + m->entry_point_entity = e; + + map_ir_value_set(&m->values, hash_pointer(e), p); + map_ir_value_set(&m->members, hash_string(name), p); + + irProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + e->Procedure.link_name = name; + + ir_begin_procedure_body(proc); + ir_emit_global_call(proc, "main", NULL, 0); + ir_emit_return(proc, v_one32); + ir_end_procedure_body(proc); + } +#endif + { // Startup Runtime + // Cleanup(bill): probably better way of doing code insertion + String name = str_lit(IR_STARTUP_RUNTIME_PROC_NAME); + Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), + NULL, 0, + NULL, 0, false, ProcCC_Odin); + AstNode *body = gb_alloc_item(a, AstNode); + Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); + irValue *p = ir_make_value_procedure(a, m, e, proc_type, NULL, body, name); + + map_ir_value_set(&m->values, hash_pointer(e), p); + map_ir_value_set(&m->members, hash_string(name), p); + + irProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + + ir_begin_procedure_body(proc); + + // TODO(bill): Should do a dependency graph do check which order to initialize them in? + for_array(i, global_variables) { + irGlobalVariable *var = &global_variables.e[i]; + if (var->decl->init_expr != NULL) { + var->init = ir_build_expr(proc, var->decl->init_expr); + } + } + + // NOTE(bill): Initialize constants first + for_array(i, global_variables) { + irGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind == irValue_Constant) { + ir_emit_store(proc, var->var, var->init); + } + } + } + + for_array(i, global_variables) { + irGlobalVariable *var = &global_variables.e[i]; + if (var->init != NULL) { + if (var->init->kind != irValue_Constant) { + ir_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 + irValue *type_info_data = NULL; + irValue *type_info_member_data = NULL; + + irValue **found = NULL; + found = map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + type_info_data = *found; + + found = map_ir_value_get(&proc->module->members, hash_string(str_lit(IR_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; + + irValue *tag = NULL; + + switch (t->kind) { + case Type_Named: { + tag = ir_add_local_generated(proc, t_type_info_named); + + // TODO(bill): Which is better? The mangled name or actual name? + irValue *name = ir_make_const_string(a, t->Named.type_name->token.string); + irValue *gtip = ir_get_type_info_ptr(proc, type_info_data, t->Named.base); + + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), gtip); + } break; + + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: + tag = ir_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 = ir_add_local_generated(proc, t_type_info_integer); + bool is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; + irValue *bits = ir_make_const_int(a, type_size_of(m->sizes, a, t)); + irValue *is_signed = ir_make_const_bool(a, !is_unsigned); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), is_signed); + } break; + + // case Basic_f16: + case Basic_f32: + case Basic_f64: + // case Basic_f128: + { + tag = ir_add_local_generated(proc, t_type_info_float); + irValue *bits = ir_make_const_int(a, type_size_of(m->sizes, a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits); + } break; + + case Basic_rawptr: + tag = ir_add_local_generated(proc, t_type_info_pointer); + break; + + case Basic_string: + tag = ir_add_local_generated(proc, t_type_info_string); + break; + + case Basic_any: + tag = ir_add_local_generated(proc, t_type_info_any); + break; + } + break; + + case Type_Pointer: { + tag = ir_add_local_generated(proc, t_type_info_pointer); + irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Maybe: { + tag = ir_add_local_generated(proc, t_type_info_maybe); + irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Maybe.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Array: { + tag = ir_add_local_generated(proc, t_type_info_array); + irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Array.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Array.elem); + irValue *elem_size = ir_emit_struct_ep(proc, tag, 1); + ir_emit_store(proc, elem_size, ir_make_const_int(a, ez)); + + irValue *count = ir_emit_struct_ep(proc, tag, 2); + ir_emit_store(proc, count, ir_make_const_int(a, t->Array.count)); + + } break; + case Type_Slice: { + tag = ir_add_local_generated(proc, t_type_info_slice); + irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Slice.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Slice.elem); + irValue *elem_size = ir_emit_struct_ep(proc, tag, 1); + ir_emit_store(proc, elem_size, ir_make_const_int(a, ez)); + + } break; + case Type_Vector: { + tag = ir_add_local_generated(proc, t_type_info_vector); + irValue *gep = ir_get_type_info_ptr(proc, type_info_data, t->Vector.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Vector.elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_make_const_int(a, ez)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_make_const_int(a, t->Vector.count)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), ir_make_const_int(a, type_align_of(m->sizes, a, t))); + + } break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + tag = ir_add_local_generated(proc, t_type_info_struct); + + { + irValue *packed = ir_make_const_bool(a, t->Record.struct_is_packed); + irValue *ordered = ir_make_const_bool(a, t->Record.struct_is_ordered); + irValue *size = ir_make_const_int(a, type_size_of(m->sizes, a, t)); + irValue *align = ir_make_const_int(a, type_align_of(m->sizes, a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), align); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), packed); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), ordered); + } + + irValue *memory = ir_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]; + irValue *tip = ir_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); + + irValue *field = ir_emit_ptr_offset(proc, memory, ir_make_const_int(a, source_index)); + irValue *name = ir_emit_struct_ep(proc, field, 0); + irValue *type_info = ir_emit_struct_ep(proc, field, 1); + irValue *offset = ir_emit_struct_ep(proc, field, 2); + + if (f->token.string.len > 0) { + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + ir_emit_store(proc, type_info, tip); + ir_emit_store(proc, offset, ir_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); + irValue *slice = ir_emit_struct_ep(proc, tag, 0); + irValue *field_count = ir_make_const_int(a, t->Record.field_count); + + irValue *elem = ir_emit_struct_ep(proc, slice, 0); + irValue *len = ir_emit_struct_ep(proc, slice, 1); + irValue *cap = ir_emit_struct_ep(proc, slice, 2); + + ir_emit_store(proc, elem, memory); + ir_emit_store(proc, len, field_count); + ir_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Union: + tag = ir_add_local_generated(proc, t_type_info_union); + { + irValue *size = ir_make_const_int(a, type_size_of(m->sizes, a, t)); + irValue *align = ir_make_const_int(a, type_align_of(m->sizes, a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), align); + } + break; + case TypeRecord_RawUnion: { + tag = ir_add_local_generated(proc, t_type_info_raw_union); + { + irValue *size = ir_make_const_int(a, type_size_of(m->sizes, a, t)); + irValue *align = ir_make_const_int(a, type_align_of(m->sizes, a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), size); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), align); + } + + irValue *memory = ir_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++) { + irValue *field = ir_emit_ptr_offset(proc, memory, ir_make_const_int(a, i)); + irValue *name = ir_emit_struct_ep(proc, field, 0); + irValue *type_info = ir_emit_struct_ep(proc, field, 1); + irValue *offset = ir_emit_struct_ep(proc, field, 2); + + Entity *f = t->Record.fields[i]; + irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + ir_emit_store(proc, type_info, tip); + ir_emit_store(proc, offset, ir_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); + irValue *slice = ir_emit_struct_ep(proc, tag, 0); + irValue *field_count = ir_make_const_int(a, t->Record.field_count); + + irValue *elem = ir_emit_struct_ep(proc, slice, 0); + irValue *len = ir_emit_struct_ep(proc, slice, 1); + irValue *cap = ir_emit_struct_ep(proc, slice, 2); + + ir_emit_store(proc, elem, memory); + ir_emit_store(proc, len, field_count); + ir_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Enum: + tag = ir_add_local_generated(proc, t_type_info_enum); + { + GB_ASSERT(t->Record.enum_base_type != NULL); + irValue *base = ir_type_info(proc, t->Record.enum_base_type); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), base); + + if (t->Record.field_count > 0) { + Entity **fields = t->Record.fields; + isize count = t->Record.field_count; + irValue *name_array = NULL; + + { + Token token = {Token_Ident}; + 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 = ir_make_value_global(a, e, NULL); + name_array->Global.is_private = true; + ir_module_add_value(m, e, name_array); + map_ir_value_set(&m->members, hash_string(token.string), name_array); + } + + for (isize i = 0; i < count; i++) { + irValue *name_ep = ir_emit_array_epi(proc, name_array, i); + ir_emit_store(proc, name_ep, ir_make_const_string(a, fields[i]->token.string)); + } + + irValue *v_count = ir_make_const_int(a, count); + + irValue *names = ir_emit_struct_ep(proc, tag, 1); + irValue *name_array_elem = ir_array_elem(proc, name_array); + + ir_emit_store(proc, ir_emit_struct_ep(proc, names, 0), name_array_elem); + ir_emit_store(proc, ir_emit_struct_ep(proc, names, 1), v_count); + ir_emit_store(proc, ir_emit_struct_ep(proc, names, 2), v_count); + } + } + break; + } + } break; + + case Type_Tuple: { + tag = ir_add_local_generated(proc, t_type_info_tuple); + + { + irValue *align = ir_make_const_int(a, type_align_of(m->sizes, a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), align); + } + + irValue *memory = ir_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++) { + irValue *field = ir_emit_ptr_offset(proc, memory, ir_make_const_int(a, i)); + irValue *name = ir_emit_struct_ep(proc, field, 0); + irValue *type_info = ir_emit_struct_ep(proc, field, 1); + // NOTE(bill): offset is not used for tuples + + Entity *f = t->Tuple.variables[i]; + irValue *tip = ir_get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ir_emit_store(proc, name, ir_make_const_string(a, f->token.string)); + } + ir_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); + irValue *slice = ir_emit_struct_ep(proc, tag, 0); + irValue *variable_count = ir_make_const_int(a, t->Tuple.variable_count); + + irValue *elem = ir_emit_struct_ep(proc, slice, 0); + irValue *len = ir_emit_struct_ep(proc, slice, 1); + irValue *cap = ir_emit_struct_ep(proc, slice, 2); + + ir_emit_store(proc, elem, memory); + ir_emit_store(proc, len, variable_count); + ir_emit_store(proc, cap, variable_count); + } break; + + case Type_Proc: { + tag = ir_add_local_generated(proc, t_type_info_procedure); + + irValue *params = ir_emit_struct_ep(proc, tag, 0); + irValue *results = ir_emit_struct_ep(proc, tag, 1); + irValue *variadic = ir_emit_struct_ep(proc, tag, 2); + + if (t->Proc.params) { + ir_emit_store(proc, params, ir_get_type_info_ptr(proc, type_info_data, t->Proc.params)); + } + if (t->Proc.results) { + ir_emit_store(proc, results, ir_get_type_info_ptr(proc, type_info_data, t->Proc.results)); + } + ir_emit_store(proc, variadic, ir_make_const_bool(a, t->Proc.variadic)); + + // TODO(bill): Type_Info for procedures + } break; + } + + if (tag != NULL) { + irValue *gep = ir_emit_array_epi(proc, type_info_data, entry_index); + irValue *val = ir_emit_conv(proc, ir_emit_load(proc, tag), t_type_info); + ir_emit_store(proc, gep, val); + } + } + } + + ir_end_procedure_body(proc); + } + + for_array(i, m->procs_to_generate) { + ir_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); + } + + + // 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/llir_opt.c b/src/ir_opt.c index 3c6c0a7e6..66342ee3f 100644 --- a/src/llir_opt.c +++ b/src/ir_opt.c @@ -1,93 +1,93 @@ -// Optimizations for the LLIR code +// Optimizations for the IR code -void llir_opt_add_operands(llirValueArray *ops, llirInstr *i) { +void ir_opt_add_operands(irValueArray *ops, irInstr *i) { switch (i->kind) { - case llirInstr_Comment: + case irInstr_Comment: break; - case llirInstr_Local: + case irInstr_Local: break; - case llirInstr_ZeroInit: + case irInstr_ZeroInit: array_add(ops, i->ZeroInit.address); break; - case llirInstr_Store: + case irInstr_Store: array_add(ops, i->Store.address); array_add(ops, i->Store.value); break; - case llirInstr_Load: + case irInstr_Load: array_add(ops, i->Load.address); break; - case llirInstr_ArrayElementPtr: + case irInstr_ArrayElementPtr: array_add(ops, i->ArrayElementPtr.address); array_add(ops, i->ArrayElementPtr.elem_index); break; - case llirInstr_StructElementPtr: + case irInstr_StructElementPtr: array_add(ops, i->StructElementPtr.address); break; - case llirInstr_PtrOffset: + case irInstr_PtrOffset: array_add(ops, i->PtrOffset.address); array_add(ops, i->PtrOffset.offset); break; - case llirInstr_ArrayExtractValue: + case irInstr_ArrayExtractValue: array_add(ops, i->ArrayExtractValue.address); break; - case llirInstr_StructExtractValue: + case irInstr_StructExtractValue: array_add(ops, i->StructExtractValue.address); break; - case llirInstr_Conv: + case irInstr_Conv: array_add(ops, i->Conv.value); break; - case llirInstr_Jump: + case irInstr_Jump: break; - case llirInstr_If: + case irInstr_If: array_add(ops, i->If.cond); break; - case llirInstr_Return: + case irInstr_Return: if (i->Return.value != NULL) { array_add(ops, i->Return.value); } break; - case llirInstr_Select: + case irInstr_Select: array_add(ops, i->Select.cond); break; - case llirInstr_Phi: + case irInstr_Phi: for_array(j, i->Phi.edges) { array_add(ops, i->Phi.edges.e[j]); } break; - case llirInstr_Unreachable: + case irInstr_Unreachable: break; - case llirInstr_UnaryOp: + case irInstr_UnaryOp: array_add(ops, i->UnaryOp.expr); break; - case llirInstr_BinaryOp: + case irInstr_BinaryOp: array_add(ops, i->BinaryOp.left); array_add(ops, i->BinaryOp.right); break; - case llirInstr_Call: + case irInstr_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 llirInstr_VectorExtractElement: + case irInstr_VectorExtractElement: array_add(ops, i->VectorExtractElement.vector); array_add(ops, i->VectorExtractElement.index); break; - case llirInstr_VectorInsertElement: + case irInstr_VectorInsertElement: array_add(ops, i->VectorInsertElement.vector); array_add(ops, i->VectorInsertElement.elem); array_add(ops, i->VectorInsertElement.index); break; - case llirInstr_VectorShuffle: + case irInstr_VectorShuffle: array_add(ops, i->VectorShuffle.vector); break; - case llirInstr_StartupRuntime: + case irInstr_StartupRuntime: break; - case llirInstr_BoundsCheck: + case irInstr_BoundsCheck: array_add(ops, i->BoundsCheck.index); array_add(ops, i->BoundsCheck.len); break; - case llirInstr_SliceBoundsCheck: + case irInstr_SliceBoundsCheck: array_add(ops, i->SliceBoundsCheck.low); array_add(ops, i->SliceBoundsCheck.high); break; @@ -98,26 +98,26 @@ void llir_opt_add_operands(llirValueArray *ops, llirInstr *i) { -void llir_opt_block_replace_pred(llirBlock *b, llirBlock *from, llirBlock *to) { +void ir_opt_block_replace_pred(irBlock *b, irBlock *from, irBlock *to) { for_array(i, b->preds) { - llirBlock *pred = b->preds.e[i]; + irBlock *pred = b->preds.e[i]; if (pred == from) { b->preds.e[i] = to; } } } -void llir_opt_block_replace_succ(llirBlock *b, llirBlock *from, llirBlock *to) { +void ir_opt_block_replace_succ(irBlock *b, irBlock *from, irBlock *to) { for_array(i, b->succs) { - llirBlock *succ = b->succs.e[i]; + irBlock *succ = b->succs.e[i]; if (succ == from) { b->succs.e[i] = to; } } } -bool llir_opt_block_has_phi(llirBlock *b) { - return b->instrs.e[0]->Instr.kind == llirInstr_Phi; +bool ir_opt_block_has_phi(irBlock *b) { + return b->instrs.e[0]->Instr.kind == irInstr_Phi; } @@ -129,11 +129,11 @@ bool llir_opt_block_has_phi(llirBlock *b) { -llirValueArray llir_get_block_phi_nodes(llirBlock *b) { - llirValueArray phis = {0}; +irValueArray ir_get_block_phi_nodes(irBlock *b) { + irValueArray phis = {0}; for_array(i, b->instrs) { - llirInstr *instr = &b->instrs.e[i]->Instr; - if (instr->kind != llirInstr_Phi) { + irInstr *instr = &b->instrs.e[i]->Instr; + if (instr->kind != irInstr_Phi) { phis = b->instrs; phis.count = i; return phis; @@ -142,15 +142,15 @@ llirValueArray llir_get_block_phi_nodes(llirBlock *b) { return phis; } -void llir_remove_pred(llirBlock *b, llirBlock *p) { - llirValueArray phis = llir_get_block_phi_nodes(b); +void ir_remove_pred(irBlock *b, irBlock *p) { + irValueArray phis = ir_get_block_phi_nodes(b); isize i = 0; for_array(j, b->preds) { - llirBlock *pred = b->preds.e[j]; + irBlock *pred = b->preds.e[j]; if (pred != p) { b->preds.e[i] = b->preds.e[j]; for_array(k, phis) { - llirInstrPhi *phi = &phis.e[k]->Instr.Phi; + irInstrPhi *phi = &phis.e[k]->Instr.Phi; phi->edges.e[i] = phi->edges.e[j]; } i++; @@ -158,16 +158,16 @@ void llir_remove_pred(llirBlock *b, llirBlock *p) { } b->preds.count = i; for_array(k, phis) { - llirInstrPhi *phi = &phis.e[k]->Instr.Phi; + irInstrPhi *phi = &phis.e[k]->Instr.Phi; phi->edges.count = i; } } -void llir_remove_dead_blocks(llirProcedure *proc) { +void ir_remove_dead_blocks(irProcedure *proc) { isize j = 0; for_array(i, proc->blocks) { - llirBlock *b = proc->blocks.e[i]; + irBlock *b = proc->blocks.e[i]; if (b == NULL) { continue; } @@ -178,34 +178,34 @@ void llir_remove_dead_blocks(llirProcedure *proc) { proc->blocks.count = j; } -void llir_mark_reachable(llirBlock *b) { +void ir_mark_reachable(irBlock *b) { isize const WHITE = 0; isize const BLACK = -1; b->index = BLACK; for_array(i, b->succs) { - llirBlock *succ = b->succs.e[i]; + irBlock *succ = b->succs.e[i]; if (succ->index == WHITE) { - llir_mark_reachable(succ); + ir_mark_reachable(succ); } } } -void llir_remove_unreachable_blocks(llirProcedure *proc) { +void ir_remove_unreachable_blocks(irProcedure *proc) { isize const WHITE = 0; isize const BLACK = -1; for_array(i, proc->blocks) { proc->blocks.e[i]->index = WHITE; } - llir_mark_reachable(proc->blocks.e[0]); + ir_mark_reachable(proc->blocks.e[0]); for_array(i, proc->blocks) { - llirBlock *b = proc->blocks.e[i]; + irBlock *b = proc->blocks.e[i]; if (b->index == WHITE) { for_array(j, b->succs) { - llirBlock *c = b->succs.e[j]; + irBlock *c = b->succs.e[j]; if (c->index == BLACK) { - llir_remove_pred(c, b); + ir_remove_pred(c, b); } } // NOTE(bill): Mark as empty but don't actually free it @@ -213,26 +213,26 @@ void llir_remove_unreachable_blocks(llirProcedure *proc) { proc->blocks.e[i] = NULL; } } - llir_remove_dead_blocks(proc); + ir_remove_dead_blocks(proc); } -bool llir_opt_block_fusion(llirProcedure *proc, llirBlock *a) { +bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) { if (a->succs.count != 1) { return false; } - llirBlock *b = a->succs.e[0]; + irBlock *b = a->succs.e[0]; if (b->preds.count != 1) { return false; } - if (llir_opt_block_has_phi(b)) { + if (ir_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]); - llir_set_instr_parent(b->instrs.e[i], a); + ir_set_instr_parent(b->instrs.e[i], a); } array_clear(&a->succs); @@ -242,28 +242,28 @@ bool llir_opt_block_fusion(llirProcedure *proc, llirBlock *a) { // Fix preds links for_array(i, b->succs) { - llir_opt_block_replace_pred(b->succs.e[i], b, a); + ir_opt_block_replace_pred(b->succs.e[i], b, a); } proc->blocks.e[b->index] = NULL; return true; } -void llir_opt_blocks(llirProcedure *proc) { - llir_remove_unreachable_blocks(proc); +void ir_opt_blocks(irProcedure *proc) { + ir_remove_unreachable_blocks(proc); #if 1 bool changed = true; while (changed) { changed = false; for_array(i, proc->blocks) { - llirBlock *b = proc->blocks.e[i]; + irBlock *b = proc->blocks.e[i]; if (b == NULL) { continue; } GB_ASSERT(b->index == i); - if (llir_opt_block_fusion(proc, b)) { + if (ir_opt_block_fusion(proc, b)) { changed = true; } // TODO(bill): other simple block optimizations @@ -271,25 +271,25 @@ void llir_opt_blocks(llirProcedure *proc) { } #endif - llir_remove_dead_blocks(proc); + ir_remove_dead_blocks(proc); } -void llir_opt_build_referrers(llirProcedure *proc) { +void ir_opt_build_referrers(irProcedure *proc) { gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - llirValueArray ops = {0}; // NOTE(bill): Act as a buffer + irValueArray 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) { - llirBlock *b = proc->blocks.e[i]; + irBlock *b = proc->blocks.e[i]; for_array(j, b->instrs) { - llirValue *instr = b->instrs.e[j]; + irValue *instr = b->instrs.e[j]; array_clear(&ops); - llir_opt_add_operands(&ops, &instr->Instr); + ir_opt_add_operands(&ops, &instr->Instr); for_array(k, ops) { - llirValue *op = ops.e[k]; + irValue *op = ops.e[k]; if (op == NULL) { continue; } - llirValueArray *refs = llir_value_referrers(op); + irValueArray *refs = ir_value_referrers(op); if (refs != NULL) { array_add(refs, instr); } @@ -308,36 +308,36 @@ void llir_opt_build_referrers(llirProcedure *proc) { // State of Lengauer-Tarjan algorithm // Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf -typedef struct llirLTState { +typedef struct irLTState { isize count; // NOTE(bill): These are arrays - llirBlock **sdom; // Semidominator - llirBlock **parent; // Parent in DFS traversal of CFG - llirBlock **ancestor; -} llirLTState; + irBlock **sdom; // Semidominator + irBlock **parent; // Parent in DFS traversal of CFG + irBlock **ancestor; +} irLTState; // §2.2 - bottom of page -void llir_lt_link(llirLTState *lt, llirBlock *p, llirBlock *q) { +void ir_lt_link(irLTState *lt, irBlock *p, irBlock *q) { lt->ancestor[q->index] = p; } -i32 llir_lt_depth_first_search(llirLTState *lt, llirBlock *p, i32 i, llirBlock **preorder) { +i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorder) { preorder[i] = p; p->dom.pre = i++; lt->sdom[p->index] = p; - llir_lt_link(lt, NULL, p); + ir_lt_link(lt, NULL, p); for_array(index, p->succs) { - llirBlock *q = p->succs.e[index]; + irBlock *q = p->succs.e[index]; if (lt->sdom[q->index] == NULL) { lt->parent[q->index] = p; - i = llir_lt_depth_first_search(lt, q, i, preorder); + i = ir_lt_depth_first_search(lt, q, i, preorder); } } return i; } -llirBlock *llir_lt_eval(llirLTState *lt, llirBlock *v) { - llirBlock *u = v; +irBlock *ir_lt_eval(irLTState *lt, irBlock *v) { + irBlock *u = v; for (; lt->ancestor[v->index] != NULL; v = lt->ancestor[v->index]) { @@ -348,16 +348,16 @@ llirBlock *llir_lt_eval(llirLTState *lt, llirBlock *v) { return u; } -typedef struct llirDomPrePost { +typedef struct irDomPrePost { i32 pre, post; -} llirDomPrePost; +} irDomPrePost; -llirDomPrePost llir_opt_number_dom_tree(llirBlock *v, i32 pre, i32 post) { - llirDomPrePost result = {pre, post}; +irDomPrePost ir_opt_number_dom_tree(irBlock *v, i32 pre, i32 post) { + irDomPrePost result = {pre, post}; v->dom.pre = pre++; for_array(i, v->dom.children) { - result = llir_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post); + result = ir_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post); } v->dom.post = post++; @@ -367,35 +367,35 @@ llirDomPrePost llir_opt_number_dom_tree(llirBlock *v, i32 pre, i32 post) { } -// NOTE(bill): Requires `llir_opt_blocks` to be called before this -void llir_opt_build_dom_tree(llirProcedure *proc) { +// NOTE(bill): Requires `ir_opt_blocks` to be called before this +void ir_opt_build_dom_tree(irProcedure *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; - llirBlock **buf = gb_alloc_array(proc->module->tmp_allocator, llirBlock *, 5*n); + irBlock **buf = gb_alloc_array(proc->module->tmp_allocator, irBlock *, 5*n); - llirLTState lt = {0}; + irLTState lt = {0}; lt.count = n; lt.sdom = &buf[0*n]; lt.parent = &buf[1*n]; lt.ancestor = &buf[2*n]; - llirBlock **preorder = &buf[3*n]; - llirBlock **buckets = &buf[4*n]; - llirBlock *root = proc->blocks.e[0]; + irBlock **preorder = &buf[3*n]; + irBlock **buckets = &buf[4*n]; + irBlock *root = proc->blocks.e[0]; // Step 1 - number vertices - i32 pre_num = llir_lt_depth_first_search(<, root, 0, preorder); + i32 pre_num = ir_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--) { - llirBlock *w = preorder[i]; + irBlock *w = preorder[i]; // Step 3 - Implicitly define idom for nodes - for (llirBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) { - llirBlock *u = llir_lt_eval(<, v); + for (irBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) { + irBlock *u = ir_lt_eval(<, v); if (lt.sdom[u->index]->dom.pre < i) { v->dom.idom = u; } else { @@ -406,14 +406,14 @@ void llir_opt_build_dom_tree(llirProcedure *proc) { // Step 2 - Compute all sdoms lt.sdom[w->index] = lt.parent[w->index]; for_array(pred_index, w->preds) { - llirBlock *v = w->preds.e[pred_index]; - llirBlock *u = llir_lt_eval(<, v); + irBlock *v = w->preds.e[pred_index]; + irBlock *u = ir_lt_eval(<, v); if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) { lt.sdom[w->index] = lt.sdom[u->index]; } } - llir_lt_link(<, lt.parent[w->index], w); + ir_lt_link(<, lt.parent[w->index], w); if (lt.parent[w->index] == lt.sdom[w->index]) { w->dom.idom = lt.parent[w->index]; @@ -424,13 +424,13 @@ void llir_opt_build_dom_tree(llirProcedure *proc) { } // The rest of Step 3 - for (llirBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) { + for (irBlock *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++) { - llirBlock *w = preorder[i]; + irBlock *w = preorder[i]; if (w == root) { w->dom.idom = NULL; } else { @@ -449,32 +449,32 @@ void llir_opt_build_dom_tree(llirProcedure *proc) { } } - llir_opt_number_dom_tree(root, 0, 0); + ir_opt_number_dom_tree(root, 0, 0); gb_temp_arena_memory_end(tmp); } -void llir_opt_mem2reg(llirProcedure *proc) { - // TODO(bill): llir_opt_mem2reg +void ir_opt_mem2reg(irProcedure *proc) { + // TODO(bill): ir_opt_mem2reg } -void llir_opt_tree(llirGen *s) { +void ir_opt_tree(irGen *s) { s->opt_called = true; for_array(member_index, s->module.procs) { - llirProcedure *proc = s->module.procs.e[member_index]; + irProcedure *proc = s->module.procs.e[member_index]; if (proc->blocks.count == 0) { // Prototype/external procedure continue; } - llir_opt_blocks(proc); + ir_opt_blocks(proc); #if 1 - llir_opt_build_referrers(proc); - llir_opt_build_dom_tree(proc); + ir_opt_build_referrers(proc); + ir_opt_build_dom_tree(proc); - // TODO(bill): llir optimization + // TODO(bill): ir optimization // [ ] cse (common-subexpression) elim // [ ] copy elim // [ ] dead code elim @@ -485,10 +485,10 @@ void llir_opt_tree(llirGen *s) { // [ ] lift/mem2reg // [ ] lift/mem2reg - llir_opt_mem2reg(proc); + ir_opt_mem2reg(proc); #endif GB_ASSERT(proc->blocks.count > 0); - llir_number_proc_registers(proc); + ir_number_proc_registers(proc); } } diff --git a/src/ir_print.c b/src/ir_print.c new file mode 100644 index 000000000..4ceb7fe18 --- /dev/null +++ b/src/ir_print.c @@ -0,0 +1,1515 @@ +typedef struct irFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile * output; +} irFileBuffer; + +void ir_file_buffer_init(irFileBuffer *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 ir_file_buffer_destroy(irFileBuffer *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 ir_file_buffer_write(irFileBuffer *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 ir_fprintf(irFileBuffer *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); + ir_file_buffer_write(f, buf, len-1); + va_end(va); +} + + +void ir_file_write(irFileBuffer *f, void *data, isize len) { + ir_file_buffer_write(f, data, len); +} + + +bool ir_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 ir_print_escape_string(irFileBuffer *f, String name, bool print_quotes) { + isize extra = 0; + for (isize i = 0; i < name.len; i++) { + u8 c = name.text[i]; + if (!ir_valid_char(c)) { + extra += 2; + } + } + + if (extra == 0) { + ir_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 (ir_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++] = '"'; + } + + ir_file_write(f, buf, j); + + gb_temp_arena_memory_end(tmp); +} + + + +void ir_print_encoded_local(irFileBuffer *f, String name) { + ir_fprintf(f, "%%"); + ir_print_escape_string(f, name, true); +} + +void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) { + ir_fprintf(f, "@"); + if (!remove_prefix) { + ir_fprintf(f, "."); + } + ir_print_escape_string(f, name, true); +} + + +void ir_print_type(irFileBuffer *f, irModule *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: ir_fprintf(f, "i1"); break; + case Basic_i8: ir_fprintf(f, "i8"); break; + case Basic_u8: ir_fprintf(f, "i8"); break; + case Basic_i16: ir_fprintf(f, "i16"); break; + case Basic_u16: ir_fprintf(f, "i16"); break; + case Basic_i32: ir_fprintf(f, "i32"); break; + case Basic_u32: ir_fprintf(f, "i32"); break; + case Basic_i64: ir_fprintf(f, "i64"); break; + case Basic_u64: ir_fprintf(f, "i64"); break; + // case Basic_i128: ir_fprintf(f, "i128"); break; + // case Basic_u128: ir_fprintf(f, "i128"); break; + // case Basic_f16: ir_fprintf(f, "half"); break; + case Basic_f32: ir_fprintf(f, "float"); break; + case Basic_f64: ir_fprintf(f, "double"); break; + // case Basic_f128: ir_fprintf(f, "fp128"); break; + case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); break; + case Basic_string: ir_fprintf(f, "%%..string"); break; + case Basic_uint: ir_fprintf(f, "i%lld", word_bits); break; + case Basic_int: ir_fprintf(f, "i%lld", word_bits); break; + case Basic_any: ir_fprintf(f, "%%..any"); break; + } + break; + case Type_Pointer: + ir_print_type(f, m, t->Pointer.elem); + ir_fprintf(f, "*"); + break; + case Type_Maybe: + ir_fprintf(f, "{"); + ir_print_type(f, m, t->Maybe.elem); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_bool); + ir_fprintf(f, "}"); + break; + case Type_Array: + ir_fprintf(f, "[%lld x ", t->Array.count); + ir_print_type(f, m, t->Array.elem); + ir_fprintf(f, "]"); + break; + case Type_Vector: + ir_fprintf(f, "<%lld x ", t->Vector.count); + ir_print_type(f, m, t->Vector.elem); + ir_fprintf(f, ">"); + break; + case Type_Slice: + ir_fprintf(f, "{"); + ir_print_type(f, m, t->Slice.elem); + ir_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) { + ir_fprintf(f, "<"); + } + ir_fprintf(f, "{"); + for (isize i = 0; i < t->Record.field_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t->Record.fields[i]->type); + } + ir_fprintf(f, "}"); + if (t->Record.struct_is_packed) { + ir_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); + ir_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); + ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union); + } break; + case TypeRecord_Enum: + ir_print_type(f, m, base_enum_type(t)); + 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)); + ir_print_encoded_local(f, *name); + } else { + ir_print_type(f, m, base_type(t)); + } + break; + case Type_Tuple: + if (t->Tuple.variable_count == 1) { + ir_print_type(f, m, t->Tuple.variables[0]->type); + } else { + ir_fprintf(f, "{"); + for (isize i = 0; i < t->Tuple.variable_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t->Tuple.variables[i]->type); + } + ir_fprintf(f, "}"); + } + break; + case Type_Proc: { + if (t->Proc.result_count == 0) { + ir_fprintf(f, "void"); + } else { + ir_print_type(f, m, t->Proc.results); + } + ir_fprintf(f, " ("); + TypeTuple *params = &t->Proc.params->Tuple; + for (isize i = 0; i < t->Proc.param_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, params->variables[i]->type); + } + ir_fprintf(f, ")*"); + } break; + } +} + +void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type); + +void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type *elem_type) { + ir_print_type(f, m, elem_type); + ir_fprintf(f, " "); + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + Type *t = base_type(elem_type)->Maybe.elem; + ir_fprintf(f, "{"); + ir_print_type(f, m, t); + ir_fprintf(f, " "); + } + + if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) { + ir_fprintf(f, "zeroinitializer"); + } else { + ir_print_exact_value(f, m, v, elem_type); + } + + if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { + ir_fprintf(f, ", "); + ir_print_type(f, m, t_bool); + ir_fprintf(f, " "); + ir_fprintf(f, "true}"); + } +} + +void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) { + type = base_type(base_enum_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: + ir_fprintf(f, "%s", (value.value_bool ? "true" : "false")); + break; + case ExactValue_String: { + String str = value.value_string; + if (str.len == 0) { + ir_fprintf(f, "zeroinitializer"); + break; + } + if (!is_type_string(type)) { + GB_ASSERT(is_type_array(type)); + ir_fprintf(f, "c\""); + ir_print_escape_string(f, str, false); + ir_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 + irValue *str_array = ir_add_global_string_array(m, str); + + ir_fprintf(f, "{i8* getelementptr inbounds ("); + ir_print_type(f, m, str_array->Global.entity->type); + ir_fprintf(f, ", "); + ir_print_type(f, m, str_array->Global.entity->type); + ir_fprintf(f, "* "); + ir_print_encoded_global(f, str_array->Global.entity->token.string, false); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " 0, i32 0), "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " %lld}", cast(i64)str.len); + } + } break; + case ExactValue_Integer: { + if (is_type_pointer(type)) { + if (value.value_integer == 0) { + ir_fprintf(f, "null"); + } else { + ir_fprintf(f, "inttoptr ("); + ir_print_type(f, m, t_int); + ir_fprintf(f, " %llu to ", value.value_integer); + ir_print_type(f, m, t_rawptr); + ir_fprintf(f, ")"); + } + } else { + ir_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 mantiir: 52 bits + // 32 bit mantiir: 23 bits + // 29 == 52-23 + u >>= 29; + u <<= 29; + break; + } + + switch (type->Basic.kind) { + case 0: break; +#if 0 + case Basic_f16: + ir_fprintf(f, "bitcast ("); + ir_print_type(f, m, t_u16); + ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float)); + ir_print_type(f, m, t_f16); + ir_fprintf(f, ")"); + break; + case Basic_f128: + ir_fprintf(f, "bitcast ("); + ir_fprintf(f, "i128"); + // TODO(bill): Actually support f128 + ir_fprintf(f, " %llu to ", u); + ir_print_type(f, m, t_f128); + ir_fprintf(f, ")"); + break; +#endif + default: + ir_fprintf(f, "0x%016llx", u); + break; + } + } break; + case ExactValue_Pointer: + if (value.value_pointer == 0) { + ir_fprintf(f, "null"); + } else { + ir_fprintf(f, "inttoptr ("); + ir_print_type(f, m, t_int); + ir_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer); + ir_print_type(f, m, t_rawptr); + ir_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) { + ir_fprintf(f, "zeroinitializer"); + break; + } + + ir_fprintf(f, "["); + Type *elem_type = type->Array.elem; + + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ir_print_compound_element(f, m, tav->value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, elem_type); + ir_fprintf(f, " zeroinitializer"); + } + + ir_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) { + ir_fprintf(f, "zeroinitializer"); + break; + } + + ir_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) { + ir_fprintf(f, ", "); + } + ir_print_compound_element(f, m, tav->value, elem_type); + } + } else { + for (isize i = 0; i < elem_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); + GB_ASSERT(tav != NULL); + ir_print_compound_element(f, m, tav->value, elem_type); + } + } + + ir_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) { + ir_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) { + ir_fprintf(f, "<"); + } + ir_fprintf(f, "{"); + + + for (isize i = 0; i < value_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + Type *elem_type = type->Record.fields[i]->type; + + ir_print_compound_element(f, m, values[i], elem_type); + } + + + ir_fprintf(f, "}"); + if (type->Record.struct_is_packed) { + ir_fprintf(f, ">"); + } + + gb_temp_arena_memory_end(tmp); + } else { + ir_fprintf(f, "zeroinitializer"); + } + + } break; + + default: + ir_fprintf(f, "zeroinitializer"); + // GB_PANIC("Invalid ExactValue: %d", value.kind); + break; + } +} + +void ir_print_block_name(irFileBuffer *f, irBlock *b) { + if (b != NULL) { + ir_print_escape_string(f, b->label, false); + ir_fprintf(f, "-%td", b->index); + } else { + ir_fprintf(f, "<INVALID-BLOCK>"); + } +} + +bool ir_print_is_proc_global(irModule *m, irProcedure *proc) { + if (proc->entity != NULL && + proc->entity->kind == Entity_Procedure) { + if (m->entry_point_entity == proc->entity) { + // TODO(bill): This may not be needed during windows + return true; + } + if (proc->entity->Procedure.link_name.len > 0) { + return true; + } + } + return (proc->tags & (ProcTag_foreign|ProcTag_export)) != 0; +} + +void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint) { + if (value == NULL) { + ir_fprintf(f, "!!!NULL_VALUE"); + return; + } + switch (value->kind) { + default: GB_PANIC("Unknown irValue kind"); break; + + case irValue_Constant: + ir_print_exact_value(f, m, value->Constant.value, type_hint); + break; + + case irValue_ConstantSlice: { + irValueConstantSlice *cs = &value->ConstantSlice; + if (cs->backing_array == NULL || cs->count == 0) { + ir_fprintf(f, "zeroinitializer"); + } else { + Type *at = base_type(type_deref(ir_type(cs->backing_array))); + Type *et = at->Array.elem; + ir_fprintf(f, "{"); + ir_print_type(f, m, et); + ir_fprintf(f, "* getelementptr inbounds ("); + ir_print_type(f, m, at); + ir_fprintf(f, ", "); + ir_print_type(f, m, at); + ir_fprintf(f, "* "); + ir_print_value(f, m, cs->backing_array, at); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " 0, i32 0), "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " %lld, ", cs->count); + ir_print_type(f, m, t_int); + ir_fprintf(f, " %lld}", cs->count); + } + } break; + + case irValue_Nil: + ir_fprintf(f, "zeroinitializer"); + break; + + case irValue_TypeName: + ir_print_encoded_local(f, value->TypeName.name); + break; + case irValue_Global: { + Scope *scope = value->Global.entity->scope; + bool in_global_scope = false; + if (scope != NULL) { + in_global_scope = scope->is_global || scope->is_init; + } + ir_print_encoded_global(f, value->Global.entity->token.string, in_global_scope); + } break; + case irValue_Param: + ir_print_encoded_local(f, value->Param.entity->token.string); + break; + case irValue_Proc: + ir_print_encoded_global(f, value->Proc.name, ir_print_is_proc_global(m, &value->Proc)); + break; + case irValue_Instr: + ir_fprintf(f, "%%%d", value->index); + break; + } +} + +void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConvention cc) { + switch (cc) { + case ProcCC_Odin: ir_fprintf(f, ""); break; + case ProcCC_C: ir_fprintf(f, "ccc "); break; + case ProcCC_Std: ir_fprintf(f, "cc 64 "); break; + case ProcCC_Fast: ir_fprintf(f, "cc 65 "); break; + default: GB_PANIC("unknown calling convention: %d", cc); + } +} + +void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { + GB_ASSERT(value->kind == irValue_Instr); + irInstr *instr = &value->Instr; + + ir_fprintf(f, "\t"); + + switch (instr->kind) { + case irInstr_StartupRuntime: { + ir_fprintf(f, "call void "); + ir_print_encoded_global(f, str_lit(IR_STARTUP_RUNTIME_PROC_NAME), false); + ir_fprintf(f, "()\n"); + } break; + + case irInstr_Comment: + ir_fprintf(f, "; %.*s\n", LIT(instr->Comment.text)); + break; + + case irInstr_Local: { + Type *type = instr->Local.entity->type; + ir_fprintf(f, "%%%d = alloca ", value->index); + ir_print_type(f, m, type); + ir_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case irInstr_ZeroInit: { + Type *type = type_deref(ir_type(instr->ZeroInit.address)); + ir_fprintf(f, "store "); + ir_print_type(f, m, type); + ir_fprintf(f, " zeroinitializer, "); + ir_print_type(f, m, type); + ir_fprintf(f, "* %%%d\n", instr->ZeroInit.address->index); + } break; + + case irInstr_Store: { + Type *type = ir_type(instr->Store.value); + ir_fprintf(f, "store "); + ir_print_type(f, m, type); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->Store.value, type); + ir_fprintf(f, ", "); + ir_print_type(f, m, type); + ir_fprintf(f, "* "); + ir_print_value(f, m, instr->Store.address, type); + ir_fprintf(f, "\n"); + } break; + + case irInstr_Load: { + Type *type = instr->Load.type; + ir_fprintf(f, "%%%d = load ", value->index); + ir_print_type(f, m, type); + ir_fprintf(f, ", "); + ir_print_type(f, m, type); + ir_fprintf(f, "* "); + ir_print_value(f, m, instr->Load.address, type); + ir_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); + } break; + + case irInstr_ArrayElementPtr: { + Type *et = ir_type(instr->ArrayElementPtr.address); + ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ir_print_type(f, m, type_deref(et)); + ir_fprintf(f, ", "); + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->ArrayElementPtr.address, et); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " 0, "); + + irValue *index =instr->ArrayElementPtr.elem_index; + Type *t = ir_type(index); + ir_print_type(f, m, t); + ir_fprintf(f, " "); + ir_print_value(f, m, index, t); + ir_fprintf(f, "\n"); + } break; + + case irInstr_StructElementPtr: { + Type *et = ir_type(instr->StructElementPtr.address); + ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ir_print_type(f, m, type_deref(et)); + ir_fprintf(f, ", "); + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->StructElementPtr.address, et); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " 0, "); + ir_print_type(f, m, t_i32); + ir_fprintf(f, " %d", instr->StructElementPtr.elem_index); + ir_fprintf(f, "\n"); + } break; + + case irInstr_PtrOffset: { + Type *pt = ir_type(instr->PtrOffset.address); + ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + ir_print_type(f, m, type_deref(pt)); + ir_fprintf(f, ", "); + ir_print_type(f, m, pt); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->PtrOffset.address, pt); + + irValue *offset = instr->PtrOffset.offset; + Type *t = ir_type(offset); + ir_fprintf(f, ", "); + ir_print_type(f, m, t); + ir_fprintf(f, " "); + ir_print_value(f, m, offset, t); + ir_fprintf(f, "\n"); + } break; + + case irInstr_Phi: { + ir_fprintf(f, "%%%d = phi ", value->index); + ir_print_type(f, m, instr->Phi.type); + ir_fprintf(f, " ", value->index); + + for (isize i = 0; i < instr->Phi.edges.count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + + irValue *edge = instr->Phi.edges.e[i]; + irBlock *block = NULL; + if (instr->parent != NULL && + i < instr->parent->preds.count) { + block = instr->parent->preds.e[i]; + } + + ir_fprintf(f, "[ "); + ir_print_value(f, m, edge, instr->Phi.type); + ir_fprintf(f, ", %%"); + ir_print_block_name(f, block); + ir_fprintf(f, " ]"); + } + ir_fprintf(f, "\n"); + } break; + + case irInstr_ArrayExtractValue: { + Type *et = ir_type(instr->ArrayExtractValue.address); + ir_fprintf(f, "%%%d = extractvalue ", value->index); + + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->ArrayExtractValue.address, et); + ir_fprintf(f, ", %d\n", instr->ArrayExtractValue.index); + } break; + + case irInstr_StructExtractValue: { + Type *et = ir_type(instr->StructExtractValue.address); + ir_fprintf(f, "%%%d = extractvalue ", value->index); + + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->StructExtractValue.address, et); + ir_fprintf(f, ", %d\n", instr->StructExtractValue.index); + } break; + + case irInstr_UnionTagPtr: { + Type *et = ir_type(instr->UnionTagPtr.address); + ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); + + ir_print_type(f, m, type_deref(et)); + ir_fprintf(f, ", "); + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->UnionTagPtr.address, et); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_int); + ir_fprintf(f, " 0, "); + ir_print_type(f, m, t_i32); + ir_fprintf(f, " %d", 2); + ir_fprintf(f, "\n"); + } break; + + case irInstr_UnionTagValue: { + Type *et = ir_type(instr->UnionTagValue.address); + ir_fprintf(f, "%%%d = extractvalue ", value->index); + + ir_print_type(f, m, et); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->UnionTagValue.address, et); + ir_fprintf(f, ", %d\n", 2); + } break; + + case irInstr_Jump: {; + ir_fprintf(f, "br label %%"); + ir_print_block_name(f, instr->Jump.block); + ir_fprintf(f, "\n"); + } break; + + case irInstr_If: {; + ir_fprintf(f, "br "); + ir_print_type(f, m, t_bool); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->If.cond, t_bool); + ir_fprintf(f, ", ", instr->If.cond->index); + ir_fprintf(f, "label %%"); ir_print_block_name(f, instr->If.true_block); + ir_fprintf(f, ", label %%"); ir_print_block_name(f, instr->If.false_block); + ir_fprintf(f, "\n"); + } break; + + case irInstr_Return: { + irInstrReturn *ret = &instr->Return; + ir_fprintf(f, "ret "); + if (ret->value == NULL) { + ir_fprintf(f, "void"); + } else { + Type *t = ir_type(ret->value); + ir_print_type(f, m, t); + ir_fprintf(f, " "); + ir_print_value(f, m, ret->value, t); + } + + ir_fprintf(f, "\n"); + + } break; + + case irInstr_Conv: { + irInstrConv *c = &instr->Conv; + ir_fprintf(f, "%%%d = %.*s ", value->index, LIT(ir_conv_strings[c->kind])); + ir_print_type(f, m, c->from); + ir_fprintf(f, " "); + ir_print_value(f, m, c->value, c->from); + ir_fprintf(f, " to "); + ir_print_type(f, m, c->to); + ir_fprintf(f, "\n"); + + } break; + + case irInstr_Unreachable: { + ir_fprintf(f, "unreachable\n"); + } break; + + case irInstr_UnaryOp: { + irInstrUnaryOp *uo = &value->Instr.UnaryOp; + Type *type = base_type(ir_type(uo->expr)); + Type *elem_type = type; + while (elem_type->kind == Type_Vector) { + elem_type = base_type(elem_type->Vector.elem); + } + + ir_fprintf(f, "%%%d = ", value->index); + switch (uo->op) { + case Token_Sub: + if (is_type_float(elem_type)) { + ir_fprintf(f, "fsub"); + } else { + ir_fprintf(f, "sub"); + } + break; + case Token_Xor: + case Token_Not: + GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); + ir_fprintf(f, "xor"); + break; + default: + GB_PANIC("Unknown unary operator"); + break; + } + + ir_fprintf(f, " "); + ir_print_type(f, m, type); + ir_fprintf(f, " "); + switch (uo->op) { + case Token_Sub: + if (is_type_float(elem_type)) { + ir_print_exact_value(f, m, make_exact_value_float(0), type); + } else { + ir_fprintf(f, "0"); + } + break; + case Token_Xor: + case Token_Not: + GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); + ir_fprintf(f, "-1"); + break; + } + ir_fprintf(f, ", "); + ir_print_value(f, m, uo->expr, type); + ir_fprintf(f, "\n"); + } break; + + case irInstr_BinaryOp: { + irInstrBinaryOp *bo = &value->Instr.BinaryOp; + Type *type = base_type(ir_type(bo->left)); + Type *elem_type = type; + while (elem_type->kind == Type_Vector) { + elem_type = base_type(elem_type->Vector.elem); + } + + ir_fprintf(f, "%%%d = ", value->index); + + if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { + if (is_type_string(elem_type)) { + ir_fprintf(f, "call "); + ir_print_calling_convention(f, m, ProcCC_Odin); + ir_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; + } + + ir_fprintf(f, " "); + ir_print_encoded_global(f, make_string_c(runtime_proc), false); + ir_fprintf(f, "("); + ir_print_type(f, m, type); + ir_fprintf(f, " "); + ir_print_value(f, m, bo->left, type); + ir_fprintf(f, ", "); + ir_print_type(f, m, type); + ir_fprintf(f, " "); + ir_print_value(f, m, bo->right, type); + ir_fprintf(f, ")\n"); + return; + + } else if (is_type_float(elem_type)) { + ir_fprintf(f, "fcmp "); + switch (bo->op) { + case Token_CmpEq: ir_fprintf(f, "oeq"); break; + case Token_NotEq: ir_fprintf(f, "one"); break; + case Token_Lt: ir_fprintf(f, "olt"); break; + case Token_Gt: ir_fprintf(f, "ogt"); break; + case Token_LtEq: ir_fprintf(f, "ole"); break; + case Token_GtEq: ir_fprintf(f, "oge"); break; + } + } else { + ir_fprintf(f, "icmp "); + if (bo->op != Token_CmpEq && + bo->op != Token_NotEq) { + if (is_type_unsigned(elem_type)) { + ir_fprintf(f, "u"); + } else { + ir_fprintf(f, "s"); + } + } + switch (bo->op) { + case Token_CmpEq: ir_fprintf(f, "eq"); break; + case Token_NotEq: ir_fprintf(f, "ne"); break; + case Token_Lt: ir_fprintf(f, "lt"); break; + case Token_Gt: ir_fprintf(f, "gt"); break; + case Token_LtEq: ir_fprintf(f, "le"); break; + case Token_GtEq: ir_fprintf(f, "ge"); break; + default: GB_PANIC("invalid comparison");break; + } + } + } else { + if (is_type_float(elem_type)) { + ir_fprintf(f, "f"); + } + + switch (bo->op) { + case Token_Add: ir_fprintf(f, "add"); break; + case Token_Sub: ir_fprintf(f, "sub"); break; + case Token_And: ir_fprintf(f, "and"); break; + case Token_Or: ir_fprintf(f, "or"); break; + case Token_Xor: ir_fprintf(f, "xor"); break; + case Token_Shl: ir_fprintf(f, "shl"); break; + case Token_Shr: ir_fprintf(f, "lshr"); break; + case Token_Mul: ir_fprintf(f, "mul"); break; + case Token_Not: ir_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)) { + ir_fprintf(f, "u"); + } else { + ir_fprintf(f, "s"); + } + } + + switch (bo->op) { + case Token_Quo: ir_fprintf(f, "div"); break; + case Token_Mod: ir_fprintf(f, "rem"); break; + } + } break; + } + } + + ir_fprintf(f, " "); + ir_print_type(f, m, type); + ir_fprintf(f, " "); + ir_print_value(f, m, bo->left, type); + ir_fprintf(f, ", "); + ir_print_value(f, m, bo->right, type); + ir_fprintf(f, "\n"); + } break; + + case irInstr_Call: { + irInstrCall *call = &instr->Call; + Type *proc_type = base_type(ir_type(call->value)); + GB_ASSERT(is_type_proc(proc_type)); + Type *result_type = call->type; + if (result_type) { + ir_fprintf(f, "%%%d = ", value->index); + } + ir_fprintf(f, "call "); + ir_print_calling_convention(f, m, proc_type->Proc.calling_convention); + if (result_type) { + ir_print_type(f, m, result_type); + } else { + ir_fprintf(f, "void"); + } + ir_fprintf(f, " "); + ir_print_value(f, m, call->value, call->type); + + + ir_fprintf(f, "("); + if (call->arg_count > 0) { + Type *proc_type = base_type(ir_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) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t); + ir_fprintf(f, " "); + irValue *arg = call->args[i]; + ir_print_value(f, m, arg, t); + } + } + ir_fprintf(f, ")\n"); + + } break; + + case irInstr_Select: { + ir_fprintf(f, "%%%d = select i1 ", value->index); + ir_print_value(f, m, instr->Select.cond, t_bool); + ir_fprintf(f, ", "); + ir_print_type(f, m, ir_type(instr->Select.true_value)); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->Select.true_value, ir_type(instr->Select.true_value)); + ir_fprintf(f, ", "); + ir_print_type(f, m, ir_type(instr->Select.false_value)); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->Select.false_value, ir_type(instr->Select.false_value)); + ir_fprintf(f, "\n"); + } break; + + case irInstr_VectorExtractElement: { + Type *vt = ir_type(instr->VectorExtractElement.vector); + Type *it = ir_type(instr->VectorExtractElement.index); + ir_fprintf(f, "%%%d = extractelement ", value->index); + + ir_print_type(f, m, vt); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->VectorExtractElement.vector, vt); + ir_fprintf(f, ", "); + ir_print_type(f, m, it); + ir_fprintf(f, " "); + ir_print_value(f, m, instr->VectorExtractElement.index, it); + ir_fprintf(f, "\n"); + } break; + + case irInstr_VectorInsertElement: { + irInstrVectorInsertElement *ie = &instr->VectorInsertElement; + Type *vt = ir_type(ie->vector); + ir_fprintf(f, "%%%d = insertelement ", value->index); + + ir_print_type(f, m, vt); + ir_fprintf(f, " "); + ir_print_value(f, m, ie->vector, vt); + ir_fprintf(f, ", "); + + ir_print_type(f, m, ir_type(ie->elem)); + ir_fprintf(f, " "); + ir_print_value(f, m, ie->elem, ir_type(ie->elem)); + ir_fprintf(f, ", "); + + ir_print_type(f, m, ir_type(ie->index)); + ir_fprintf(f, " "); + ir_print_value(f, m, ie->index, ir_type(ie->index)); + + ir_fprintf(f, "\n"); + } break; + + case irInstr_VectorShuffle: { + irInstrVectorShuffle *sv = &instr->VectorShuffle; + Type *vt = ir_type(sv->vector); + ir_fprintf(f, "%%%d = shufflevector ", value->index); + + ir_print_type(f, m, vt); + ir_fprintf(f, " "); + ir_print_value(f, m, sv->vector, vt); + ir_fprintf(f, ", "); + + ir_print_type(f, m, vt); + ir_fprintf(f, " "); + ir_print_value(f, m, sv->vector, vt); + ir_fprintf(f, ", "); + + ir_fprintf(f, "<%td x i32> <", sv->index_count); + for (isize i = 0; i < sv->index_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + ir_fprintf(f, "i32 %d", sv->indices[i]); + } + ir_fprintf(f, ">"); + ir_fprintf(f, "\n"); + } break; + + case irInstr_BoundsCheck: { + irInstrBoundsCheck *bc = &instr->BoundsCheck; + ir_fprintf(f, "call void "); + ir_print_encoded_global(f, str_lit("__bounds_check_error"), false); + ir_fprintf(f, "("); + ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_value(f, m, bc->index, t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_value(f, m, bc->len, t_int); + + ir_fprintf(f, ")\n"); + } break; + + case irInstr_SliceBoundsCheck: { + irInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; + ir_fprintf(f, "call void "); + if (bc->is_substring) { + ir_print_encoded_global(f, str_lit("__substring_expr_error"), false); + } else { + ir_print_encoded_global(f, str_lit("__slice_expr_error"), false); + } + + ir_fprintf(f, "("); + ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_value(f, m, bc->low, t_int); + ir_fprintf(f, ", "); + + ir_print_type(f, m, t_int); + ir_fprintf(f, " "); + ir_print_value(f, m, bc->high, t_int); + + ir_fprintf(f, ")\n"); + } break; + + + default: { + GB_PANIC("<unknown instr> %d\n", instr->kind); + ir_fprintf(f, "; <unknown instr> %d\n", instr->kind); + } break; + } +} + + +void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { + if (proc->body == NULL) { + ir_fprintf(f, "declare "); + if (proc->tags & ProcTag_dll_import) { + ir_fprintf(f, "dllimport "); + } + } else { + ir_fprintf(f, "\n"); + ir_fprintf(f, "define "); + if (m->build_context->is_dll) { + // if (proc->tags & (ProcTag_export|ProcTag_dll_export)) { + if (proc->tags & (ProcTag_export)) { + ir_fprintf(f, "dllexport "); + } + } + } + + TypeProc *proc_type = &proc->type->Proc; + + ir_print_calling_convention(f, m, proc_type->calling_convention); + + if (proc_type->result_count == 0) { + ir_fprintf(f, "void"); + } else { + ir_print_type(f, m, proc_type->results); + } + + ir_fprintf(f, " "); + ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc)); + ir_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) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, e->type); + if (proc->body != NULL) { + if (!str_eq(e->token.string, str_lit("")) && + !str_eq(e->token.string, str_lit("_"))) { + ir_fprintf(f, " %%%.*s", LIT(e->token.string)); + } else { + ir_fprintf(f, " %%_.param_%td", i); + } + } + } + } + + ir_fprintf(f, ") "); + + if (proc->tags & ProcTag_inline) { + ir_fprintf(f, "alwaysinline "); + } + if (proc->tags & ProcTag_no_inline) { + ir_fprintf(f, "noinline "); + } + + + if (proc->module->generate_debug_info && proc->entity != NULL) { + if (proc->body != NULL) { + irDebugInfo *di = *map_ir_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); + GB_ASSERT(di->kind == irDebugInfo_Proc); + ir_fprintf(f, "!dbg !%d ", di->id); + } + } + + + if (proc->body != NULL) { + // ir_fprintf(f, "nounwind uwtable {\n"); + + ir_fprintf(f, "{\n"); + for_array(i, proc->blocks) { + irBlock *block = proc->blocks.e[i]; + + if (i > 0) ir_fprintf(f, "\n"); + ir_print_block_name(f, block); + ir_fprintf(f, ":\n"); + + for_array(j, block->instrs) { + irValue *value = block->instrs.e[j]; + ir_print_instr(f, m, value); + } + } + ir_fprintf(f, "}\n"); + } else { + ir_fprintf(f, "\n"); + } + + for_array(i, proc->children) { + ir_print_proc(f, m, proc->children.e[i]); + } +} + +void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) { + GB_ASSERT(v->kind == irValue_TypeName); + Type *bt = base_type(ir_type(v)); + if (!is_type_struct(bt) && !is_type_union(bt)) { + return; + } + ir_print_encoded_local(f, v->TypeName.name); + ir_fprintf(f, " = type "); + ir_print_type(f, m, base_type(v->TypeName.type)); + ir_fprintf(f, "\n"); +} + +void print_llvm_ir(irGen *ir) { + irModule *m = &ir->module; + irFileBuffer buf = {0}, *f = &buf; + ir_file_buffer_init(f, &ir->output_file); + + if (m->layout.len > 0) { + ir_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); + } + + ir_print_encoded_local(f, str_lit("..string")); + ir_fprintf(f, " = type {i8*, "); + ir_print_type(f, m, t_int); + ir_fprintf(f, "} ; Basic_string\n"); + ir_print_encoded_local(f, str_lit("..rawptr")); + ir_fprintf(f, " = type i8* ; Basic_rawptr\n"); + + ir_print_encoded_local(f, str_lit("..any")); + ir_fprintf(f, " = type {"); + ir_print_type(f, m, t_type_info_ptr); + ir_fprintf(f, ", "); + ir_print_type(f, m, t_rawptr); + ir_fprintf(f, "} ; Basic_any\n"); + + + for_array(member_index, m->members.entries) { + MapIrValueEntry *entry = &m->members.entries.e[member_index]; + irValue *v = entry->value; + if (v->kind != irValue_TypeName) { + continue; + } + ir_print_type_name(f, m, v); + } + + ir_fprintf(f, "\n"); + + bool dll_main_found = false; + + for_array(member_index, m->members.entries) { + MapIrValueEntry *entry = &m->members.entries.e[member_index]; + irValue *v = entry->value; + if (v->kind != irValue_Proc) { + continue; + } + + if (v->Proc.body == NULL) { + ir_print_proc(f, m, &v->Proc); + } + } + + for_array(member_index, m->members.entries) { + MapIrValueEntry *entry = &m->members.entries.e[member_index]; + irValue *v = entry->value; + if (v->kind != irValue_Proc) { + continue; + } + + if (v->Proc.body != NULL) { + ir_print_proc(f, m, &v->Proc); + } + } + + for_array(member_index, m->members.entries) { + MapIrValueEntry *entry = &m->members.entries.e[member_index]; + irValue *v = entry->value; + if (v->kind != irValue_Global) { + continue; + } + irValueGlobal *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; + } + ir_print_encoded_global(f, g->entity->token.string, in_global_scope); + ir_fprintf(f, " = "); + if (g->is_thread_local) { + ir_fprintf(f, "thread_local "); + } + + if (g->is_private) { + ir_fprintf(f, "private "); + } + if (g->is_constant) { + if (g->is_unnamed_addr) { + ir_fprintf(f, "unnamed_addr "); + } + ir_fprintf(f, "constant "); + } else { + ir_fprintf(f, "global "); + } + + + ir_print_type(f, m, g->entity->type); + ir_fprintf(f, " "); + if (g->value != NULL) { + ir_print_value(f, m, g->value, g->entity->type); + } else { + ir_fprintf(f, "zeroinitializer"); + } + ir_fprintf(f, "\n"); + } + + +#if 0 + if (m->generate_debug_info) { + ir_fprintf(f, "\n"); + ir_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); + + for_array(di_index, m->debug_info.entries) { + MapIrDebugInfoEntry *entry = &m->debug_info.entries.e[di_index]; + irDebugInfo *di = entry->value; + ir_fprintf(f, "!%d = ", di->id); + + switch (di->kind) { + case irDebugInfo_CompileUnit: { + auto *cu = &di->CompileUnit; + irDebugInfo *file = *map_ir_debug_info_get(&m->debug_info, hash_pointer(cu->file)); + ir_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 irDebugInfo_File: + ir_fprintf(f, "!DIFile(filename: \""); + ir_print_escape_string(f, di->File.filename, false); + ir_fprintf(f, "\", directory: \""); + ir_print_escape_string(f, di->File.directory, false); + ir_fprintf(f, "\")"); + break; + case irDebugInfo_Proc: + ir_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 irDebugInfo_AllProcs: + ir_fprintf(f, "!{"); + for_array(proc_index, di->AllProcs.procs) { + irDebugInfo *p = di->AllProcs.procs.e[proc_index]; + if (proc_index > 0) {ir_fprintf(f, ",");} + ir_fprintf(f, "!%d", p->id); + } + ir_fprintf(f, "}"); + break; + } + + ir_fprintf(f, "\n"); + } + } +#endif + ir_file_buffer_destroy(f); +} diff --git a/src/llir.c b/src/llir.c deleted file mode 100644 index b603f164e..000000000 --- a/src/llir.c +++ /dev/null @@ -1,5794 +0,0 @@ -typedef struct llirProcedure llirProcedure; -typedef struct llirBlock llirBlock; -typedef struct llirValue llirValue; -typedef struct llirDebugInfo llirDebugInfo; - -typedef Array(llirValue *) llirValueArray; - -#define MAP_TYPE llirValue * -#define MAP_PROC map_llir_value_ -#define MAP_NAME MapSsaValue -#include "map.c" - -#define MAP_TYPE llirDebugInfo * -#define MAP_PROC map_llir_debug_info_ -#define MAP_NAME MapSsaDebugInfo -#include "map.c" - -typedef struct llirModule { - CheckerInfo * info; - BuildContext *build_context; - 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 - - Entity * entry_point_entity; - - Array(llirProcedure *) procs; // NOTE(bill): All procedures with bodies - llirValueArray procs_to_generate; // NOTE(bill): Procedures to generate -} llirModule; - -// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) -typedef struct llirDomNode { - llirBlock * idom; // Parent (Immediate Dominator) - Array(llirBlock *) children; - i32 pre, post; // Ordering in tree -} llirDomNode; - - -typedef struct llirBlock { - i32 index; - String label; - llirProcedure *parent; - AstNode * node; // Can be NULL - Scope * scope; - isize scope_index; - llirDomNode dom; - i32 gaps; - - llirValueArray instrs; - llirValueArray locals; - - Array(llirBlock *) preds; - Array(llirBlock *) succs; -} llirBlock; - -typedef struct llirTargetList llirTargetList; -struct llirTargetList { - llirTargetList *prev; - llirBlock * break_; - llirBlock * continue_; - llirBlock * fallthrough_; -}; - -typedef enum llirDeferExitKind { - llirDeferExit_Default, - llirDeferExit_Return, - llirDeferExit_Branch, -} llirDeferExitKind; -typedef enum llirDeferKind { - llirDefer_Node, - llirDefer_Instr, -} llirDeferKind; - -typedef struct llirDefer { - llirDeferKind kind; - isize scope_index; - llirBlock * block; - union { - AstNode *stmt; - // NOTE(bill): `instr` will be copied every time to create a new one - llirValue *instr; - }; -} llirDefer; - -struct llirProcedure { - llirProcedure * parent; - Array(llirProcedure *) children; - - Entity * entity; - llirModule * module; - String name; - Type * type; - AstNode * type_expr; - AstNode * body; - u64 tags; - - llirValueArray params; - Array(llirDefer) defer_stmts; - Array(llirBlock *) blocks; - i32 scope_index; - llirBlock * decl_block; - llirBlock * entry_block; - llirBlock * curr_block; - llirTargetList * target_list; - llirValueArray referrers; - - i32 local_count; - i32 instr_count; - i32 block_count; -}; - -#define LLIR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" -#define LLIR_TYPE_INFO_DATA_NAME "__$type_info_data" -#define LLIR_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" - - -#define LLIR_INSTR_KINDS \ - LLIR_INSTR_KIND(Comment, struct { String text; }) \ - LLIR_INSTR_KIND(Local, struct { \ - Entity * entity; \ - Type * type; \ - bool zero_initialized; \ - llirValueArray referrers; \ - }) \ - LLIR_INSTR_KIND(ZeroInit, struct { llirValue *address; }) \ - LLIR_INSTR_KIND(Store, struct { llirValue *address, *value; }) \ - LLIR_INSTR_KIND(Load, struct { Type *type; llirValue *address; }) \ - LLIR_INSTR_KIND(PtrOffset, struct { \ - llirValue *address; \ - llirValue *offset; \ - }) \ - LLIR_INSTR_KIND(ArrayElementPtr, struct { \ - llirValue *address; \ - Type * result_type; \ - llirValue *elem_index; \ - }) \ - LLIR_INSTR_KIND(StructElementPtr, struct { \ - llirValue *address; \ - Type * result_type; \ - i32 elem_index; \ - }) \ - LLIR_INSTR_KIND(ArrayExtractValue, struct { \ - llirValue *address; \ - Type * result_type; \ - i32 index; \ - }) \ - LLIR_INSTR_KIND(StructExtractValue, struct { \ - llirValue *address; \ - Type * result_type; \ - i32 index; \ - }) \ - LLIR_INSTR_KIND(UnionTagPtr, struct { \ - llirValue *address; \ - Type *type; /* ^int */ \ - }) \ - LLIR_INSTR_KIND(UnionTagValue, struct { \ - llirValue *address; \ - Type *type; /* int */ \ - }) \ - LLIR_INSTR_KIND(Conv, struct { \ - llirConvKind kind; \ - llirValue *value; \ - Type *from, *to; \ - }) \ - LLIR_INSTR_KIND(Jump, struct { llirBlock *block; }) \ - LLIR_INSTR_KIND(If, struct { \ - llirValue *cond; \ - llirBlock *true_block; \ - llirBlock *false_block; \ - }) \ - LLIR_INSTR_KIND(Return, struct { llirValue *value; }) \ - LLIR_INSTR_KIND(Select, struct { \ - llirValue *cond; \ - llirValue *true_value; \ - llirValue *false_value; \ - }) \ - LLIR_INSTR_KIND(Phi, struct { llirValueArray edges; Type *type; }) \ - LLIR_INSTR_KIND(Unreachable, i32) \ - LLIR_INSTR_KIND(UnaryOp, struct { \ - Type * type; \ - TokenKind op; \ - llirValue *expr; \ - }) \ - LLIR_INSTR_KIND(BinaryOp, struct { \ - Type * type; \ - TokenKind op; \ - llirValue *left, *right; \ - }) \ - LLIR_INSTR_KIND(Call, struct { \ - Type * type; /* return type */ \ - llirValue *value; \ - llirValue **args; \ - isize arg_count; \ - }) \ - LLIR_INSTR_KIND(VectorExtractElement, struct { \ - llirValue *vector; \ - llirValue *index; \ - }) \ - LLIR_INSTR_KIND(VectorInsertElement, struct { \ - llirValue *vector; \ - llirValue *elem; \ - llirValue *index; \ - }) \ - LLIR_INSTR_KIND(VectorShuffle, struct { \ - llirValue *vector; \ - i32 * indices; \ - i32 index_count; \ - Type * type; \ - }) \ - LLIR_INSTR_KIND(StartupRuntime, i32) \ - LLIR_INSTR_KIND(BoundsCheck, struct { \ - TokenPos pos; \ - llirValue *index; \ - llirValue *len; \ - }) \ - LLIR_INSTR_KIND(SliceBoundsCheck, struct { \ - TokenPos pos; \ - llirValue *low; \ - llirValue *high; \ - bool is_substring; \ - }) - -#define LLIR_CONV_KINDS \ - LLIR_CONV_KIND(trunc) \ - LLIR_CONV_KIND(zext) \ - LLIR_CONV_KIND(fptrunc) \ - LLIR_CONV_KIND(fpext) \ - LLIR_CONV_KIND(fptoui) \ - LLIR_CONV_KIND(fptosi) \ - LLIR_CONV_KIND(uitofp) \ - LLIR_CONV_KIND(sitofp) \ - LLIR_CONV_KIND(ptrtoint) \ - LLIR_CONV_KIND(inttoptr) \ - LLIR_CONV_KIND(bitcast) - -typedef enum llirInstrKind { - llirInstr_Invalid, -#define LLIR_INSTR_KIND(x, ...) GB_JOIN2(llirInstr_, x), - LLIR_INSTR_KINDS -#undef LLIR_INSTR_KIND -} llirInstrKind; - -String const llir_instr_strings[] = { - {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, -#define LLIR_INSTR_KIND(x, ...) {cast(u8 *)#x, gb_size_of(#x)-1}, - LLIR_INSTR_KINDS -#undef LLIR_INSTR_KIND -}; - -typedef enum llirConvKind { - llirConv_Invalid, -#define LLIR_CONV_KIND(x) GB_JOIN2(llirConv_, x), - LLIR_CONV_KINDS -#undef LLIR_CONV_KIND -} llirConvKind; - -String const llir_conv_strings[] = { - {cast(u8 *)"Invalid", gb_size_of("Invalid")-1}, -#define LLIR_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}, - LLIR_CONV_KINDS -#undef LLIR_CONV_KIND -}; - -#define LLIR_INSTR_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(llirInstr, k); - LLIR_INSTR_KINDS -#undef LLIR_INSTR_KIND - -typedef struct llirInstr llirInstr; -struct llirInstr { - llirInstrKind kind; - - llirBlock *parent; - Type *type; - - union { -#define LLIR_INSTR_KIND(k, ...) GB_JOIN2(llirInstr, k) k; - LLIR_INSTR_KINDS -#undef LLIR_INSTR_KIND - }; -}; - - -typedef enum llirValueKind { - llirValue_Invalid, - - llirValue_Constant, - llirValue_ConstantSlice, - llirValue_Nil, - llirValue_TypeName, - llirValue_Global, - llirValue_Param, - - llirValue_Proc, - llirValue_Block, - llirValue_Instr, - - llirValue_Count, -} llirValueKind; - -typedef struct llirValueConstant { - Type * type; - ExactValue value; -} llirValueConstant; - -typedef struct llirValueConstantSlice { - Type * type; - llirValue *backing_array; - i64 count; -} llirValueConstantSlice; - -typedef struct llirValueNil { - Type *type; -} llirValueNil; - -typedef struct llirValueTypeName { - Type * type; - String name; -} llirValueTypeName; - -typedef struct llirValueGlobal { - Entity * entity; - Type * type; - llirValue * value; - llirValueArray referrers; - bool is_constant; - bool is_private; - bool is_thread_local; - bool is_unnamed_addr; -} llirValueGlobal; - -typedef struct llirValueParam { - llirProcedure *parent; - Entity * entity; - Type * type; - llirValueArray referrers; -} llirValueParam; - -typedef struct llirValue { - llirValueKind kind; - i32 index; - union { - llirValueConstant Constant; - llirValueConstantSlice ConstantSlice; - llirValueNil Nil; - llirValueTypeName TypeName; - llirValueGlobal Global; - llirValueParam Param; - llirProcedure Proc; - llirBlock Block; - llirInstr Instr; - }; -} llirValue; - -gb_global llirValue *v_zero = NULL; -gb_global llirValue *v_one = NULL; -gb_global llirValue *v_zero32 = NULL; -gb_global llirValue *v_one32 = NULL; -gb_global llirValue *v_two32 = NULL; -gb_global llirValue *v_false = NULL; -gb_global llirValue *v_true = NULL; - -typedef enum llirAddrKind { - llirAddr_Default, - llirAddr_Vector, -} llirAddrKind; - -typedef struct llirAddr { - llirValue * addr; - AstNode * expr; // NOTE(bill): Just for testing - probably remove later - llirAddrKind kind; - union { - struct { llirValue *index; } Vector; - }; -} llirAddr; - -llirAddr llir_make_addr(llirValue *addr, AstNode *expr) { - llirAddr v = {addr, expr}; - return v; -} -llirAddr llir_make_addr_vector(llirValue *addr, llirValue *index, AstNode *expr) { - llirAddr v = llir_make_addr(addr, expr); - v.kind = llirAddr_Vector; - v.Vector.index = index; - return v; -} - - - -typedef enum llirDebugEncoding { - llirDebugBasicEncoding_Invalid = 0, - - llirDebugBasicEncoding_address = 1, - llirDebugBasicEncoding_boolean = 2, - llirDebugBasicEncoding_float = 3, - llirDebugBasicEncoding_signed = 4, - llirDebugBasicEncoding_signed_char = 5, - llirDebugBasicEncoding_unsigned = 6, - llirDebugBasicEncoding_unsigned_char = 7, - - llirDebugBasicEncoding_member = 13, - llirDebugBasicEncoding_pointer_type = 15, - llirDebugBasicEncoding_typedef = 22, - - llirDebugBasicEncoding_array_type = 1, - llirDebugBasicEncoding_enumeration_type = 4, - llirDebugBasicEncoding_structure_type = 19, - llirDebugBasicEncoding_union_type = 23, - -} llirDebugEncoding; - -typedef enum llirDebugInfoKind { - llirDebugInfo_Invalid, - - llirDebugInfo_CompileUnit, - llirDebugInfo_File, - llirDebugInfo_Scope, - llirDebugInfo_Proc, - llirDebugInfo_AllProcs, - - llirDebugInfo_BasicType, // basic types - llirDebugInfo_ProcType, - llirDebugInfo_DerivedType, // pointer, typedef - llirDebugInfo_CompositeType, // array, struct, enum, (raw_)union - llirDebugInfo_Enumerator, // For llirDebugInfo_CompositeType if enum - llirDebugInfo_GlobalVariable, - llirDebugInfo_LocalVariable, - - - llirDebugInfo_Count, -} llirDebugInfoKind; - -typedef struct llirDebugInfo llirDebugInfo; -struct llirDebugInfo { - llirDebugInfoKind kind; - i32 id; - - union { - struct { - AstFile * file; - String producer; - llirDebugInfo *all_procs; - } CompileUnit; - struct { - AstFile *file; - String filename; - String directory; - } File; - struct { - llirDebugInfo *parent; - llirDebugInfo *file; - TokenPos pos; - Scope * scope; // Actual scope - } Scope; - struct { - Entity * entity; - String name; - llirDebugInfo *file; - TokenPos pos; - } Proc; - struct { - Array(llirDebugInfo *) procs; - } AllProcs; - - - struct { - String name; - i32 size; - i32 align; - llirDebugEncoding encoding; - } BasicType; - struct { - llirDebugInfo * return_type; - Array(llirDebugInfo *) param_types; - } ProcType; - struct { - llirDebugInfo * base_type; - llirDebugEncoding encoding; - } DerivedType; - struct { - llirDebugEncoding encoding; - String name; - String identifier; - llirDebugInfo * file; - TokenPos pos; - i32 size; - i32 align; - Array(llirDebugInfo *) elements; - } CompositeType; - struct { - String name; - i64 value; - } Enumerator; - struct { - String name; - String linkage_name; - llirDebugInfo *scope; - llirDebugInfo *file; - TokenPos pos; - llirValue *variable; - llirDebugInfo *declaration; - } GlobalVariable; - struct { - String name; - llirDebugInfo *scope; - llirDebugInfo *file; - TokenPos pos; - i32 arg; // Non-zero if proc parameter - llirDebugInfo *type; - } LocalVariable; - }; -}; - -typedef struct llirGen { - llirModule module; - gbFile output_file; - bool opt_called; -} llirGen; - -llirValue *llir_lookup_member(llirModule *m, String name) { - llirValue **v = map_llir_value_get(&m->members, hash_string(name)); - if (v != NULL) { - return *v; - } - return NULL; -} - - -Type *llir_type(llirValue *value); -Type *llir_instr_type(llirInstr *instr) { - switch (instr->kind) { - case llirInstr_Local: - return instr->Local.type; - case llirInstr_Load: - return instr->Load.type; - case llirInstr_StructElementPtr: - return instr->StructElementPtr.result_type; - case llirInstr_ArrayElementPtr: - return instr->ArrayElementPtr.result_type; - case llirInstr_PtrOffset: - return llir_type(instr->PtrOffset.address); - case llirInstr_Phi: - return instr->Phi.type; - case llirInstr_ArrayExtractValue: - return instr->ArrayExtractValue.result_type; - case llirInstr_StructExtractValue: - return instr->StructExtractValue.result_type; - case llirInstr_UnionTagPtr: - return instr->UnionTagPtr.type; - case llirInstr_UnionTagValue: - return instr->UnionTagValue.type; - case llirInstr_UnaryOp: - return instr->UnaryOp.type; - case llirInstr_BinaryOp: - return instr->BinaryOp.type; - case llirInstr_Conv: - return instr->Conv.to; - case llirInstr_Select: - return llir_type(instr->Select.true_value); - case llirInstr_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 llirInstr_VectorExtractElement: { - Type *vt = llir_type(instr->VectorExtractElement.vector); - Type *bt = base_vector_type(vt); - GB_ASSERT(!is_type_vector(bt)); - return bt; - } break; - case llirInstr_VectorInsertElement: - return llir_type(instr->VectorInsertElement.vector); - case llirInstr_VectorShuffle: - return instr->VectorShuffle.type; - } - return NULL; -} - -Type *llir_type(llirValue *value) { - switch (value->kind) { - case llirValue_Constant: - return value->Constant.type; - case llirValue_ConstantSlice: - return value->ConstantSlice.type; - case llirValue_Nil: - return value->Nil.type; - case llirValue_TypeName: - return value->TypeName.type; - case llirValue_Global: - return value->Global.type; - case llirValue_Param: - return value->Param.type; - case llirValue_Proc: - return value->Proc.type; - case llirValue_Instr: - return llir_instr_type(&value->Instr); - } - return NULL; -} - -Type *llir_addr_type(llirAddr lval) { - if (lval.addr != NULL) { - Type *t = llir_type(lval.addr); - GB_ASSERT(is_type_pointer(t)); - return type_deref(t); - } - return NULL; -} - - - -bool llir_is_blank_ident(AstNode *node) { - if (node->kind == AstNode_Ident) { - ast_node(i, Ident, node); - return is_blank_ident(i->string); - } - return false; -} - - -llirInstr *llir_get_last_instr(llirBlock *block) { - if (block != NULL) { - isize len = block->instrs.count; - if (len > 0) { - llirValue *v = block->instrs.e[len-1]; - GB_ASSERT(v->kind == llirValue_Instr); - return &v->Instr; - } - } - return NULL; - -} - -bool llir_is_instr_terminating(llirInstr *i) { - if (i != NULL) { - switch (i->kind) { - case llirInstr_Return: - case llirInstr_Unreachable: - return true; - } - } - - return false; -} - - -void llir_add_edge(llirBlock *from, llirBlock *to) { - array_add(&from->succs, to); - array_add(&to->preds, from); -} - -void llir_set_instr_parent(llirValue *instr, llirBlock *parent) { - if (instr->kind == llirValue_Instr) { - instr->Instr.parent = parent; - } -} - -llirValueArray *llir_value_referrers(llirValue *v) { - switch (v->kind) { - case llirValue_Global: - return &v->Global.referrers; - case llirValue_Param: - return &v->Param.referrers; - case llirValue_Proc: { - if (v->Proc.parent != NULL) { - return &v->Proc.referrers; - } - return NULL; - } - case llirValue_Instr: { - llirInstr *i = &v->Instr; - switch (i->kind) { - case llirInstr_Local: - return &i->Local.referrers; - } - } break; - } - - return NULL; -} - - - -//////////////////////////////////////////////////////////////// -// -// @Make -// -//////////////////////////////////////////////////////////////// - -void llir_module_add_value (llirModule *m, Entity *e, llirValue *v); -llirValue *llir_emit_zero_init (llirProcedure *p, llirValue *address); -llirValue *llir_emit_comment (llirProcedure *p, String text); -llirValue *llir_emit_store (llirProcedure *p, llirValue *address, llirValue *value); -llirValue *llir_emit_load (llirProcedure *p, llirValue *address); -void llir_emit_jump (llirProcedure *proc, llirBlock *block); -llirValue *llir_emit_conv (llirProcedure *proc, llirValue *value, Type *t); -llirValue *llir_type_info (llirProcedure *proc, Type *type); -llirValue *llir_build_expr (llirProcedure *proc, AstNode *expr); -void llir_build_stmt (llirProcedure *proc, AstNode *node); -llirValue *llir_build_cond (llirProcedure *proc, AstNode *cond, llirBlock *true_block, llirBlock *false_block); -void llir_build_defer_stmt (llirProcedure *proc, llirDefer d); -llirAddr llir_build_addr (llirProcedure *proc, AstNode *expr); -void llir_build_proc (llirValue *value, llirProcedure *parent); -void llir_gen_global_type_name(llirModule *m, Entity *e, String name); - - - - -llirValue *llir_alloc_value(gbAllocator a, llirValueKind kind) { - llirValue *v = gb_alloc_item(a, llirValue); - v->kind = kind; - return v; -} -llirValue *llir_alloc_instr(llirProcedure *proc, llirInstrKind kind) { - llirValue *v = llir_alloc_value(proc->module->allocator, llirValue_Instr); - v->Instr.kind = kind; - proc->instr_count++; - return v; -} -llirDebugInfo *llir_alloc_debug_info(gbAllocator a, llirDebugInfoKind kind) { - llirDebugInfo *di = gb_alloc_item(a, llirDebugInfo); - di->kind = kind; - return di; -} - - - - -llirValue *llir_make_value_type_name(gbAllocator a, String name, Type *type) { - llirValue *v = llir_alloc_value(a, llirValue_TypeName); - v->TypeName.name = name; - v->TypeName.type = type; - return v; -} - -llirValue *llir_make_value_global(gbAllocator a, Entity *e, llirValue *value) { - llirValue *v = llir_alloc_value(a, llirValue_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; -} -llirValue *llir_make_value_param(gbAllocator a, llirProcedure *parent, Entity *e) { - llirValue *v = llir_alloc_value(a, llirValue_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; -} -llirValue *llir_make_value_nil(gbAllocator a, Type *type) { - llirValue *v = llir_alloc_value(a, llirValue_Nil); - v->Nil.type = type; - return v; -} - - - -llirValue *llir_make_instr_local(llirProcedure *p, Entity *e, bool zero_initialized) { - llirValue *v = llir_alloc_instr(p, llirInstr_Local); - llirInstr *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 - llir_module_add_value(p->module, e, v); - return v; -} - - -llirValue *llir_make_instr_store(llirProcedure *p, llirValue *address, llirValue *value) { - llirValue *v = llir_alloc_instr(p, llirInstr_Store); - llirInstr *i = &v->Instr; - i->Store.address = address; - i->Store.value = value; - return v; -} - -llirValue *llir_make_instr_zero_init(llirProcedure *p, llirValue *address) { - llirValue *v = llir_alloc_instr(p, llirInstr_ZeroInit); - llirInstr *i = &v->Instr; - i->ZeroInit.address = address; - return v; -} - -llirValue *llir_make_instr_load(llirProcedure *p, llirValue *address) { - llirValue *v = llir_alloc_instr(p, llirInstr_Load); - llirInstr *i = &v->Instr; - i->Load.address = address; - i->Load.type = type_deref(llir_type(address)); - return v; -} - -llirValue *llir_make_instr_array_element_ptr(llirProcedure *p, llirValue *address, llirValue *elem_index) { - llirValue *v = llir_alloc_instr(p, llirInstr_ArrayElementPtr); - llirInstr *i = &v->Instr; - Type *t = llir_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(llir_type(address)), - "%s", type_to_string(llir_type(address))); - return v; -} -llirValue *llir_make_instr_struct_element_ptr(llirProcedure *p, llirValue *address, i32 elem_index, Type *result_type) { - llirValue *v = llir_alloc_instr(p, llirInstr_StructElementPtr); - llirInstr *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(llir_type(address)), - "%s", type_to_string(llir_type(address))); - return v; -} -llirValue *llir_make_instr_ptr_offset(llirProcedure *p, llirValue *address, llirValue *offset) { - llirValue *v = llir_alloc_instr(p, llirInstr_PtrOffset); - llirInstr *i = &v->Instr; - i->PtrOffset.address = address; - i->PtrOffset.offset = offset; - - GB_ASSERT_MSG(is_type_pointer(llir_type(address)), - "%s", type_to_string(llir_type(address))); - GB_ASSERT_MSG(is_type_integer(llir_type(offset)), - "%s", type_to_string(llir_type(address))); - - return v; -} - - - -llirValue *llir_make_instr_array_extract_value(llirProcedure *p, llirValue *address, i32 index) { - llirValue *v = llir_alloc_instr(p, llirInstr_ArrayExtractValue); - llirInstr *i = &v->Instr; - i->ArrayExtractValue.address = address; - i->ArrayExtractValue.index = index; - Type *t = base_type(llir_type(address)); - GB_ASSERT(is_type_array(t)); - i->ArrayExtractValue.result_type = t->Array.elem; - return v; -} - -llirValue *llir_make_instr_struct_extract_value(llirProcedure *p, llirValue *address, i32 index, Type *result_type) { - llirValue *v = llir_alloc_instr(p, llirInstr_StructExtractValue); - llirInstr *i = &v->Instr; - i->StructExtractValue.address = address; - i->StructExtractValue.index = index; - i->StructExtractValue.result_type = result_type; - return v; -} - -llirValue *llir_make_instr_union_tag_ptr(llirProcedure *p, llirValue *address) { - llirValue *v = llir_alloc_instr(p, llirInstr_UnionTagPtr); - llirInstr *i = &v->Instr; - i->UnionTagPtr.address = address; - i->UnionTagPtr.type = t_int_ptr; - return v; -} - -llirValue *llir_make_instr_union_tag_value(llirProcedure *p, llirValue *address) { - llirValue *v = llir_alloc_instr(p, llirInstr_UnionTagValue); - llirInstr *i = &v->Instr; - i->UnionTagValue.address = address; - i->UnionTagValue.type = t_int_ptr; - return v; -} - -llirValue *llir_make_instr_unary_op(llirProcedure *p, TokenKind op, llirValue *expr, Type *type) { - llirValue *v = llir_alloc_instr(p, llirInstr_UnaryOp); - llirInstr *i = &v->Instr; - i->UnaryOp.op = op; - i->UnaryOp.expr = expr; - i->UnaryOp.type = type; - return v; -} - - -llirValue *llir_make_instr_binary_op(llirProcedure *p, TokenKind op, llirValue *left, llirValue *right, Type *type) { - llirValue *v = llir_alloc_instr(p, llirInstr_BinaryOp); - llirInstr *i = &v->Instr; - i->BinaryOp.op = op; - i->BinaryOp.left = left; - i->BinaryOp.right = right; - i->BinaryOp.type = type; - return v; -} - -llirValue *llir_make_instr_jump(llirProcedure *p, llirBlock *block) { - llirValue *v = llir_alloc_instr(p, llirInstr_Jump); - llirInstr *i = &v->Instr; - i->Jump.block = block; - return v; -} -llirValue *llir_make_instr_if(llirProcedure *p, llirValue *cond, llirBlock *true_block, llirBlock *false_block) { - llirValue *v = llir_alloc_instr(p, llirInstr_If); - llirInstr *i = &v->Instr; - i->If.cond = cond; - i->If.true_block = true_block; - i->If.false_block = false_block; - return v; -} - - -llirValue *llir_make_instr_phi(llirProcedure *p, llirValueArray edges, Type *type) { - llirValue *v = llir_alloc_instr(p, llirInstr_Phi); - llirInstr *i = &v->Instr; - i->Phi.edges = edges; - i->Phi.type = type; - return v; -} - -llirValue *llir_make_instr_unreachable(llirProcedure *p) { - llirValue *v = llir_alloc_instr(p, llirInstr_Unreachable); - return v; -} - -llirValue *llir_make_instr_return(llirProcedure *p, llirValue *value) { - llirValue *v = llir_alloc_instr(p, llirInstr_Return); - v->Instr.Return.value = value; - return v; -} - -llirValue *llir_make_instr_select(llirProcedure *p, llirValue *cond, llirValue *t, llirValue *f) { - llirValue *v = llir_alloc_instr(p, llirInstr_Select); - v->Instr.Select.cond = cond; - v->Instr.Select.true_value = t; - v->Instr.Select.false_value = f; - return v; -} - -llirValue *llir_make_instr_call(llirProcedure *p, llirValue *value, llirValue **args, isize arg_count, Type *result_type) { - llirValue *v = llir_alloc_instr(p, llirInstr_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; -} - -llirValue *llir_make_instr_conv(llirProcedure *p, llirConvKind kind, llirValue *value, Type *from, Type *to) { - llirValue *v = llir_alloc_instr(p, llirInstr_Conv); - v->Instr.Conv.kind = kind; - v->Instr.Conv.value = value; - v->Instr.Conv.from = from; - v->Instr.Conv.to = to; - return v; -} - -llirValue *llir_make_instr_extract_element(llirProcedure *p, llirValue *vector, llirValue *index) { - llirValue *v = llir_alloc_instr(p, llirInstr_VectorExtractElement); - v->Instr.VectorExtractElement.vector = vector; - v->Instr.VectorExtractElement.index = index; - return v; -} - -llirValue *llir_make_instr_insert_element(llirProcedure *p, llirValue *vector, llirValue *elem, llirValue *index) { - llirValue *v = llir_alloc_instr(p, llirInstr_VectorInsertElement); - v->Instr.VectorInsertElement.vector = vector; - v->Instr.VectorInsertElement.elem = elem; - v->Instr.VectorInsertElement.index = index; - return v; -} - -llirValue *llir_make_instr_vector_shuffle(llirProcedure *p, llirValue *vector, i32 *indices, isize index_count) { - llirValue *v = llir_alloc_instr(p, llirInstr_VectorShuffle); - v->Instr.VectorShuffle.vector = vector; - v->Instr.VectorShuffle.indices = indices; - v->Instr.VectorShuffle.index_count = index_count; - - Type *vt = base_type(llir_type(vector)); - v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); - - return v; -} - -llirValue *llir_make_instr_comment(llirProcedure *p, String text) { - llirValue *v = llir_alloc_instr(p, llirInstr_Comment); - v->Instr.Comment.text = text; - return v; -} - -llirValue *llir_make_instr_bounds_check(llirProcedure *p, TokenPos pos, llirValue *index, llirValue *len) { - llirValue *v = llir_alloc_instr(p, llirInstr_BoundsCheck); - v->Instr.BoundsCheck.pos = pos; - v->Instr.BoundsCheck.index = index; - v->Instr.BoundsCheck.len = len; - return v; -} -llirValue *llir_make_instr_slice_bounds_check(llirProcedure *p, TokenPos pos, llirValue *low, llirValue *high, bool is_substring) { - llirValue *v = llir_alloc_instr(p, llirInstr_SliceBoundsCheck); - v->Instr.SliceBoundsCheck.pos = pos; - v->Instr.SliceBoundsCheck.low = low; - v->Instr.SliceBoundsCheck.high = high; - v->Instr.SliceBoundsCheck.is_substring = is_substring; - return v; -} - - - -llirValue *llir_make_value_constant(gbAllocator a, Type *type, ExactValue value) { - llirValue *v = llir_alloc_value(a, llirValue_Constant); - v->Constant.type = type; - v->Constant.value = value; - return v; -} - - -llirValue *llir_make_value_constant_slice(gbAllocator a, Type *type, llirValue *backing_array, i64 count) { - llirValue *v = llir_alloc_value(a, llirValue_ConstantSlice); - v->ConstantSlice.type = type; - v->ConstantSlice.backing_array = backing_array; - v->ConstantSlice.count = count; - return v; -} - -llirValue *llir_make_const_int(gbAllocator a, i64 i) { - return llir_make_value_constant(a, t_int, make_exact_value_integer(i)); -} -llirValue *llir_make_const_i32(gbAllocator a, i64 i) { - return llir_make_value_constant(a, t_i32, make_exact_value_integer(i)); -} -llirValue *llir_make_const_i64(gbAllocator a, i64 i) { - return llir_make_value_constant(a, t_i64, make_exact_value_integer(i)); -} -llirValue *llir_make_const_bool(gbAllocator a, bool b) { - return llir_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); -} -llirValue *llir_make_const_string(gbAllocator a, String s) { - return llir_make_value_constant(a, t_string, make_exact_value_string(s)); -} - -llirValue *llir_make_value_procedure(gbAllocator a, llirModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { - llirValue *v = llir_alloc_value(a, llirValue_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; -} - -llirBlock *llir_add_block(llirProcedure *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])); - } - } - - llirValue *v = llir_alloc_value(proc->module->allocator, llirValue_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()); - - llirBlock *block = &v->Block; - - array_add(&proc->blocks, block); - proc->block_count++; - - return block; -} - - - - - -llirDefer llir_add_defer_node(llirProcedure *proc, isize scope_index, AstNode *stmt) { - llirDefer d = {llirDefer_Node}; - d.scope_index = scope_index; - d.block = proc->curr_block; - d.stmt = stmt; - array_add(&proc->defer_stmts, d); - return d; -} - - -llirDefer llir_add_defer_instr(llirProcedure *proc, isize scope_index, llirValue *instr) { - llirDefer d = {llirDefer_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; -} - - - -llirValue *llir_add_module_constant(llirModule *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 llir_make_value_nil(a, type); - } - Type *elem = base_type(type)->Slice.elem; - Type *t = make_type_array(a, elem, count); - llirValue *backing_array = llir_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); - llirValue *g = llir_make_value_global(a, e, backing_array); - llir_module_add_value(m, e, g); - map_llir_value_set(&m->members, hash_string(name), g); - - return llir_make_value_constant_slice(a, type, g, count); - } - - return llir_make_value_constant(a, type, value); -} - -llirValue *llir_add_global_string_array(llirModule *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); - llirValue *g = llir_make_value_global(a, entity, llir_add_module_constant(m, type, ev)); - g->Global.is_private = true; - // g->Global.is_unnamed_addr = true; - // g->Global.is_constant = true; - - llir_module_add_value(m, entity, g); - map_llir_value_set(&m->members, hash_string(name), g); - - return g; -} - - - - -llirValue *llir_add_local(llirProcedure *proc, Entity *e) { - llirBlock *b = proc->decl_block; // all variables must be in the first block - llirValue *instr = llir_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) { - llir_emit_zero_init(proc, instr); - // } - - return instr; -} - -llirValue *llir_add_local_for_identifier(llirProcedure *proc, AstNode *name, bool zero_initialized) { - Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(name)); - if (found) { - Entity *e = *found; - llir_emit_comment(proc, e->token.string); - return llir_add_local(proc, e); - } - return NULL; -} - -llirValue *llir_add_local_generated(llirProcedure *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 llir_add_local(proc, e); -} - -llirValue *llir_add_param(llirProcedure *proc, Entity *e) { - llirValue *v = llir_make_value_param(proc->module->allocator, proc, e); -#if 1 - llirValue *l = llir_add_local(proc, e); - llir_emit_store(proc, l, v); -#else - llir_module_add_value(proc->module, e, v); -#endif - return v; -} - - - -//////////////////////////////////////////////////////////////// -// -// @Debug -// -//////////////////////////////////////////////////////////////// - -llirDebugInfo *llir_add_debug_info_file(llirProcedure *proc, AstFile *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } - - GB_ASSERT(file != NULL); - llirDebugInfo *di = llir_alloc_debug_info(proc->module->allocator, llirDebugInfo_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_llir_debug_info_set(&proc->module->debug_info, hash_pointer(file), di); - return di; -} - - -llirDebugInfo *llir_add_debug_info_proc(llirProcedure *proc, Entity *entity, String name, llirDebugInfo *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } - - GB_ASSERT(entity != NULL); - llirDebugInfo *di = llir_alloc_debug_info(proc->module->allocator, llirDebugInfo_Proc); - di->Proc.entity = entity; - di->Proc.name = name; - di->Proc.file = file; - di->Proc.pos = entity->token.pos; - - map_llir_debug_info_set(&proc->module->debug_info, hash_pointer(entity), di); - return di; -} - -//////////////////////////////////////////////////////////////// -// -// @Emit -// -//////////////////////////////////////////////////////////////// - - -llirValue *llir_emit(llirProcedure *proc, llirValue *instr) { - GB_ASSERT(instr->kind == llirValue_Instr); - llirBlock *b = proc->curr_block; - instr->Instr.parent = b; - if (b != NULL) { - llirInstr *i = llir_get_last_instr(b); - if (!llir_is_instr_terminating(i)) { - array_add(&b->instrs, instr); - } - } - return instr; -} -llirValue *llir_emit_store(llirProcedure *p, llirValue *address, llirValue *value) { - return llir_emit(p, llir_make_instr_store(p, address, value)); -} -llirValue *llir_emit_load(llirProcedure *p, llirValue *address) { - return llir_emit(p, llir_make_instr_load(p, address)); -} -llirValue *llir_emit_select(llirProcedure *p, llirValue *cond, llirValue *t, llirValue *f) { - return llir_emit(p, llir_make_instr_select(p, cond, t, f)); -} - -llirValue *llir_emit_zero_init(llirProcedure *p, llirValue *address) { - return llir_emit(p, llir_make_instr_zero_init(p, address)); -} - -llirValue *llir_emit_comment(llirProcedure *p, String text) { - return llir_emit(p, llir_make_instr_comment(p, text)); -} - - -llirValue *llir_emit_call(llirProcedure *p, llirValue *value, llirValue **args, isize arg_count) { - Type *pt = base_type(llir_type(value)); - GB_ASSERT(pt->kind == Type_Proc); - Type *results = pt->Proc.results; - return llir_emit(p, llir_make_instr_call(p, value, args, arg_count, results)); -} - -llirValue *llir_emit_global_call(llirProcedure *proc, char *name_, llirValue **args, isize arg_count) { - String name = make_string_c(name_); - llirValue **found = map_llir_value_get(&proc->module->members, hash_string(name)); - GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); - llirValue *gp = *found; - return llir_emit_call(proc, gp, args, arg_count); -} - - - -void llir_emit_defer_stmts(llirProcedure *proc, llirDeferExitKind kind, llirBlock *block) { - isize count = proc->defer_stmts.count; - isize i = count; - while (i --> 0) { - llirDefer d = proc->defer_stmts.e[i]; - if (kind == llirDeferExit_Default) { - if (proc->scope_index == d.scope_index && - d.scope_index > 1) { - llir_build_defer_stmt(proc, d); - array_pop(&proc->defer_stmts); - continue; - } else { - break; - } - } else if (kind == llirDeferExit_Return) { - llir_build_defer_stmt(proc, d); - } else if (kind == llirDeferExit_Branch) { - GB_ASSERT(block != NULL); - isize lower_limit = block->scope_index+1; - if (lower_limit < d.scope_index) { - llir_build_defer_stmt(proc, d); - } - } - } -} - - -void llir_open_scope(llirProcedure *proc) { - proc->scope_index++; -} - -void llir_close_scope(llirProcedure *proc, llirDeferExitKind kind, llirBlock *block) { - llir_emit_defer_stmts(proc, kind, block); - GB_ASSERT(proc->scope_index > 0); - proc->scope_index--; -} - - - -void llir_emit_unreachable(llirProcedure *proc) { - llir_emit(proc, llir_make_instr_unreachable(proc)); -} - -void llir_emit_return(llirProcedure *proc, llirValue *v) { - llir_emit_defer_stmts(proc, llirDeferExit_Return, NULL); - llir_emit(proc, llir_make_instr_return(proc, v)); -} - -void llir_emit_jump(llirProcedure *proc, llirBlock *target_block) { - llirBlock *b = proc->curr_block; - if (b == NULL) { - return; - } - llir_emit(proc, llir_make_instr_jump(proc, target_block)); - llir_add_edge(b, target_block); - proc->curr_block = NULL; -} - -void llir_emit_if(llirProcedure *proc, llirValue *cond, llirBlock *true_block, llirBlock *false_block) { - llirBlock *b = proc->curr_block; - if (b == NULL) { - return; - } - llir_emit(proc, llir_make_instr_if(proc, cond, true_block, false_block)); - llir_add_edge(b, true_block); - llir_add_edge(b, false_block); - proc->curr_block = NULL; -} - -void llir_emit_startup_runtime(llirProcedure *proc) { - GB_ASSERT(proc->parent == NULL && str_eq(proc->name, str_lit("main"))); - llir_emit(proc, llir_alloc_instr(proc, llirInstr_StartupRuntime)); -} - - - - -llirValue *llir_addr_store(llirProcedure *proc, llirAddr addr, llirValue *value) { - if (addr.addr == NULL) { - return NULL; - } - - if (addr.kind == llirAddr_Vector) { - llirValue *v = llir_emit_load(proc, addr.addr); - Type *elem_type = base_type(llir_type(v))->Vector.elem; - llirValue *elem = llir_emit_conv(proc, value, elem_type); - llirValue *out = llir_emit(proc, llir_make_instr_insert_element(proc, v, elem, addr.Vector.index)); - return llir_emit_store(proc, addr.addr, out); - } else { - llirValue *v = llir_emit_conv(proc, value, llir_addr_type(addr)); - return llir_emit_store(proc, addr.addr, v); - } -} -llirValue *llir_addr_load(llirProcedure *proc, llirAddr addr) { - if (addr.addr == NULL) { - GB_PANIC("Illegal addr load"); - return NULL; - } - - if (addr.kind == llirAddr_Vector) { - llirValue *v = llir_emit_load(proc, addr.addr); - return llir_emit(proc, llir_make_instr_extract_element(proc, v, addr.Vector.index)); - } - Type *t = base_type(llir_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 llir_emit_load(proc, addr.addr); -} - - - - -llirValue *llir_emit_ptr_offset(llirProcedure *proc, llirValue *ptr, llirValue *offset) { - offset = llir_emit_conv(proc, offset, t_int); - return llir_emit(proc, llir_make_instr_ptr_offset(proc, ptr, offset)); -} - -llirValue *llir_emit_arith(llirProcedure *proc, TokenKind op, llirValue *left, llirValue *right, Type *type) { - Type *t_left = llir_type(left); - Type *t_right = llir_type(right); - - if (op == Token_Add) { - if (is_type_pointer(t_left)) { - llirValue *ptr = llir_emit_conv(proc, left, type); - llirValue *offset = right; - return llir_emit_ptr_offset(proc, ptr, offset); - } else if (is_type_pointer(llir_type(right))) { - llirValue *ptr = llir_emit_conv(proc, right, type); - llirValue *offset = left; - return llir_emit_ptr_offset(proc, ptr, offset); - } - } else if (op == Token_Sub) { - if (is_type_pointer(t_left) && is_type_integer(t_right)) { - // ptr - int - llirValue *ptr = llir_emit_conv(proc, left, type); - llirValue *offset = right; - return llir_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; - llirModule *m = proc->module; - llirValue *x = llir_emit_conv(proc, left, type); - llirValue *y = llir_emit_conv(proc, right, type); - llirValue *diff = llir_emit_arith(proc, op, x, y, type); - llirValue *elem_size = llir_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); - return llir_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` - llirValue *neg = llir_add_module_constant(proc->module, type, make_exact_value_integer(-1)); - op = Token_Xor; - right = llir_emit_arith(proc, op, right, neg, type); - GB_ASSERT(right->Instr.kind == llirInstr_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 = llir_emit_conv(proc, left, type); - right = llir_emit_conv(proc, right, type); - break; - } - - return llir_emit(proc, llir_make_instr_binary_op(proc, op, left, right, type)); -} - -llirValue *llir_emit_comp(llirProcedure *proc, TokenKind op_kind, llirValue *left, llirValue *right) { - Type *a = base_type(llir_type(left)); - Type *b = base_type(llir_type(right)); - - GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)); - - if (are_types_identical(a, b)) { - // NOTE(bill): No need for a conversion - } else if (left->kind == llirValue_Constant || left->kind == llirValue_Nil) { - left = llir_emit_conv(proc, left, llir_type(right)); - } else if (right->kind == llirValue_Constant || right->kind == llirValue_Nil) { - right = llir_emit_conv(proc, right, llir_type(left)); - } - - Type *result = t_bool; - if (is_type_vector(a)) { - result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); - } - return llir_emit(proc, llir_make_instr_binary_op(proc, op_kind, left, right, result)); -} - -llirValue *llir_emit_array_ep(llirProcedure *proc, llirValue *s, llirValue *index) { - GB_ASSERT(index != NULL); - Type *st = base_type(type_deref(llir_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 = llir_emit_conv(proc, index, t_i32); - return llir_emit(proc, llir_make_instr_array_element_ptr(proc, s, index)); -} - -llirValue *llir_emit_array_epi(llirProcedure *proc, llirValue *s, i32 index) { - return llir_emit_array_ep(proc, s, llir_make_const_i32(proc->module->allocator, index)); -} - -llirValue *llir_emit_union_tag_ptr(llirProcedure *proc, llirValue *u) { - Type *t = llir_type(u); - GB_ASSERT(is_type_pointer(t) && - is_type_union(type_deref(t))); - GB_ASSERT(are_types_identical(t, llir_type(u))); - return llir_emit(proc, llir_make_instr_union_tag_ptr(proc, u)); -} - -llirValue *llir_emit_union_tag_value(llirProcedure *proc, llirValue *u) { - Type *t = llir_type(u); - GB_ASSERT(is_type_union(t)); - GB_ASSERT(are_types_identical(t, llir_type(u))); - return llir_emit(proc, llir_make_instr_union_tag_value(proc, u)); -} - - - -llirValue *llir_emit_struct_ep(llirProcedure *proc, llirValue *s, i32 index) { - gbAllocator a = proc->module->allocator; - Type *t = base_type(type_deref(llir_type(s))); - Type *result_type = NULL; - llirValue *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(llir_type(s)), index); - } - - GB_ASSERT(result_type != NULL); - - gep = llir_make_instr_struct_element_ptr(proc, s, index, result_type); - return llir_emit(proc, gep); -} - - - -llirValue *llir_emit_array_ev(llirProcedure *proc, llirValue *s, i32 index) { - Type *st = base_type(llir_type(s)); - GB_ASSERT(is_type_array(st)); - return llir_emit(proc, llir_make_instr_array_extract_value(proc, s, index)); -} - -llirValue *llir_emit_struct_ev(llirProcedure *proc, llirValue *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(llir_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(llir_type(s)), index); - } - - GB_ASSERT(result_type != NULL); - - return llir_emit(proc, llir_make_instr_struct_extract_value(proc, s, index, result_type)); -} - - -llirValue *llir_emit_deep_field_gep(llirProcedure *proc, Type *type, llirValue *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 = llir_emit_load(proc, e); - e = llir_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 = llir_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); - } else if (type->kind == Type_Record) { - type = type->Record.fields[index]->type; - e = llir_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 = llir_emit_struct_ep(proc, e, index); - } break; - - case Basic_string: - e = llir_emit_struct_ep(proc, e, index); - break; - - default: - GB_PANIC("un-gep-able type"); - break; - } - } else if (type->kind == Type_Slice) { - e = llir_emit_struct_ep(proc, e, index); - } else if (type->kind == Type_Vector) { - e = llir_emit_array_epi(proc, e, index); - } else if (type->kind == Type_Array) { - e = llir_emit_array_epi(proc, e, index); - } else { - GB_PANIC("un-gep-able type"); - } - } - - return e; -} - - -llirValue *llir_emit_deep_field_ev(llirProcedure *proc, Type *type, llirValue *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 = llir_emit_load(proc, e); - e = llir_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 = llir_emit_conv(proc, e, type); - } else { - e = llir_emit_struct_ev(proc, e, index); - } - } - - return e; -} - - - - -llirValue *llir_array_elem(llirProcedure *proc, llirValue *array) { - return llir_emit_array_ep(proc, array, v_zero32); -} -llirValue *llir_array_len(llirProcedure *proc, llirValue *array) { - Type *t = llir_type(array); - GB_ASSERT(t->kind == Type_Array); - return llir_make_const_int(proc->module->allocator, t->Array.count); -} -llirValue *llir_array_cap(llirProcedure *proc, llirValue *array) { - return llir_array_len(proc, array); -} - -llirValue *llir_slice_elem(llirProcedure *proc, llirValue *slice) { - Type *t = llir_type(slice); - GB_ASSERT(t->kind == Type_Slice); - return llir_emit_struct_ev(proc, slice, 0); -} -llirValue *llir_slice_len(llirProcedure *proc, llirValue *slice) { - Type *t = llir_type(slice); - GB_ASSERT(t->kind == Type_Slice); - return llir_emit_struct_ev(proc, slice, 1); -} - -llirValue *llir_string_elem(llirProcedure *proc, llirValue *string) { - Type *t = llir_type(string); - GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); - return llir_emit_struct_ev(proc, string, 0); -} -llirValue *llir_string_len(llirProcedure *proc, llirValue *string) { - Type *t = llir_type(string); - GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t)); - return llir_emit_struct_ev(proc, string, 1); -} - - - -llirValue *llir_add_local_slice(llirProcedure *proc, Type *slice_type, llirValue *base, llirValue *low, llirValue *high) { - // TODO(bill): array bounds checking for slice creation - // TODO(bill): check that low < high <= max - gbAllocator a = proc->module->allocator; - Type *bt = base_type(llir_type(base)); - - if (low == NULL) { - low = v_zero; - } - if (high == NULL) { - switch (bt->kind) { - case Type_Array: high = llir_array_len(proc, base); break; - case Type_Slice: high = llir_slice_len(proc, base); break; - case Type_Pointer: high = v_one; break; - } - } - - llirValue *len = llir_emit_arith(proc, Token_Sub, high, low, t_int); - - llirValue *elem = NULL; - switch (bt->kind) { - case Type_Array: elem = llir_array_elem(proc, base); break; - case Type_Slice: elem = llir_slice_elem(proc, base); break; - case Type_Pointer: elem = llir_emit_load(proc, base); break; - } - - elem = llir_emit_ptr_offset(proc, elem, low); - - llirValue *slice = llir_add_local_generated(proc, slice_type); - - llirValue *gep = NULL; - gep = llir_emit_struct_ep(proc, slice, 0); - llir_emit_store(proc, gep, elem); - - gep = llir_emit_struct_ep(proc, slice, 1); - llir_emit_store(proc, gep, len); - - return slice; -} - -llirValue *llir_emit_string(llirProcedure *proc, llirValue *elem, llirValue *len) { - llirValue *str = llir_add_local_generated(proc, t_string); - llirValue *str_elem = llir_emit_struct_ep(proc, str, 0); - llirValue *str_len = llir_emit_struct_ep(proc, str, 1); - llir_emit_store(proc, str_elem, elem); - llir_emit_store(proc, str_len, len); - return llir_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(""); -} - -llirValue *llir_emit_bitcast(llirProcedure *proc, llirValue *data, Type *type) { - return llir_emit(proc, llir_make_instr_conv(proc, llirConv_bitcast, data, llir_type(data), type)); -} - - -llirValue *llir_emit_conv(llirProcedure *proc, llirValue *value, Type *t) { - Type *src_type = llir_type(value); - if (are_types_identical(t, src_type)) { - return value; - } - - Type *src = base_type(base_enum_type(src_type)); - Type *dst = base_type(base_enum_type(t)); - - if (value->kind == llirValue_Constant) { - if (is_type_any(dst)) { - llirValue *default_value = llir_add_local_generated(proc, default_type(src_type)); - llir_emit_store(proc, default_value, value); - return llir_emit_conv(proc, llir_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` - llirValue *i = llir_add_module_constant(proc->module, t_uint, ev); - return llir_emit(proc, llir_make_instr_conv(proc, llirConv_inttoptr, i, t_uint, dst)); - } - return llir_add_module_constant(proc->module, t, ev); - } - } - - if (are_types_identical(src, dst)) { - return value; - } - - if (is_type_maybe(dst)) { - llirValue *maybe = llir_add_local_generated(proc, dst); - llirValue *val = llir_emit_struct_ep(proc, maybe, 0); - llirValue *set = llir_emit_struct_ep(proc, maybe, 1); - llir_emit_store(proc, val, value); - llir_emit_store(proc, set, v_true); - return llir_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; - } - - llirConvKind kind = llirConv_trunc; - if (dz >= sz) { - kind = llirConv_zext; - } - return llir_emit(proc, llir_make_instr_conv(proc, kind, value, src, dst)); - } - - // boolean -> integer - if (is_type_boolean(src) && is_type_integer(dst)) { - return llir_emit(proc, llir_make_instr_conv(proc, llirConv_zext, value, src, dst)); - } - - // integer -> boolean - if (is_type_integer(src) && is_type_boolean(dst)) { - return llir_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); - llirConvKind kind = llirConv_fptrunc; - if (dz >= sz) { - kind = llirConv_fpext; - } - return llir_emit(proc, llir_make_instr_conv(proc, kind, value, src, dst)); - } - - // float <-> integer - if (is_type_float(src) && is_type_integer(dst)) { - llirConvKind kind = llirConv_fptosi; - if (is_type_unsigned(dst)) { - kind = llirConv_fptoui; - } - return llir_emit(proc, llir_make_instr_conv(proc, kind, value, src, dst)); - } - if (is_type_integer(src) && is_type_float(dst)) { - llirConvKind kind = llirConv_sitofp; - if (is_type_unsigned(src)) { - kind = llirConv_uitofp; - } - return llir_emit(proc, llir_make_instr_conv(proc, kind, value, src, dst)); - } - - // Pointer <-> int - if (is_type_pointer(src) && is_type_int_or_uint(dst)) { - return llir_emit(proc, llir_make_instr_conv(proc, llirConv_ptrtoint, value, src, dst)); - } - if (is_type_int_or_uint(src) && is_type_pointer(dst)) { - return llir_emit(proc, llir_make_instr_conv(proc, llirConv_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)) { - llir_emit_comment(proc, str_lit("union - child to parent")); - gbAllocator allocator = proc->module->allocator; - llirValue *parent = llir_add_local_generated(proc, t); - llirValue *tag = llir_make_const_int(allocator, i); - llir_emit_store(proc, llir_emit_union_tag_ptr(proc, parent), tag); - - llirValue *data = llir_emit_conv(proc, parent, t_rawptr); - - Type *tag_type = src_type; - Type *tag_type_ptr = make_type_pointer(allocator, tag_type); - llirValue *underlying = llir_emit_bitcast(proc, data, tag_type_ptr); - llir_emit_store(proc, underlying, value); - - return llir_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) { - llir_emit_comment(proc, str_lit("cast - polymorphism")); - if (src_is_ptr) { - value = llir_emit_load(proc, value); - } - return llir_emit_deep_field_ev(proc, sb, value, sel); - } - } - } - } - - - - // Pointer <-> Pointer - if (is_type_pointer(src) && is_type_pointer(dst)) { - return llir_emit_bitcast(proc, value, dst); - } - - - - // proc <-> proc - if (is_type_proc(src) && is_type_proc(dst)) { - return llir_emit_bitcast(proc, value, dst); - } - - // pointer -> proc - if (is_type_pointer(src) && is_type_proc(dst)) { - return llir_emit_bitcast(proc, value, dst); - } - // proc -> pointer - if (is_type_proc(src) && is_type_pointer(dst)) { - return llir_emit_bitcast(proc, value, dst); - } - - - - // []byte/[]u8 <-> string - if (is_type_u8_slice(src) && is_type_string(dst)) { - llirValue *elem = llir_slice_elem(proc, value); - llirValue *len = llir_slice_len(proc, value); - return llir_emit_string(proc, elem, len); - } - if (is_type_string(src) && is_type_u8_slice(dst)) { - llirValue *elem = llir_string_elem(proc, value); - llirValue *elem_ptr = llir_add_local_generated(proc, llir_type(elem)); - llir_emit_store(proc, elem_ptr, elem); - - llirValue *len = llir_string_len(proc, value); - llirValue *slice = llir_add_local_slice(proc, dst, elem_ptr, v_zero, len); - return llir_emit_load(proc, slice); - } - - if (is_type_vector(dst)) { - Type *dst_elem = dst->Vector.elem; - value = llir_emit_conv(proc, value, dst_elem); - llirValue *v = llir_add_local_generated(proc, t); - v = llir_emit_load(proc, v); - v = llir_emit(proc, llir_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 = llir_emit(proc, llir_make_instr_vector_shuffle(proc, v, indices, index_count)); - return v; - } - - if (is_type_any(dst)) { - llirValue *result = llir_add_local_generated(proc, t_any); - - if (is_type_untyped_nil(src)) { - return llir_emit_load(proc, result); - } - - llirValue *data = NULL; - if (value->kind == llirValue_Instr && - value->Instr.kind == llirInstr_Load) { - // NOTE(bill): Addrellirble value - data = value->Instr.Load.address; - } else { - // NOTE(bill): Non-addrellirble value - data = llir_add_local_generated(proc, src_type); - llir_emit_store(proc, data, value); - } - GB_ASSERT(is_type_pointer(llir_type(data))); - GB_ASSERT(is_type_typed(src_type)); - data = llir_emit_conv(proc, data, t_rawptr); - - - llirValue *ti = llir_type_info(proc, src_type); - - llirValue *gep0 = llir_emit_struct_ep(proc, result, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, result, 1); - llir_emit_store(proc, gep0, ti); - llir_emit_store(proc, gep1, data); - - return llir_emit_load(proc, result); - } - - if (is_type_untyped_nil(src) && type_has_nil(dst)) { - return llir_make_value_nil(proc->module->allocator, t); - } - - - gb_printf_err("llir_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 llir_is_type_aggregate(Type *t) { - t = 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 llir_is_type_aggregate(t->Named.base); - } - - return false; -} - -llirValue *llir_emit_transmute(llirProcedure *proc, llirValue *value, Type *t) { - Type *src_type = llir_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; - } - - llirModule *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 (llir_is_type_aggregate(src) || llir_is_type_aggregate(dst)) { - llirValue *s = llir_add_local_generated(proc, src); - llir_emit_store(proc, s, value); - - llirValue *d = llir_emit_bitcast(proc, s, make_type_pointer(m->allocator, dst)); - return llir_emit_load(proc, d); - } - - // TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM - - return llir_emit_bitcast(proc, value, dst); -} - -llirValue *llir_emit_down_cast(llirProcedure *proc, llirValue *value, Type *t) { - GB_ASSERT(is_type_pointer(llir_type(value))); - gbAllocator allocator = proc->module->allocator; - - String field_name = check_down_cast_name(t, type_deref(llir_type(value))); - GB_ASSERT(field_name.len > 0); - Selection sel = lookup_field(proc->module->allocator, t, field_name, false); - llirValue *bytes = llir_emit_conv(proc, value, t_u8_ptr); - - i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); - llirValue *offset = llir_make_const_int(allocator, -offset_); - llirValue *head = llir_emit_ptr_offset(proc, bytes, offset); - return llir_emit_conv(proc, head, t); -} - -llirValue *llir_emit_union_cast(llirProcedure *proc, llirValue *value, Type *tuple) { - GB_ASSERT(tuple->kind == Type_Tuple); - gbAllocator a = proc->module->allocator; - - Type *src_type = llir_type(value); - bool is_ptr = is_type_pointer(src_type); - - llirValue *v = llir_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); - - llirValue *tag = llir_emit_load(proc, llir_emit_union_tag_ptr(proc, value)); - llirValue *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 = llir_make_const_int(a, i); - break; - } - } - GB_ASSERT(dst_tag != NULL); - - llirBlock *ok_block = llir_add_block(proc, NULL, "union_cast.ok"); - llirBlock *end_block = llir_add_block(proc, NULL, "union_cast.end"); - llirValue *cond = llir_emit_comp(proc, Token_CmpEq, tag, dst_tag); - llir_emit_if(proc, cond, ok_block, end_block); - proc->curr_block = ok_block; - - llirValue *gep0 = llir_emit_struct_ep(proc, v, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, v, 1); - - llirValue *data = llir_emit_conv(proc, value, dst_ptr); - llir_emit_store(proc, gep0, data); - llir_emit_store(proc, gep1, v_true); - - llir_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); - - llirValue *tag = llir_emit_union_tag_value(proc, value); - llirValue *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 = llir_make_const_int(a, i); - break; - } - } - GB_ASSERT(dst_tag != NULL); - - // HACK(bill): This is probably not very efficient - llirValue *union_copy = llir_add_local_generated(proc, src_type); - llir_emit_store(proc, union_copy, value); - - llirBlock *ok_block = llir_add_block(proc, NULL, "union_cast.ok"); - llirBlock *end_block = llir_add_block(proc, NULL, "union_cast.end"); - llirValue *cond = llir_emit_comp(proc, Token_CmpEq, tag, dst_tag); - llir_emit_if(proc, cond, ok_block, end_block); - proc->curr_block = ok_block; - - llirValue *gep0 = llir_emit_struct_ep(proc, v, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, v, 1); - - llirValue *data = llir_emit_load(proc, llir_emit_conv(proc, union_copy, dst_ptr)); - llir_emit_store(proc, gep0, data); - llir_emit_store(proc, gep1, v_true); - - llir_emit_jump(proc, end_block); - proc->curr_block = end_block; - - } - return llir_emit_load(proc, v); -} - - -isize llir_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; -} - -llirValue *llir_type_info(llirProcedure *proc, Type *type) { - llirValue **found = map_llir_value_get(&proc->module->members, hash_string(str_lit(LLIR_TYPE_INFO_DATA_NAME))); - GB_ASSERT(found != NULL); - llirValue *type_info_data = *found; - CheckerInfo *info = proc->module->info; - - type = default_type(type); - - i32 entry_index = llir_type_info_index(info, type); - - // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); - - return llir_emit_array_ep(proc, type_info_data, llir_make_const_i32(proc->module->allocator, entry_index)); -} - - - -llirValue *llir_emit_logical_binary_expr(llirProcedure *proc, AstNode *expr) { - ast_node(be, BinaryExpr, expr); -#if 0 - llirBlock *true_ = llir_add_block(proc, NULL, "logical.cmp.true"); - llirBlock *false_ = llir_add_block(proc, NULL, "logical.cmp.false"); - llirBlock *done = llir_add_block(proc, NULL, "logical.cmp.done"); - - llirValue *result = llir_add_local_generated(proc, t_bool); - llir_build_cond(proc, expr, true_, false_); - - proc->curr_block = true_; - llir_emit_store(proc, result, v_true); - llir_emit_jump(proc, done); - - proc->curr_block = false_; - llir_emit_store(proc, result, v_false); - llir_emit_jump(proc, done); - - proc->curr_block = done; - - return llir_emit_load(proc, result); -#else - llirBlock *rhs = llir_add_block(proc, NULL, "logical.cmp.rhs"); - llirBlock *done = llir_add_block(proc, NULL, "logical.cmp.done"); - - Type *type = type_of_expr(proc->module->info, expr); - type = default_type(type); - - llirValue *short_circuit = NULL; - if (be->op.kind == Token_CmpAnd) { - llir_build_cond(proc, be->left, rhs, done); - short_circuit = v_false; - } else if (be->op.kind == Token_CmpOr) { - llir_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 llir_build_expr(proc, be->right); - } - - llirValueArray 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, llir_build_expr(proc, be->right)); - llir_emit_jump(proc, done); - proc->curr_block = done; - - return llir_emit(proc, llir_make_instr_phi(proc, edges, type)); -#endif -} - - -void llir_emit_bounds_check(llirProcedure *proc, Token token, llirValue *index, llirValue *len) { - if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { - return; - } - - index = llir_emit_conv(proc, index, t_int); - len = llir_emit_conv(proc, len, t_int); - - llir_emit(proc, llir_make_instr_bounds_check(proc, token.pos, index, len)); - - // gbAllocator a = proc->module->allocator; - // llirValue **args = gb_alloc_array(a, llirValue *, 5); - // args[0] = llir_emit_global_string(proc, token.pos.file); - // args[1] = llir_make_const_int(a, token.pos.line); - // args[2] = llir_make_const_int(a, token.pos.column); - // args[3] = llir_emit_conv(proc, index, t_int); - // args[4] = llir_emit_conv(proc, len, t_int); - - // llir_emit_global_call(proc, "__bounds_check_error", args, 5); -} - -void llir_emit_slice_bounds_check(llirProcedure *proc, Token token, llirValue *low, llirValue *high, bool is_substring) { - if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { - return; - } - - low = llir_emit_conv(proc, low, t_int); - high = llir_emit_conv(proc, high, t_int); - - llir_emit(proc, llir_make_instr_slice_bounds_check(proc, token.pos, low, high, is_substring)); -} - - -//////////////////////////////////////////////////////////////// -// -// @Build -// -//////////////////////////////////////////////////////////////// - - -void llir_push_target_list(llirProcedure *proc, llirBlock *break_, llirBlock *continue_, llirBlock *fallthrough_) { - llirTargetList *tl = gb_alloc_item(proc->module->allocator, llirTargetList); - tl->prev = proc->target_list; - tl->break_ = break_; - tl->continue_ = continue_; - tl->fallthrough_ = fallthrough_; - proc->target_list = tl; -} - -void llir_pop_target_list(llirProcedure *proc) { - proc->target_list = proc->target_list->prev; -} - - -void llir_mangle_sub_type_name(llirModule *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); - llir_gen_global_type_name(m, field, child); -} - -void llir_gen_global_type_name(llirModule *m, Entity *e, String name) { - llirValue *t = llir_make_value_type_name(m->allocator, name, e->type); - llir_module_add_value(m, e, t); - map_llir_value_set(&m->members, hash_string(name), t); - - if (is_type_union(e->type)) { - Type *bt = base_type(e->type); - TypeRecord *s = &bt->Record; - // NOTE(bill): Zeroth entry is null (for `match type` stmts) - for (isize j = 1; j < s->field_count; j++) { - llir_mangle_sub_type_name(m, s->fields[j], name); - } - } -} - - - - -void llir_build_defer_stmt(llirProcedure *proc, llirDefer d) { - llirBlock *b = llir_add_block(proc, NULL, "defer"); - // NOTE(bill): The prev block may defer injection before it's terminator - llirInstr *last_instr = llir_get_last_instr(proc->curr_block); - if (last_instr == NULL || !llir_is_instr_terminating(last_instr)) { - llir_emit_jump(proc, b); - } - proc->curr_block = b; - llir_emit_comment(proc, str_lit("defer")); - if (d.kind == llirDefer_Node) { - llir_build_stmt(proc, d.stmt); - } else if (d.kind == llirDefer_Instr) { - // NOTE(bill): Need to make a new copy - llirValue *instr = cast(llirValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(llirValue)); - llir_emit(proc, instr); - } -} - - - -llirValue *llir_find_global_variable(llirProcedure *proc, String name) { - llirValue **value = map_llir_value_get(&proc->module->members, hash_string(name)); - GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); - return *value; -} - -llirValue *llir_find_implicit_value_backing(llirProcedure *proc, ImplicitValueId id) { - Entity *e = proc->module->info->implicit_values[id]; - GB_ASSERT(e->kind == Entity_ImplicitValue); - Entity *backing = e->ImplicitValue.backing; - llirValue **value = map_llir_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; -} - -void llir_build_stmt_list(llirProcedure *proc, AstNodeArray stmts); - -llirValue *llir_build_single_expr(llirProcedure *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): llir_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 llir_make_value_nil(proc->module->allocator, tv->type); - } else if (e->kind == Entity_ImplicitValue) { - return llir_emit_load(proc, llir_find_implicit_value_backing(proc, e->ImplicitValue.id)); - } - - llirValue **found = map_llir_value_get(&proc->module->values, hash_pointer(e)); - if (found) { - llirValue *v = *found; - if (v->kind == llirValue_Proc) { - return v; - } - // if (e->kind == Entity_Variable && e->Variable.param) { - // return v; - // } - return llir_emit_load(proc, v); - } else if (e != NULL && e->kind == Entity_Variable) { - return llir_addr_load(proc, llir_build_addr(proc, expr)); - } - GB_PANIC("NULL value for expression from identifier: %.*s", LIT(i->string)); - return NULL; - case_end; - - case_ast_node(re, RunExpr, expr); - // TODO(bill): Run Expression - return llir_build_single_expr(proc, re->expr, tv); - case_end; - - case_ast_node(de, DerefExpr, expr); - return llir_addr_load(proc, llir_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 llir_addr_load(proc, llir_build_addr(proc, expr)); - case_end; - - case_ast_node(be, BlockExpr, expr); - llir_emit_comment(proc, str_lit("BlockExpr")); - llir_open_scope(proc); - - AstNodeArray stmts = be->stmts; - stmts.count--; - llir_build_stmt_list(proc, stmts); - - AstNode *give_stmt = be->stmts.e[be->stmts.count-1]; - GB_ASSERT(give_stmt->kind == AstNode_ExprStmt); - AstNode *give_expr = give_stmt->ExprStmt.expr; - GB_ASSERT(give_expr->kind == AstNode_GiveExpr); - llirValue *value = llir_build_expr(proc, give_expr); - - llir_close_scope(proc, llirDeferExit_Default, NULL); - - return value; - case_end; - - case_ast_node(ie, IfExpr, expr); - llir_emit_comment(proc, str_lit("IfExpr")); - if (ie->init != NULL) { - llirBlock *init = llir_add_block(proc, expr, "if.init"); - llir_emit_jump(proc, init); - proc->curr_block = init; - llir_build_stmt(proc, ie->init); - } - - llirValueArray edges = {0}; - array_init_reserve(&edges, proc->module->allocator, 2); - - GB_ASSERT(ie->else_expr != NULL); - llirBlock *then = llir_add_block(proc, expr, "if.then"); - llirBlock *done = llir_add_block(proc, expr, "if.done"); // NOTE(bill): Append later - llirBlock *else_ = llir_add_block(proc, ie->else_expr, "if.else"); - - llirValue *cond = llir_build_cond(proc, ie->cond, then, else_); - proc->curr_block = then; - - llir_open_scope(proc); - array_add(&edges, llir_build_expr(proc, ie->body)); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_emit_jump(proc, done); - proc->curr_block = else_; - - llir_open_scope(proc); - array_add(&edges, llir_build_expr(proc, ie->else_expr)); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_emit_jump(proc, done); - proc->curr_block = done; - - Type *type = type_of_expr(proc->module->info, expr); - - return llir_emit(proc, llir_make_instr_phi(proc, edges, type)); - case_end; - - case_ast_node(ge, GiveExpr, expr); - llir_emit_comment(proc, str_lit("GiveExpr")); - - llirValue *v = NULL; - Type *give_type = type_of_expr(proc->module->info, expr); - GB_ASSERT(give_type != NULL); - if (give_type->kind != Type_Tuple) { - v = llir_emit_conv(proc, llir_build_expr(proc, ge->results.e[0]), give_type); - } else { - TypeTuple *tuple = &give_type->Tuple; - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - - llirValueArray results; - array_init_reserve(&results, proc->module->tmp_allocator, tuple->variable_count); - - for_array(res_index, ge->results) { - llirValue *res = llir_build_expr(proc, ge->results.e[res_index]); - Type *t = llir_type(res); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - llirValue *v = llir_emit_struct_ev(proc, res, i); - array_add(&results, v); - } - } else { - array_add(&results, res); - } - } - - v = llir_add_local_generated(proc, give_type); - for_array(i, results) { - Entity *e = tuple->variables[i]; - llirValue *res = llir_emit_conv(proc, results.e[i], e->type); - llirValue *field = llir_emit_struct_ep(proc, v, i); - llir_emit_store(proc, field, res); - } - v = llir_emit_load(proc, v); - - gb_temp_arena_memory_end(tmp); - } - - return v; - case_end; - - case_ast_node(ue, UnaryExpr, expr); - switch (ue->op.kind) { - case Token_Pointer: - return llir_emit_ptr_offset(proc, llir_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer - - case Token_Maybe: - return llir_emit_conv(proc, llir_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); - - case Token_Add: - return llir_build_expr(proc, ue->expr); - - case Token_Not: // Boolean not - case Token_Xor: // Bitwise not - case Token_Sub: // Bitwise not - return llir_emit(proc, llir_make_instr_unary_op(proc, ue->op.kind, llir_build_expr(proc, ue->expr), tv->type)); - } - case_end; - - case_ast_node(be, BinaryExpr, expr); - llirValue *left = llir_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: { - llirValue *right = llir_build_expr(proc, be->right); - return llir_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: { - llirValue *right = llir_build_expr(proc, be->right); - llirValue *cmp = llir_emit_comp(proc, be->op.kind, left, right); - return llir_emit_conv(proc, cmp, type); - } break; - - case Token_CmpAnd: - case Token_CmpOr: - return llir_emit_logical_binary_expr(proc, expr); - - case Token_as: - llir_emit_comment(proc, str_lit("cast - as")); - return llir_emit_conv(proc, left, type); - - case Token_transmute: - llir_emit_comment(proc, str_lit("cast - transmute")); - return llir_emit_transmute(proc, left, type); - - case Token_down_cast: - llir_emit_comment(proc, str_lit("cast - down_cast")); - return llir_emit_down_cast(proc, left, type); - - case Token_union_cast: - llir_emit_comment(proc, str_lit("cast - union_cast")); - return llir_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); - llirValue *value = llir_make_value_procedure(proc->module->allocator, - proc->module, NULL, type, pl->type, pl->body, name); - - value->Proc.tags = pl->tags; - value->Proc.parent = proc; - - array_add(&proc->children, &value->Proc); - array_add(&proc->module->procs_to_generate, value); - - return value; - case_end; - - - case_ast_node(cl, CompoundLit, expr); - return llir_emit_load(proc, llir_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 llir_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 llir_type_info(proc, t); - } break; - - case BuiltinProc_new: { - llir_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); - - llirValue **args = gb_alloc_array(allocator, llirValue *, 2); - args[0] = llir_make_const_int(allocator, s); - args[1] = llir_make_const_int(allocator, a); - llirValue *call = llir_emit_global_call(proc, "alloc_align", args, 2); - llirValue *v = llir_emit_conv(proc, call, ptr_type); - return v; - } break; - - case BuiltinProc_new_slice: { - llir_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); - - llirValue *elem_size = llir_make_const_int(allocator, s); - llirValue *elem_align = llir_make_const_int(allocator, a); - - llirValue *count = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[1]), t_int); - - llir_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, count, false); - - llirValue *slice_size = llir_emit_arith(proc, Token_Mul, elem_size, count, t_int); - - llirValue **args = gb_alloc_array(allocator, llirValue *, 2); - args[0] = slice_size; - args[1] = elem_align; - llirValue *call = llir_emit_global_call(proc, "alloc_align", args, 2); - - llirValue *ptr = llir_emit_conv(proc, call, ptr_type); - llirValue *slice = llir_add_local_generated(proc, slice_type); - - llirValue *gep0 = llir_emit_struct_ep(proc, slice, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, slice, 1); - llir_emit_store(proc, gep0, ptr); - llir_emit_store(proc, gep1, count); - return llir_emit_load(proc, slice); - } break; - - case BuiltinProc_assert: { - llir_emit_comment(proc, str_lit("assert")); - llirValue *cond = llir_build_expr(proc, ce->args.e[0]); - GB_ASSERT(is_type_boolean(llir_type(cond))); - - cond = llir_emit_comp(proc, Token_CmpEq, cond, v_false); - llirBlock *err = llir_add_block(proc, NULL, "builtin.assert.err"); - llirBlock *done = llir_add_block(proc, NULL, "builtin.assert.done"); - - llir_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); - - - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, 4); - args[0] = llir_make_const_string(proc->module->allocator, pos.file); - args[1] = llir_make_const_int(proc->module->allocator, pos.line); - args[2] = llir_make_const_int(proc->module->allocator, pos.column); - args[3] = llir_make_const_string(proc->module->allocator, expr_str); - llir_emit_global_call(proc, "__assert", args, 4); - - llir_emit_jump(proc, done); - proc->curr_block = done; - - return NULL; - } break; - - case BuiltinProc_panic: { - llir_emit_comment(proc, str_lit("panic")); - llirValue *msg = llir_build_expr(proc, ce->args.e[0]); - GB_ASSERT(is_type_string(llir_type(msg))); - - Token token = ast_node_token(ce->args.e[0]); - TokenPos pos = token.pos; - - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, 4); - args[0] = llir_make_const_string(proc->module->allocator, pos.file); - args[1] = llir_make_const_int(proc->module->allocator, pos.line); - args[2] = llir_make_const_int(proc->module->allocator, pos.column); - args[3] = msg; - llir_emit_global_call(proc, "__assert", args, 4); - - return NULL; - } break; - - - case BuiltinProc_copy: { - llir_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]; - llirValue *dst_slice = llir_build_expr(proc, dst_node); - llirValue *src_slice = llir_build_expr(proc, src_node); - Type *slice_type = base_type(llir_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); - - llirValue *dst = llir_emit_conv(proc, llir_slice_elem(proc, dst_slice), t_rawptr); - llirValue *src = llir_emit_conv(proc, llir_slice_elem(proc, src_slice), t_rawptr); - - llirValue *len_dst = llir_slice_len(proc, dst_slice); - llirValue *len_src = llir_slice_len(proc, src_slice); - - llirValue *cond = llir_emit_comp(proc, Token_Lt, len_dst, len_src); - llirValue *len = llir_emit_select(proc, cond, len_dst, len_src); - - llirValue *elem_size = llir_make_const_int(proc->module->allocator, size_of_elem); - llirValue *byte_count = llir_emit_arith(proc, Token_Mul, len, elem_size, t_int); - - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, 3); - args[0] = dst; - args[1] = src; - args[2] = byte_count; - - llir_emit_global_call(proc, "__mem_copy", args, 3); - - return len; - } break; - #if 0 - case BuiltinProc_append: { - llir_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]; - llirValue *slice_ptr = llir_build_expr(proc, sptr_node); - llirValue *slice = llir_emit_load(proc, slice_ptr); - - llirValue *elem = llir_slice_elem(proc, slice); - llirValue *len = llir_slice_len(proc, slice); - llirValue *cap = llir_slice_cap(proc, slice); - - Type *elem_type = type_deref(llir_type(elem)); - - llirValue *item_value = llir_build_expr(proc, item_node); - item_value = llir_emit_conv(proc, item_value, elem_type); - - llirValue *item = llir_add_local_generated(proc, elem_type); - llir_emit_store(proc, item, item_value); - - - // NOTE(bill): Check if can append is possible - llirValue *cond = llir_emit_comp(proc, Token_Lt, len, cap); - llirBlock *able = llir_add_block(proc, NULL, "builtin.append.able"); - llirBlock *done = llir_add_block(proc, NULL, "builtin.append.done"); - - llir_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); - llirValue *byte_count = llir_make_const_int(proc->module->allocator, item_size); - - llirValue *offset = llir_emit_ptr_offset(proc, elem, len); - offset = llir_emit_conv(proc, offset, t_rawptr); - - item = llir_emit_ptr_offset(proc, item, v_zero); - item = llir_emit_conv(proc, item, t_rawptr); - - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, 3); - args[0] = offset; - args[1] = item; - args[2] = byte_count; - - llir_emit_global_call(proc, "__mem_copy", args, 3); - - // Increment slice length - llirValue *new_len = llir_emit_arith(proc, Token_Add, len, v_one, t_int); - llirValue *gep = llir_emit_struct_ep(proc, slice_ptr, 1); - llir_emit_store(proc, gep, new_len); - - llir_emit_jump(proc, done); - proc->curr_block = done; - - return llir_emit_conv(proc, cond, t_bool); - } break; - #endif - - case BuiltinProc_swizzle: { - llir_emit_comment(proc, str_lit("swizzle")); - llirValue *vector = llir_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 llir_emit(proc, llir_make_instr_vector_shuffle(proc, vector, indices, index_count)); - - } break; - - case BuiltinProc_slice_ptr: { - llir_emit_comment(proc, str_lit("slice_ptr")); - llirValue *ptr = llir_build_expr(proc, ce->args.e[0]); - llirValue *count = llir_build_expr(proc, ce->args.e[1]); - count = llir_emit_conv(proc, count, t_int); - - Type *slice_type = make_type_slice(proc->module->allocator, type_deref(llir_type(ptr))); - llirValue *slice = llir_add_local_generated(proc, slice_type); - llir_emit_store(proc, llir_emit_struct_ep(proc, slice, 0), ptr); - llir_emit_store(proc, llir_emit_struct_ep(proc, slice, 1), count); - return llir_emit_load(proc, slice); - } break; - - case BuiltinProc_min: { - llir_emit_comment(proc, str_lit("min")); - Type *t = type_of_expr(proc->module->info, expr); - llirValue *x = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[0]), t); - llirValue *y = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[1]), t); - llirValue *cond = llir_emit_comp(proc, Token_Lt, x, y); - return llir_emit_select(proc, cond, x, y); - } break; - - case BuiltinProc_max: { - llir_emit_comment(proc, str_lit("max")); - Type *t = type_of_expr(proc->module->info, expr); - llirValue *x = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[0]), t); - llirValue *y = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[1]), t); - llirValue *cond = llir_emit_comp(proc, Token_Gt, x, y); - return llir_emit_select(proc, cond, x, y); - } break; - - case BuiltinProc_abs: { - llir_emit_comment(proc, str_lit("abs")); - gbAllocator a = proc->module->allocator; - - llirValue *x = llir_build_expr(proc, ce->args.e[0]); - Type *original_type = llir_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 = llir_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 - */ - - llirValue *m = llir_emit_arith(proc, Token_Shr, - x, - llir_make_value_constant(a, t, make_exact_value_integer(sz-1)), - t); - llirValue *b = llir_emit_arith(proc, Token_Xor, x, m, t); - llirValue *v = llir_emit_arith(proc, Token_Sub, b, m, t); - - if (is_type_float(t)) { - v = llir_emit_bitcast(proc, v, original_type); - } - return v; - } break; - - case BuiltinProc_clamp: { - llir_emit_comment(proc, str_lit("clamp")); - Type *t = type_of_expr(proc->module->info, expr); - llirValue *x = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[0]), t); - llirValue *min = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[1]), t); - llirValue *max = llir_emit_conv(proc, llir_build_expr(proc, ce->args.e[2]), t); - llirValue *cond; - cond = llir_emit_comp(proc, Token_Gt, min, x); - x = llir_emit_select(proc, cond, min, x); - cond = llir_emit_comp(proc, Token_Lt, max, x); - x = llir_emit_select(proc, cond, max, x); - return x; - } break; - } - } - } - - // NOTE(bill): Regular call - llirValue *value = llir_build_expr(proc, ce->proc); - GB_ASSERT(value != NULL); - Type *proc_type_ = base_type(llir_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++; - } - } - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, arg_count); - bool variadic = proc_type_->Proc.variadic; - bool vari_expand = ce->ellipsis.pos.line != 0; - - for_array(i, ce->args) { - llirValue *a = llir_build_expr(proc, ce->args.e[i]); - Type *at = llir_type(a); - if (at->kind == Type_Tuple) { - for (isize i = 0; i < at->Tuple.variable_count; i++) { - Entity *e = at->Tuple.variables[i]; - llirValue *v = llir_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] = llir_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] = llir_emit_conv(proc, args[i], variadic_type); - } - } - } else { - for (isize i = 0; i < arg_count; i++) { - args[i] = llir_emit_conv(proc, args[i], pt->variables[i]->type); - } - } - - if (variadic && !vari_expand) { - llir_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; - llirValue *slice = llir_add_local_generated(proc, slice_type); - isize slice_len = arg_count+1 - type->param_count; - - if (slice_len > 0) { - llirValue *base_array = llir_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++) { - llirValue *addr = llir_emit_array_epi(proc, base_array, j); - llir_emit_store(proc, addr, args[i]); - } - - llirValue *base_elem = llir_emit_array_epi(proc, base_array, 0); - llirValue *slice_elem = llir_emit_struct_ep(proc, slice, 0); - llir_emit_store(proc, slice_elem, base_elem); - llirValue *len = llir_make_const_int(allocator, slice_len); - llir_emit_store(proc, llir_emit_struct_ep(proc, slice, 1), len); - llir_emit_store(proc, llir_emit_struct_ep(proc, slice, 2), len); - } - - arg_count = type->param_count; - args[arg_count-1] = llir_emit_load(proc, slice); - } - - return llir_emit_call(proc, value, args, arg_count); - case_end; - - case_ast_node(de, DemaybeExpr, expr); - return llir_emit_load(proc, llir_build_addr(proc, expr).addr); - case_end; - - case_ast_node(se, SliceExpr, expr); - return llir_emit_load(proc, llir_build_addr(proc, expr).addr); - case_end; - - case_ast_node(ie, IndexExpr, expr); - return llir_emit_load(proc, llir_build_addr(proc, expr).addr); - case_end; - } - - GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); - return NULL; -} - - -llirValue *llir_build_expr(llirProcedure *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 llir_add_module_constant(proc->module, tv->type, tv->value); - } - - llirValue *value = NULL; - if (tv->mode == Addressing_Variable) { - value = llir_addr_load(proc, llir_build_addr(proc, expr)); - } else { - value = llir_build_single_expr(proc, expr, tv); - } - - return value; -} - -llirValue *llir_add_using_variable(llirProcedure *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); - llirValue **pv = map_llir_value_get(&proc->module->values, hash_pointer(parent)); - llirValue *v = NULL; - if (pv != NULL) { - v = *pv; - } else { - v = llir_build_addr(proc, e->using_expr).addr; - } - GB_ASSERT(v != NULL); - llirValue *var = llir_emit_deep_field_gep(proc, parent->type, v, sel); - map_llir_value_set(&proc->module->values, hash_pointer(e), var); - return var; -} - -bool llir_is_elem_const(llirModule *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; -} - -llirAddr llir_build_addr_from_entity(llirProcedure *proc, Entity *e, AstNode *expr) { - GB_ASSERT(e != NULL); - GB_ASSERT(e->kind != Entity_Constant); - - llirValue *v = NULL; - llirValue **found = map_llir_value_get(&proc->module->values, hash_pointer(e)); - if (found) { - v = *found; - } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { - v = llir_add_using_variable(proc, e); - } else if (e->kind == Entity_ImplicitValue) { - // TODO(bill): Should a copy be made? - v = llir_find_implicit_value_backing(proc, e->ImplicitValue.id); - } - - if (v == NULL) { - GB_PANIC("Unknown value: %.*s, entity: %p %.*s\n", LIT(e->token.string), e, LIT(entity_strings[e->kind])); - } - - return llir_make_addr(v, expr); -} - -llirAddr llir_build_addr(llirProcedure *proc, AstNode *expr) { - switch (expr->kind) { - case_ast_node(i, Ident, expr); - if (llir_is_blank_ident(expr)) { - llirAddr val = {0}; - return val; - } - - Entity *e = entity_of_ident(proc->module->info, expr); - return llir_build_addr_from_entity(proc, e, expr); - case_end; - - case_ast_node(pe, ParenExpr, expr); - return llir_build_addr(proc, unparen_expr(expr)); - case_end; - - case_ast_node(se, SelectorExpr, expr); - llir_emit_comment(proc, str_lit("SelectorExpr")); - AstNode *sel = unparen_expr(se->selector); - GB_ASSERT(sel->kind == AstNode_Ident); - String selector = sel->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 llir_build_addr(proc, unparen_expr(se->selector)); - } else { - Selection sel = lookup_field(proc->module->allocator, type, selector, false); - GB_ASSERT(sel.entity != NULL); - - llirValue *a = llir_build_addr(proc, se->expr).addr; - a = llir_emit_deep_field_gep(proc, type, a, sel); - return llir_make_addr(a, expr); - } - case_end; - - case_ast_node(ue, UnaryExpr, expr); - switch (ue->op.kind) { - case Token_Pointer: { - return llir_build_addr(proc, ue->expr); - } - default: - GB_PANIC("Invalid unary expression for llir_build_addr"); - } - case_end; - - case_ast_node(be, BinaryExpr, expr); - switch (be->op.kind) { - case Token_as: { - llir_emit_comment(proc, str_lit("Cast - as")); - // NOTE(bill): Needed for dereference of pointer conversion - Type *type = type_of_expr(proc->module->info, expr); - llirValue *v = llir_add_local_generated(proc, type); - llir_emit_store(proc, v, llir_emit_conv(proc, llir_build_expr(proc, be->left), type)); - return llir_make_addr(v, expr); - } - case Token_transmute: { - llir_emit_comment(proc, str_lit("Cast - transmute")); - // NOTE(bill): Needed for dereference of pointer conversion - Type *type = type_of_expr(proc->module->info, expr); - llirValue *v = llir_add_local_generated(proc, type); - llir_emit_store(proc, v, llir_emit_transmute(proc, llir_build_expr(proc, be->left), type)); - return llir_make_addr(v, expr); - } - default: - GB_PANIC("Invalid binary expression for llir_build_addr: %.*s\n", LIT(be->op.string)); - break; - } - case_end; - - case_ast_node(ie, IndexExpr, expr); - llir_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); - - llirValue *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); - llirValue *e = llir_build_addr(proc, ie->expr).addr; - using_addr = llir_emit_deep_field_gep(proc, t, e, sel); - - t = using_field->type; - } - } - - - switch (t->kind) { - case Type_Vector: { - llirValue *vector = NULL; - if (using_addr != NULL) { - vector = using_addr; - } else { - vector = llir_build_addr(proc, ie->expr).addr; - if (deref) { - vector = llir_emit_load(proc, vector); - } - } - llirValue *index = llir_emit_conv(proc, llir_build_expr(proc, ie->index), t_int); - llirValue *len = llir_make_const_int(a, t->Vector.count); - llir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - return llir_make_addr_vector(vector, index, expr); - } break; - - case Type_Array: { - llirValue *array = NULL; - if (using_addr != NULL) { - array = using_addr; - } else { - array = llir_build_addr(proc, ie->expr).addr; - if (deref) { - array = llir_emit_load(proc, array); - } - } - llirValue *index = llir_emit_conv(proc, llir_build_expr(proc, ie->index), t_int); - llirValue *elem = llir_emit_array_ep(proc, array, index); - llirValue *len = llir_make_const_int(a, t->Vector.count); - llir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - return llir_make_addr(elem, expr); - } break; - - case Type_Slice: { - llirValue *slice = NULL; - if (using_addr != NULL) { - slice = llir_emit_load(proc, using_addr); - } else { - slice = llir_build_expr(proc, ie->expr); - if (deref) { - slice = llir_emit_load(proc, slice); - } - } - llirValue *elem = llir_slice_elem(proc, slice); - llirValue *len = llir_slice_len(proc, slice); - llirValue *index = llir_emit_conv(proc, llir_build_expr(proc, ie->index), t_int); - llir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - llirValue *v = llir_emit_ptr_offset(proc, elem, index); - return llir_make_addr(v, expr); - - } break; - - case Type_Basic: { // Basic_string - TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr)); - llirValue *str; - llirValue *elem; - llirValue *len; - llirValue *index; - - if (using_addr != NULL) { - str = llir_emit_load(proc, using_addr); - } else { - str = llir_build_expr(proc, ie->expr); - if (deref) { - str = llir_emit_load(proc, str); - } - } - elem = llir_string_elem(proc, str); - len = llir_string_len(proc, str); - - index = llir_emit_conv(proc, llir_build_expr(proc, ie->index), t_int); - llir_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - - return llir_make_addr(llir_emit_ptr_offset(proc, elem, index), expr); - } break; - } - case_end; - - case_ast_node(se, SliceExpr, expr); - llir_emit_comment(proc, str_lit("SliceExpr")); - gbAllocator a = proc->module->allocator; - llirValue *low = v_zero; - llirValue *high = NULL; - - if (se->low != NULL) low = llir_build_expr(proc, se->low); - if (se->high != NULL) high = llir_build_expr(proc, se->high); - llirValue *addr = llir_build_addr(proc, se->expr).addr; - llirValue *base = llir_emit_load(proc, addr); - Type *type = base_type(llir_type(base)); - - if (is_type_pointer(type)) { - type = type_deref(type); - addr = base; - base = llir_emit_load(proc, base); - } - - // TODO(bill): Cleanup like mad! - - switch (type->kind) { - case Type_Slice: { - Type *slice_type = type; - - if (high == NULL) high = llir_slice_len(proc, base); - - llir_emit_slice_bounds_check(proc, se->open, low, high, false); - - llirValue *elem = llir_emit_ptr_offset(proc, llir_slice_elem(proc, base), low); - llirValue *len = llir_emit_arith(proc, Token_Sub, high, low, t_int); - llirValue *slice = llir_add_local_generated(proc, slice_type); - - llirValue *gep0 = llir_emit_struct_ep(proc, slice, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, slice, 1); - llir_emit_store(proc, gep0, elem); - llir_emit_store(proc, gep1, len); - - return llir_make_addr(slice, expr); - } - - case Type_Array: { - Type *slice_type = make_type_slice(a, type->Array.elem); - - if (high == NULL) high = llir_array_len(proc, base); - - llir_emit_slice_bounds_check(proc, se->open, low, high, false); - - llirValue *elem = llir_emit_ptr_offset(proc, llir_array_elem(proc, addr), low); - llirValue *len = llir_emit_arith(proc, Token_Sub, high, low, t_int); - llirValue *slice = llir_add_local_generated(proc, slice_type); - - llirValue *gep0 = llir_emit_struct_ep(proc, slice, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, slice, 1); - llir_emit_store(proc, gep0, elem); - llir_emit_store(proc, gep1, len); - - return llir_make_addr(slice, expr); - } - - case Type_Basic: { - GB_ASSERT(type == t_string); - if (high == NULL) { - high = llir_string_len(proc, base); - } - - llir_emit_slice_bounds_check(proc, se->open, low, high, true); - - llirValue *elem, *len; - len = llir_emit_arith(proc, Token_Sub, high, low, t_int); - - elem = llir_string_elem(proc, base); - elem = llir_emit_ptr_offset(proc, elem, low); - - llirValue *str = llir_add_local_generated(proc, t_string); - llirValue *gep0 = llir_emit_struct_ep(proc, str, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, str, 1); - llir_emit_store(proc, gep0, elem); - llir_emit_store(proc, gep1, len); - - return llir_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? - llirValue *addr = llir_build_expr(proc, de->expr); - addr = llir_emit_ptr_offset(proc, addr, v_zero); - return llir_make_addr(addr, expr); - case_end; - - case_ast_node(de, DemaybeExpr, expr); - llir_emit_comment(proc, str_lit("DemaybeExpr")); - llirValue *maybe = llir_build_expr(proc, de->expr); - Type *t = default_type(type_of_expr(proc->module->info, expr)); - GB_ASSERT(is_type_tuple(t)); - - llirValue *result = llir_add_local_generated(proc, t); - llir_emit_store(proc, result, maybe); - - return llir_make_addr(result, expr); - case_end; - - case_ast_node(ce, CallExpr, expr); - llirValue *e = llir_build_expr(proc, expr); - llirValue *v = llir_add_local_generated(proc, llir_type(e)); - llir_emit_store(proc, v, e); - return llir_make_addr(v, expr); - case_end; - - - case_ast_node(cl, CompoundLit, expr); - llir_emit_comment(proc, str_lit("CompoundLit")); - Type *type = type_of_expr(proc->module->info, expr); - Type *bt = base_type(type); - llirValue *v = llir_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: { - llirValue *result = llir_add_module_constant(proc->module, type, make_exact_value_compound(expr)); - for_array(index, cl->elems) { - AstNode *elem = cl->elems.e[index]; - if (llir_is_elem_const(proc->module, elem, et)) { - continue; - } - llirValue *field_elem = llir_build_expr(proc, elem); - Type *t = llir_type(field_elem); - GB_ASSERT(t->kind != Type_Tuple); - llirValue *ev = llir_emit_conv(proc, field_elem, et); - llirValue *i = llir_make_const_int(proc->module->allocator, index); - result = llir_emit(proc, llir_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; - } - llirValue *sv = llir_emit(proc, llir_make_instr_vector_shuffle(proc, result, indices, index_count)); - llir_emit_store(proc, v, sv); - return llir_make_addr(v, expr); - } - llir_emit_store(proc, v, result); - } break; - - case Type_Record: { - GB_ASSERT(is_type_struct(bt)); - TypeRecord *st = &bt->Record; - if (cl->elems.count > 0) { - llir_emit_store(proc, v, llir_add_module_constant(proc->module, type, make_exact_value_compound(expr))); - for_array(field_index, cl->elems) { - AstNode *elem = cl->elems.e[field_index]; - - llirValue *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 (llir_is_elem_const(proc->module, elem, field->type)) { - continue; - } - - field_expr = llir_build_expr(proc, elem); - - GB_ASSERT(llir_type(field_expr)->kind != Type_Tuple); - - Type *ft = field->type; - llirValue *fv = llir_emit_conv(proc, field_expr, ft); - llirValue *gep = llir_emit_struct_ep(proc, v, index); - llir_emit_store(proc, gep, fv); - } - } - } break; - case Type_Array: { - if (cl->elems.count > 0) { - llir_emit_store(proc, v, llir_add_module_constant(proc->module, type, make_exact_value_compound(expr))); - for_array(i, cl->elems) { - AstNode *elem = cl->elems.e[i]; - if (llir_is_elem_const(proc->module, elem, et)) { - continue; - } - llirValue *field_expr = llir_build_expr(proc, elem); - Type *t = llir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - llirValue *ev = llir_emit_conv(proc, field_expr, et); - llirValue *gep = llir_emit_array_epi(proc, v, i); - llir_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); - llirValue *slice = llir_add_module_constant(proc->module, type, make_exact_value_compound(expr)); - GB_ASSERT(slice->kind == llirValue_ConstantSlice); - - llirValue *data = llir_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); - - for_array(i, cl->elems) { - AstNode *elem = cl->elems.e[i]; - if (llir_is_elem_const(proc->module, elem, et)) { - continue; - } - - llirValue *field_expr = llir_build_expr(proc, elem); - Type *t = llir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - llirValue *ev = llir_emit_conv(proc, field_expr, elem_type); - llirValue *offset = llir_emit_ptr_offset(proc, data, llir_make_const_int(proc->module->allocator, i)); - llir_emit_store(proc, offset, ev); - } - - llirValue *gep0 = llir_emit_struct_ep(proc, v, 0); - llirValue *gep1 = llir_emit_struct_ep(proc, v, 1); - llirValue *gep2 = llir_emit_struct_ep(proc, v, 1); - - llir_emit_store(proc, gep0, data); - llir_emit_store(proc, gep1, llir_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); - llir_emit_store(proc, gep2, llir_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); - } - } break; - } - - return llir_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 llir_make_addr(NULL, NULL); -} - -void llir_build_assign_op(llirProcedure *proc, llirAddr lhs, llirValue *value, TokenKind op) { - llirValue *old_value = llir_addr_load(proc, lhs); - Type *type = llir_type(old_value); - - llirValue *change = value; - if (is_type_pointer(type) && is_type_integer(llir_type(value))) { - change = llir_emit_conv(proc, value, default_type(llir_type(value))); - } else { - change = llir_emit_conv(proc, value, type); - } - llirValue *new_value = llir_emit_arith(proc, op, old_value, change, type); - llir_addr_store(proc, lhs, new_value); -} - -llirValue *llir_build_cond(llirProcedure *proc, AstNode *cond, llirBlock *true_block, llirBlock *false_block) { - switch (cond->kind) { - case_ast_node(pe, ParenExpr, cond); - return llir_build_cond(proc, pe->expr, true_block, false_block); - case_end; - - case_ast_node(ue, UnaryExpr, cond); - if (ue->op.kind == Token_Not) { - return llir_build_cond(proc, ue->expr, false_block, true_block); - } - case_end; - - case_ast_node(be, BinaryExpr, cond); - if (be->op.kind == Token_CmpAnd) { - llirBlock *block = llir_add_block(proc, NULL, "cmp.and"); - llir_build_cond(proc, be->left, block, false_block); - proc->curr_block = block; - return llir_build_cond(proc, be->right, true_block, false_block); - } else if (be->op.kind == Token_CmpOr) { - llirBlock *block = llir_add_block(proc, NULL, "cmp.or"); - llir_build_cond(proc, be->left, true_block, block); - proc->curr_block = block; - return llir_build_cond(proc, be->right, true_block, false_block); - } - case_end; - } - - llirValue *v = llir_build_expr(proc, cond); - v = llir_emit_conv(proc, v, t_bool); - llir_emit_if(proc, v, true_block, false_block); - return v; -} - - - - -void llir_build_stmt_list(llirProcedure *proc, AstNodeArray stmts) { - for_array(i, stmts) { - llir_build_stmt(proc, stmts.e[i]); - } -} - -void llir_build_stmt_internal(llirProcedure *proc, AstNode *node); -void llir_build_stmt(llirProcedure *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; - } - - llir_build_stmt_internal(proc, node); - - proc->module->stmt_state_flags = prev_stmt_state_flags; -} - -void llir_build_when_stmt(llirProcedure *proc, AstNodeWhenStmt *ws) { - llirValue *cond = llir_build_expr(proc, ws->cond); - GB_ASSERT(cond->kind == llirValue_Constant && - is_type_boolean(llir_type(cond))); - - GB_ASSERT(cond->Constant.value.kind == ExactValue_Bool); - if (cond->Constant.value.value_bool) { - llir_build_stmt_list(proc, ws->body->BlockStmt.stmts); - } else if (ws->else_stmt) { - switch (ws->else_stmt->kind) { - case AstNode_BlockStmt: - llir_build_stmt_list(proc, ws->else_stmt->BlockStmt.stmts); - break; - case AstNode_WhenStmt: - llir_build_when_stmt(proc, &ws->else_stmt->WhenStmt); - break; - default: - GB_PANIC("Invalid `else` statement in `when` statement"); - break; - } - } -} - -void llir_emit_increment(llirProcedure *proc, llirValue *addr) { - GB_ASSERT(is_type_pointer(llir_type(addr))); - Type *type = type_deref(llir_type(addr)); - llir_emit_store(proc, addr, llir_emit_arith(proc, Token_Add, llir_emit_load(proc, addr), v_one, type)); - -} - -void llir_build_range_indexed(llirProcedure *proc, llirValue *expr, Type *val_type, - llirValue **val_, llirValue **idx_, llirBlock **loop_, llirBlock **done_) { - llirValue *count = NULL; - Type *expr_type = base_type(llir_type(expr)); - switch (expr_type->kind) { - case Type_Array: - count = llir_make_const_int(proc->module->allocator, expr_type->Array.count); - break; - case Type_Slice: - count = llir_slice_len(proc, expr); - break; - default: - GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type)); - break; - } - - llirValue *val = NULL; - llirValue *idx = NULL; - llirBlock *loop = NULL; - llirBlock *done = NULL; - llirBlock *body = NULL; - - llirValue *index = llir_add_local_generated(proc, t_int); - llir_emit_store(proc, index, llir_make_const_int(proc->module->allocator, -1)); - - loop = llir_add_block(proc, NULL, "for.index.loop"); - llir_emit_jump(proc, loop); - proc->curr_block = loop; - - llirValue *incr = llir_emit_arith(proc, Token_Add, llir_emit_load(proc, index), v_one, t_int); - llir_emit_store(proc, index, incr); - - body = llir_add_block(proc, NULL, "for.index.body"); - done = llir_add_block(proc, NULL, "for.index.done"); - llirValue *cond = llir_emit_comp(proc, Token_Lt, incr, count); - llir_emit_if(proc, cond, body, done); - proc->curr_block = body; - - idx = llir_emit_load(proc, index); - if (val_type != NULL) { - switch (expr_type->kind) { - case Type_Array: { - val = llir_emit_load(proc, llir_emit_array_ep(proc, expr, idx)); - } break; - case Type_Slice: { - llirValue *elem = llir_slice_elem(proc, expr); - val = llir_emit_load(proc, llir_emit_ptr_offset(proc, elem, idx)); - } break; - default: - GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type)); - break; - } - } - - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; -} - - -void llir_build_range_string(llirProcedure *proc, llirValue *expr, Type *val_type, - llirValue **val_, llirValue **idx_, llirBlock **loop_, llirBlock **done_) { - llirValue *count = v_zero; - Type *expr_type = base_type(llir_type(expr)); - switch (expr_type->kind) { - case Type_Basic: - count = llir_string_len(proc, expr); - break; - default: - GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type)); - break; - } - - llirValue *val = NULL; - llirValue *idx = NULL; - llirBlock *loop = NULL; - llirBlock *done = NULL; - llirBlock *body = NULL; - - llirValue *index = llir_add_local_generated(proc, t_int); - llir_emit_store(proc, index, v_zero); - - llirValue *offset_ = llir_add_local_generated(proc, t_int); - llir_emit_store(proc, index, v_zero); - - loop = llir_add_block(proc, NULL, "for.string.loop"); - llir_emit_jump(proc, loop); - proc->curr_block = loop; - - - - body = llir_add_block(proc, NULL, "for.string.body"); - done = llir_add_block(proc, NULL, "for.string.done"); - - llirValue *offset = llir_emit_load(proc, offset_); - - llirValue *cond = llir_emit_comp(proc, Token_Lt, offset, count); - llir_emit_if(proc, cond, body, done); - proc->curr_block = body; - - - llirValue *str_elem = llir_emit_ptr_offset(proc, llir_string_elem(proc, expr), offset); - llirValue *str_len = llir_emit_arith(proc, Token_Sub, count, offset, t_int); - llirValue **args = gb_alloc_array(proc->module->allocator, llirValue *, 1); - args[0] = llir_emit_string(proc, str_elem, str_len); - llirValue *rune_and_len = llir_emit_global_call(proc, "__string_decode_rune", args, 1); - llirValue *len = llir_emit_struct_ev(proc, rune_and_len, 1); - llir_emit_store(proc, offset_, llir_emit_arith(proc, Token_Add, offset, len, t_int)); - - - idx = llir_emit_load(proc, index); - if (val_type != NULL) { - val = llir_emit_struct_ev(proc, rune_and_len, 0); - } - llir_emit_increment(proc, index); - - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; -} - -void llir_build_range_interval(llirProcedure *proc, AstNodeIntervalExpr *node, Type *val_type, - llirValue **val_, llirValue **idx_, llirBlock **loop_, llirBlock **done_) { - // TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration? - // If `lower` is changed, should `val` do so or is that not typical behaviour? - - llirValue *lower = llir_build_expr(proc, node->left); - llirValue *upper = NULL; - - llirValue *val = NULL; - llirValue *idx = NULL; - llirBlock *loop = NULL; - llirBlock *done = NULL; - llirBlock *body = NULL; - - if (val_type == NULL) { - val_type = llir_type(lower); - } - llirValue *value = llir_add_local_generated(proc, val_type); - llir_emit_store(proc, value, lower); - - llirValue *index = llir_add_local_generated(proc, t_int); - llir_emit_store(proc, index, llir_make_const_int(proc->module->allocator, 0)); - - loop = llir_add_block(proc, NULL, "for.interval.loop"); - llir_emit_jump(proc, loop); - proc->curr_block = loop; - - body = llir_add_block(proc, NULL, "for.interval.body"); - done = llir_add_block(proc, NULL, "for.interval.done"); - - upper = llir_build_expr(proc, node->right); - - llirValue *cond = llir_emit_comp(proc, Token_Lt, llir_emit_load(proc, value), upper); - llir_emit_if(proc, cond, body, done); - proc->curr_block = body; - - if (value != NULL) { - val = llir_emit_load(proc, value); - } - idx = llir_emit_load(proc, index); - - llir_emit_increment(proc, value); - llir_emit_increment(proc, index); - - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; -} - - -void llir_build_stmt_internal(llirProcedure *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_GenericDecl) { - llir_build_stmt(proc, decl); - } - case_end; - - case_ast_node(ws, WhenStmt, node); - llir_build_when_stmt(proc, ws); - case_end; - - case_ast_node(vd, ValueDecl, node); - if (vd->is_var) { - llirModule *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 (!llir_is_blank_ident(name)) { - llir_add_local_for_identifier(proc, name, true); - } - } - } else { // Tuple(s) - Array(llirAddr) lvals; - llirValueArray 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]; - llirAddr lval = llir_make_addr(NULL, NULL); - if (!llir_is_blank_ident(name)) { - llir_add_local_for_identifier(proc, name, false); - lval = llir_build_addr(proc, name); - } - - array_add(&lvals, lval); - } - - for_array(i, vd->values) { - llirValue *init = llir_build_expr(proc, vd->values.e[i]); - Type *t = llir_type(init); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - llirValue *v = llir_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; - } - llirValue *v = llir_emit_conv(proc, inits.e[i], llir_addr_type(lvals.e[i])); - llir_addr_store(proc, lvals.e[i], v); - } - } - - gb_temp_arena_memory_end(tmp); - } else { - for_array(i, vd->names) { - AstNode *ident = vd->names.e[i]; - GB_ASSERT(ident->kind == AstNode_Ident); - Entity *e = entity_of_ident(proc->module->info, ident); - GB_ASSERT(e != NULL); - switch (e->kind) { - case Entity_TypeName: { - // NOTE(bill): Generate a new name - // parent_proc.name-guid - String ts_name = e->token.string; - isize name_len = proc->name.len + 1 + ts_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(ts_name), guid); - String name = make_string(name_text, name_len-1); - - llirValue *value = llir_make_value_type_name(proc->module->allocator, - name, e->type); - map_string_set(&proc->module->type_names, hash_pointer(e->type), name); - llir_gen_global_type_name(proc->module, e, name); - } break; - case Entity_Procedure: { - DeclInfo **decl_info = map_decl_info_get(&proc->module->info->entities, hash_pointer(e)); - GB_ASSERT(decl_info != NULL); - DeclInfo *dl = *decl_info; - ast_node(pd, ProcLit, dl->proc_decl); - if (pd->body != NULL) { - CheckerInfo *info = proc->module->info; - - 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 = e->token.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); - - - llirValue *value = llir_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; - - llir_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; - - // FFI - Foreign function interace - String original_name = e->token.string; - String name = original_name; - if (pd->foreign_name.len > 0) { - name = pd->foreign_name; - } - - llirValue *value = llir_make_value_procedure(proc->module->allocator, - proc->module, e, e->type, pd->type, pd->body, name); - - value->Proc.tags = pd->tags; - - llir_module_add_value(proc->module, e, value); - llir_build_proc(value, proc); - - if (value->Proc.tags & ProcTag_foreign) { - HashKey key = hash_string(name); - llirValue **prev_value = map_llir_value_get(&proc->module->members, key); - if (prev_value == NULL) { - // NOTE(bill): Don't do mutliple declarations in the IR - map_llir_value_set(&proc->module->members, key, value); - } - } else { - array_add(&proc->children, &value->Proc); - } - } - } break; - } - } - } - case_end; - - case_ast_node(as, AssignStmt, node); - llir_emit_comment(proc, str_lit("AssignStmt")); - - llirModule *m = proc->module; - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); - - switch (as->op.kind) { - case Token_Eq: { - Array(llirAddr) lvals; - array_init(&lvals, m->tmp_allocator); - - for_array(i, as->lhs) { - AstNode *lhs = as->lhs.e[i]; - llirAddr lval = {0}; - if (!llir_is_blank_ident(lhs)) { - lval = llir_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]; - llirValue *init = llir_build_expr(proc, rhs); - llir_addr_store(proc, lvals.e[0], init); - } else { - llirValueArray inits; - array_init_reserve(&inits, m->tmp_allocator, lvals.count); - - for_array(i, as->rhs) { - llirValue *init = llir_build_expr(proc, as->rhs.e[i]); - array_add(&inits, init); - } - - for_array(i, inits) { - llir_addr_store(proc, lvals.e[i], inits.e[i]); - } - } - } else { - llirValueArray inits; - array_init_reserve(&inits, m->tmp_allocator, lvals.count); - - for_array(i, as->rhs) { - llirValue *init = llir_build_expr(proc, as->rhs.e[i]); - Type *t = llir_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]; - llirValue *v = llir_emit_struct_ev(proc, init, i); - array_add(&inits, v); - } - } else { - array_add(&inits, init); - } - } - - for_array(i, inits) { - llir_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 + - llirAddr lhs = llir_build_addr(proc, as->lhs.e[0]); - llirValue *value = llir_build_expr(proc, as->rhs.e[0]); - llir_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 - llir_build_expr(proc, es->expr); - case_end; - - case_ast_node(bs, BlockStmt, node); - llir_open_scope(proc); - llir_build_stmt_list(proc, bs->stmts); - llir_close_scope(proc, llirDeferExit_Default, NULL); - case_end; - - case_ast_node(ds, DeferStmt, node); - llir_emit_comment(proc, str_lit("DeferStmt")); - isize scope_index = proc->scope_index; - if (ds->stmt->kind == AstNode_BlockStmt) { - scope_index--; - } - llir_add_defer_node(proc, scope_index, ds->stmt); - case_end; - - case_ast_node(rs, ReturnStmt, node); - llir_emit_comment(proc, str_lit("ReturnStmt")); - llirValue *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 = llir_emit_conv(proc, llir_build_expr(proc, rs->results.e[0]), e->type); - } else { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - - llirValueArray results; - array_init_reserve(&results, proc->module->tmp_allocator, return_count); - - for_array(res_index, rs->results) { - llirValue *res = llir_build_expr(proc, rs->results.e[res_index]); - Type *t = llir_type(res); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - llirValue *v = llir_emit_struct_ev(proc, res, i); - array_add(&results, v); - } - } else { - array_add(&results, res); - } - } - - Type *ret_type = proc->type->Proc.results; - v = llir_add_local_generated(proc, ret_type); - for_array(i, results) { - Entity *e = return_type_tuple->variables[i]; - llirValue *res = llir_emit_conv(proc, results.e[i], e->type); - llirValue *field = llir_emit_struct_ep(proc, v, i); - llir_emit_store(proc, field, res); - } - - v = llir_emit_load(proc, v); - - gb_temp_arena_memory_end(tmp); - } - llir_emit_return(proc, v); - - case_end; - - case_ast_node(is, IfStmt, node); - llir_emit_comment(proc, str_lit("IfStmt")); - if (is->init != NULL) { - llirBlock *init = llir_add_block(proc, node, "if.init"); - llir_emit_jump(proc, init); - proc->curr_block = init; - llir_build_stmt(proc, is->init); - } - llirBlock *then = llir_add_block(proc, node, "if.then"); - llirBlock *done = llir_add_block(proc, node, "if.done"); // NOTE(bill): Append later - llirBlock *else_ = done; - if (is->else_stmt != NULL) { - else_ = llir_add_block(proc, is->else_stmt, "if.else"); - } - - llir_build_cond(proc, is->cond, then, else_); - proc->curr_block = then; - - llir_open_scope(proc); - llir_build_stmt(proc, is->body); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_emit_jump(proc, done); - - if (is->else_stmt != NULL) { - proc->curr_block = else_; - - llir_open_scope(proc); - llir_build_stmt(proc, is->else_stmt); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_emit_jump(proc, done); - } - proc->curr_block = done; - case_end; - - case_ast_node(ws, WhileStmt, node); - llir_emit_comment(proc, str_lit("WhileStmt")); - if (ws->init != NULL) { - llirBlock *init = llir_add_block(proc, node, "while.init"); - llir_emit_jump(proc, init); - proc->curr_block = init; - llir_build_stmt(proc, ws->init); - } - llirBlock *body = llir_add_block(proc, node, "while.body"); - llirBlock *done = llir_add_block(proc, node, "while.done"); // NOTE(bill): Append later - - llirBlock *loop = body; - - if (ws->cond != NULL) { - loop = llir_add_block(proc, node, "while.loop"); - } - llir_emit_jump(proc, loop); - proc->curr_block = loop; - if (loop != body) { - llir_build_cond(proc, ws->cond, body, done); - proc->curr_block = body; - } - - llir_push_target_list(proc, done, loop, NULL); - - llir_open_scope(proc); - llir_build_stmt(proc, ws->body); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_pop_target_list(proc); - llir_emit_jump(proc, loop); - - proc->curr_block = done; - case_end; - - - case_ast_node(rs, ForStmt, node); - llir_emit_comment(proc, str_lit("ForStmt")); - - Type *val_type = NULL; - Type *idx_type = NULL; - if (rs->value != NULL && !llir_is_blank_ident(rs->value)) { - val_type = type_of_expr(proc->module->info, rs->value); - } - if (rs->index != NULL && !llir_is_blank_ident(rs->index)) { - idx_type = type_of_expr(proc->module->info, rs->index); - } - - if (val_type != NULL) { - llir_add_local_for_identifier(proc, rs->value, true); - } - if (idx_type != NULL) { - llir_add_local_for_identifier(proc, rs->index, true); - } - - llirValue *val = NULL; - llirValue *index = NULL; - llirBlock *loop = NULL; - llirBlock *done = NULL; - - if (rs->expr->kind == AstNode_IntervalExpr) { - llir_build_range_interval(proc, &rs->expr->IntervalExpr, val_type, &val, &index, &loop, &done); - } else { - Type *expr_type = type_of_expr(proc->module->info, rs->expr); - Type *et = base_type(type_deref(expr_type)); - bool deref = is_type_pointer(expr_type); - switch (et->kind) { - case Type_Array: { - llirValue *array = llir_build_addr(proc, rs->expr).addr; - if (deref) { - array = llir_emit_load(proc, array); - } - llir_build_range_indexed(proc, array, val_type, &val, &index, &loop, &done); - } break; - case Type_Slice: { - llirValue *slice = llir_build_expr(proc, rs->expr); - if (deref) { - slice = llir_emit_load(proc, slice); - } - llir_build_range_indexed(proc, slice, val_type, &val, &index, &loop, &done); - } break; - case Type_Basic: { - llirValue *string = llir_build_expr(proc, rs->expr); - if (deref) { - string = llir_emit_load(proc, string); - } - if (is_type_untyped(expr_type)) { - llirValue *s = llir_add_local_generated(proc, t_string); - llir_emit_store(proc, s, string); - string = llir_emit_load(proc, s); - } - llir_build_range_string(proc, string, val_type, &val, &index, &loop, &done); - } break; - default: - GB_PANIC("Cannot range over %s", type_to_string(expr_type)); - break; - } - } - - llirAddr val_addr = {0}; - llirAddr idx_addr = {0}; - if (val_type != NULL) { - val_addr = llir_build_addr(proc, rs->value); - } - if (idx_type != NULL) { - idx_addr = llir_build_addr(proc, rs->index); - } - if (val_type != NULL) { - llir_addr_store(proc, val_addr, val); - } - if (idx_type != NULL) { - llir_addr_store(proc, idx_addr, index); - } - - llir_push_target_list(proc, done, loop, NULL); - - llir_open_scope(proc); - llir_build_stmt(proc, rs->body); - llir_close_scope(proc, llirDeferExit_Default, NULL); - - llir_pop_target_list(proc); - llir_emit_jump(proc, loop); - proc->curr_block = done; - case_end; - - case_ast_node(ms, MatchStmt, node); - llir_emit_comment(proc, str_lit("MatchStmt")); - if (ms->init != NULL) { - llir_build_stmt(proc, ms->init); - } - llirValue *tag = v_true; - if (ms->tag != NULL) { - tag = llir_build_expr(proc, ms->tag); - } - llirBlock *done = llir_add_block(proc, node, "match.done"); // NOTE(bill): Append later - - ast_node(body, BlockStmt, ms->body); - - AstNodeArray default_stmts = {0}; - llirBlock *default_fall = NULL; - llirBlock *default_block = NULL; - - llirBlock *fall = NULL; - bool append_fall = false; - - isize case_count = body->stmts.count; - for_array(i, body->stmts) { - AstNode *clause = body->stmts.e[i]; - llirBlock *body = fall; - - ast_node(cc, CaseClause, clause); - - if (body == NULL) { - if (cc->list.count == 0) { - body = llir_add_block(proc, clause, "match.dflt.body"); - } else { - body = llir_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 = llir_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; - } - - llirBlock *next_cond = NULL; - for_array(j, cc->list) { - AstNode *expr = cc->list.e[j]; - next_cond = llir_add_block(proc, clause, "match.case.next"); - - llirValue *cond = llir_emit_comp(proc, Token_CmpEq, tag, llir_build_expr(proc, expr)); - llir_emit_if(proc, cond, body, next_cond); - proc->curr_block = next_cond; - } - proc->curr_block = body; - - llir_push_target_list(proc, done, NULL, fall); - llir_open_scope(proc); - llir_build_stmt_list(proc, cc->stmts); - llir_close_scope(proc, llirDeferExit_Default, body); - llir_pop_target_list(proc); - - llir_emit_jump(proc, done); - proc->curr_block = next_cond; - } - - if (default_block != NULL) { - llir_emit_jump(proc, default_block); - proc->curr_block = default_block; - - llir_push_target_list(proc, done, NULL, default_fall); - llir_open_scope(proc); - llir_build_stmt_list(proc, default_stmts); - llir_close_scope(proc, llirDeferExit_Default, default_block); - llir_pop_target_list(proc); - } - - llir_emit_jump(proc, done); - proc->curr_block = done; - case_end; - - - case_ast_node(ms, TypeMatchStmt, node); - llir_emit_comment(proc, str_lit("TypeMatchStmt")); - gbAllocator allocator = proc->module->allocator; - - llirValue *parent = llir_build_expr(proc, ms->tag); - bool is_union_ptr = false; - bool is_any = false; - GB_ASSERT(check_valid_type_match_type(llir_type(parent), &is_union_ptr, &is_any)); - - llirValue *tag_index = NULL; - llirValue *union_data = NULL; - if (is_union_ptr) { - llir_emit_comment(proc, str_lit("get union's tag")); - tag_index = llir_emit_load(proc, llir_emit_union_tag_ptr(proc, parent)); - union_data = llir_emit_conv(proc, parent, t_rawptr); - } - - llirBlock *start_block = llir_add_block(proc, node, "type-match.case.first"); - llir_emit_jump(proc, start_block); - proc->curr_block = start_block; - - llirBlock *done = llir_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}; - llirBlock *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 = llir_add_block(proc, clause, "type-match.dflt.body"); - continue; - } - - - llirBlock *body = llir_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)); - - llirBlock *next_cond = NULL; - llirValue *cond = NULL; - - if (is_union_ptr) { - Type *bt = type_deref(tag_var_entity->type); - llirValue *index = NULL; - Type *ut = base_type(type_deref(llir_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 = llir_make_const_int(allocator, field_index); - break; - } - } - GB_ASSERT(index != NULL); - - llirValue *tag_var = llir_add_local(proc, tag_var_entity); - llirValue *data_ptr = llir_emit_conv(proc, union_data, tag_var_entity->type); - llir_emit_store(proc, tag_var, data_ptr); - - cond = llir_emit_comp(proc, Token_CmpEq, tag_index, index); - } else if (is_any) { - Type *type = tag_var_entity->type; - llirValue *any_data = llir_emit_struct_ev(proc, parent, 1); - llirValue *data = llir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type)); - llir_module_add_value(proc->module, tag_var_entity, data); - - llirValue *any_ti = llir_emit_struct_ev(proc, parent, 0); - llirValue *case_ti = llir_type_info(proc, type); - cond = llir_emit_comp(proc, Token_CmpEq, any_ti, case_ti); - } else { - GB_PANIC("Invalid type for type match statement"); - } - - next_cond = llir_add_block(proc, clause, "type-match.case.next"); - llir_emit_if(proc, cond, body, next_cond); - proc->curr_block = next_cond; - - proc->curr_block = body; - - llir_push_target_list(proc, done, NULL, NULL); - llir_open_scope(proc); - llir_build_stmt_list(proc, cc->stmts); - llir_close_scope(proc, llirDeferExit_Default, body); - llir_pop_target_list(proc); - - llir_emit_jump(proc, done); - proc->curr_block = next_cond; - } - - if (default_block != NULL) { - llir_emit_jump(proc, default_block); - proc->curr_block = default_block; - - llir_push_target_list(proc, done, NULL, NULL); - llir_open_scope(proc); - llir_build_stmt_list(proc, default_stmts); - llir_close_scope(proc, llirDeferExit_Default, default_block); - llir_pop_target_list(proc); - } - - llir_emit_jump(proc, done); - proc->curr_block = done; - case_end; - - case_ast_node(bs, BranchStmt, node); - llirBlock *block = NULL; - switch (bs->token.kind) { - case Token_break: - for (llirTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->break_; - } - break; - case Token_continue: - for (llirTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->continue_; - } - break; - case Token_fallthrough: - for (llirTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->fallthrough_; - } - break; - } - if (block != NULL) { - llir_emit_defer_stmts(proc, llirDeferExit_Branch, block); - } - switch (bs->token.kind) { - case Token_break: llir_emit_comment(proc, str_lit("break")); break; - case Token_continue: llir_emit_comment(proc, str_lit("continue")); break; - case Token_fallthrough: llir_emit_comment(proc, str_lit("fallthrough")); break; - } - llir_emit_jump(proc, block); - llir_emit_unreachable(proc); - case_end; - - - - case_ast_node(pa, PushAllocator, node); - llir_emit_comment(proc, str_lit("PushAllocator")); - llir_open_scope(proc); - - llirValue *context_ptr = llir_find_implicit_value_backing(proc, ImplicitValue_context); - llirValue *prev_context = llir_add_local_generated(proc, t_context); - llir_emit_store(proc, prev_context, llir_emit_load(proc, context_ptr)); - - llir_add_defer_instr(proc, proc->scope_index, llir_make_instr_store(proc, context_ptr, llir_emit_load(proc, prev_context))); - - llirValue *gep = llir_emit_struct_ep(proc, context_ptr, 1); - llir_emit_store(proc, gep, llir_build_expr(proc, pa->expr)); - - llir_build_stmt(proc, pa->body); - - llir_close_scope(proc, llirDeferExit_Default, NULL); - case_end; - - - case_ast_node(pa, PushContext, node); - llir_emit_comment(proc, str_lit("PushContext")); - llir_open_scope(proc); - - llirValue *context_ptr = llir_find_implicit_value_backing(proc, ImplicitValue_context); - llirValue *prev_context = llir_add_local_generated(proc, t_context); - llir_emit_store(proc, prev_context, llir_emit_load(proc, context_ptr)); - - llir_add_defer_instr(proc, proc->scope_index, llir_make_instr_store(proc, context_ptr, llir_emit_load(proc, prev_context))); - - llir_emit_store(proc, context_ptr, llir_build_expr(proc, pa->expr)); - - llir_build_stmt(proc, pa->body); - - llir_close_scope(proc, llirDeferExit_Default, NULL); - case_end; - - - } -} - - - - - - - -//////////////////////////////////////////////////////////////// -// -// @Procedure -// -//////////////////////////////////////////////////////////////// - -void llir_number_proc_registers(llirProcedure *proc) { - i32 reg_index = 0; - for_array(i, proc->blocks) { - llirBlock *b = proc->blocks.e[i]; - b->index = i; - for_array(j, b->instrs) { - llirValue *value = b->instrs.e[j]; - GB_ASSERT(value->kind == llirValue_Instr); - llirInstr *instr = &value->Instr; - if (llir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions - continue; - } - value->index = reg_index; - reg_index++; - } - } -} - -void llir_begin_procedure_body(llirProcedure *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 = llir_add_block(proc, proc->type_expr, "decls"); - proc->entry_block = llir_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]; - if (!str_eq(e->token.string, str_lit("")) && - !str_eq(e->token.string, str_lit("_"))) { - llirValue *param = llir_add_param(proc, e); - array_add(&proc->params, param); - } - } - } -} - - -void llir_end_procedure_body(llirProcedure *proc) { - if (proc->type->Proc.result_count == 0) { - llir_emit_return(proc, NULL); - } - - if (proc->curr_block->instrs.count == 0) { - llir_emit_unreachable(proc); - } - - proc->curr_block = proc->decl_block; - llir_emit_jump(proc, proc->entry_block); - - llir_number_proc_registers(proc); -} - - -void llir_insert_code_before_proc(llirProcedure* proc, llirProcedure *parent) { - if (parent == NULL) { - if (str_eq(proc->name, str_lit("main"))) { - llir_emit_startup_runtime(proc); - } - } -} - -void llir_build_proc(llirValue *value, llirProcedure *parent) { - llirProcedure *proc = &value->Proc; - - proc->parent = parent; - - if (proc->entity != NULL) { - llirModule *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; - llirDebugInfo *di_file = NULL; - - llirDebugInfo **di_file_found = map_llir_debug_info_get(&m->debug_info, hash_pointer(f)); - if (di_file_found) { - di_file = *di_file_found; - GB_ASSERT(di_file->kind == llirDebugInfo_File); - } else { - di_file = llir_add_debug_info_file(proc, f); - } - - llir_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; - } - - - llir_begin_procedure_body(proc); - llir_insert_code_before_proc(proc, parent); - llir_build_stmt(proc, proc->body); - llir_end_procedure_body(proc); - - proc->module->stmt_state_flags = prev_stmt_state_flags; - } -} - - - - - - - -//////////////////////////////////////////////////////////////// -// -// @Module -// -//////////////////////////////////////////////////////////////// - - - -void llir_module_add_value(llirModule *m, Entity *e, llirValue *v) { - map_llir_value_set(&m->values, hash_pointer(e), v); -} - -void llir_init_module(llirModule *m, Checker *c, BuildContext *build_context) { - // 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(llirValue); - 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; - m->build_context = build_context; - - map_llir_value_init(&m->values, heap_allocator()); - map_llir_value_init(&m->members, heap_allocator()); - map_llir_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(LLIR_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)); - llirValue *g = llir_make_value_global(m->allocator, e, NULL); - g->Global.is_private = true; - llir_module_add_value(m, e, g); - map_llir_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(LLIR_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)); - llirValue *g = llir_make_value_global(m->allocator, e, NULL); - llir_module_add_value(m, e, g); - map_llir_value_set(&m->members, hash_string(name), g); - } - } - - { - llirDebugInfo *di = llir_alloc_debug_info(m->allocator, llirDebugInfo_CompileUnit); - di->CompileUnit.file = m->info->files.entries.e[0].value; // Zeroth is the init file - di->CompileUnit.producer = str_lit("odin"); - - map_llir_debug_info_set(&m->debug_info, hash_pointer(m), di); - } -} - -void llir_destroy_module(llirModule *m) { - map_llir_value_destroy(&m->values); - map_llir_value_destroy(&m->members); - map_string_destroy(&m->type_names); - map_llir_debug_info_destroy(&m->debug_info); - array_free(&m->procs_to_generate); - gb_arena_free(&m->arena); -} - - - -//////////////////////////////////////////////////////////////// -// -// @Code Generation -// -//////////////////////////////////////////////////////////////// - - -bool llir_gen_init(llirGen *s, Checker *c, BuildContext *build_context) { - if (global_error_collector.count != 0) { - return false; - } - - isize tc = c->parser->total_token_count; - if (tc < 2) { - return false; - } - - llir_init_module(&s->module, c, build_context); - 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 llir_gen_destroy(llirGen *s) { - llir_destroy_module(&s->module); - gb_file_close(&s->output_file); -} - -String llir_mangle_name(llirGen *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 - - llirModule *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); -} - -llirValue *llir_get_type_info_ptr(llirProcedure *proc, llirValue *type_info_data, Type *type) { - i32 index = cast(i32)llir_type_info_index(proc->module->info, type); - // gb_printf_err("%d %s\n", index, type_to_string(type)); - llirValue *ptr = llir_emit_array_epi(proc, type_info_data, index); - return llir_emit_bitcast(proc, ptr, t_type_info_ptr); -} - -llirValue *llir_type_info_member_offset(llirProcedure *proc, llirValue *data, isize count, i32 *index) { - llirValue *offset = llir_emit_array_epi(proc, data, *index); - *index += count; - return offset; -} - -void llir_gen_tree(llirGen *s) { - llirModule *m = &s->module; - CheckerInfo *info = m->info; - gbAllocator a = m->allocator; - - if (v_zero == NULL) { - v_zero = llir_make_const_int (m->allocator, 0); - v_one = llir_make_const_int (m->allocator, 1); - v_zero32 = llir_make_const_i32 (m->allocator, 0); - v_one32 = llir_make_const_i32 (m->allocator, 1); - v_two32 = llir_make_const_i32 (m->allocator, 2); - v_false = llir_make_const_bool(m->allocator, false); - v_true = llir_make_const_bool(m->allocator, true); - } - - 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; - } - } - } - } - - typedef struct llirGlobalVariable { - llirValue *var, *init; - DeclInfo *decl; - } llirGlobalVariable; - Array(llirGlobalVariable) global_variables; - array_init_reserve(&global_variables, m->tmp_allocator, global_variable_max_count); - - 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) { - } else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) { - } else { - name = llir_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); - llir_gen_global_type_name(m, e, name); - break; - - case Entity_Variable: { - llirValue *g = llir_make_value_global(a, e, NULL); - if (decl->var_decl_tags & VarDeclTag_thread_local) { - g->Global.is_thread_local = true; - } - llirGlobalVariable 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 = llir_add_module_constant(m, tav->type, v); - // } - } - } - } - - if (g->Global.value == NULL) { - array_add(&global_variables, var); - } - - llir_module_add_value(m, e, g); - map_llir_value_set(&m->members, hash_string(name), g); - } break; - - case Entity_Procedure: { - ast_node(pd, ProcLit, decl->proc_decl); - 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; - } - - llirValue *p = llir_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); - p->Proc.tags = pd->tags; - - llir_module_add_value(m, e, p); - HashKey hash_name = hash_string(name); - if (map_llir_value_get(&m->members, hash_name) == NULL) { - map_llir_value_set(&m->members, hash_name, p); - } - } break; - } - } - - for_array(i, m->members.entries) { - MapSsaValueEntry *entry = &m->members.entries.e[i]; - llirValue *v = entry->value; - if (v->kind == llirValue_Proc) { - llir_build_proc(v, NULL); - } - } - - llirDebugInfo *compile_unit = m->debug_info.entries.e[0].value; - GB_ASSERT(compile_unit->kind == llirDebugInfo_CompileUnit); - llirDebugInfo *all_procs = llir_alloc_debug_info(m->allocator, llirDebugInfo_AllProcs); - - isize all_proc_max_count = 0; - for_array(i, m->debug_info.entries) { - MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i]; - llirDebugInfo *di = entry->value; - di->id = i; - if (di->kind == llirDebugInfo_Proc) { - all_proc_max_count++; - } - } - - array_init_reserve(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); - map_llir_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]; - llirDebugInfo *di = entry->value; - di->id = i; - if (di->kind == llirDebugInfo_Proc) { - array_add(&all_procs->AllProcs.procs, di); - } - } - -#if defined(GB_SYSTEM_WINDOWS) - if (m->build_context->is_dll && !has_dll_main) { - // DllMain :: proc(inst: rawptr, reason: u32, reserved: rawptr) -> i32 - String name = str_lit("DllMain"); - Type *proc_params = make_type_tuple(a); - Type *proc_results = make_type_tuple(a); - - Scope *proc_scope = gb_alloc_item(a, Scope); - - proc_params->Tuple.variables = gb_alloc_array(a, Entity *, 3); - proc_params->Tuple.variable_count = 3; - - proc_results->Tuple.variables = gb_alloc_array(a, Entity *, 1); - proc_results->Tuple.variable_count = 1; - - proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); - proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, make_token_ident(str_lit("reason")), t_i32, false); - proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); - - proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false); - - - Type *proc_type = make_type_proc(a, proc_scope, - proc_params, 3, - proc_results, 1, false, ProcCC_Std); - - AstNode *body = gb_alloc_item(a, AstNode); - Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); - llirValue *p = llir_make_value_procedure(a, m, e, proc_type, NULL, body, name); - - map_llir_value_set(&m->values, hash_pointer(e), p); - map_llir_value_set(&m->members, hash_string(name), p); - - llirProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? - e->Procedure.link_name = name; - - llir_begin_procedure_body(proc); - - // NOTE(bill): https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx - // DLL_PROCESS_ATTACH == 1 - - llirAddr reason_addr = llir_build_addr_from_entity(proc, proc_params->Tuple.variables[1], NULL); - llirValue *cond = llir_emit_comp(proc, Token_CmpEq, llir_addr_load(proc, reason_addr), v_one32); - llirBlock *then = llir_add_block(proc, NULL, "if.then"); - llirBlock *done = llir_add_block(proc, NULL, "if.done"); // NOTE(bill): Append later - llir_emit_if(proc, cond, then, done); - proc->curr_block = then; - llir_emit_global_call(proc, "main", NULL, 0); - llir_emit_jump(proc, done); - proc->curr_block = done; - - llir_emit_return(proc, v_one32); - - - llir_end_procedure_body(proc); - } -#endif -#if defined(GB_SYSTEM_WINDOWS) - if (!m->build_context->is_dll && !has_win_main) { - // WinMain :: proc(inst, prev: rawptr, cmd_line: ^byte, cmd_show: i32) -> i32 - String name = str_lit("WinMain"); - Type *proc_params = make_type_tuple(a); - Type *proc_results = make_type_tuple(a); - - Scope *proc_scope = gb_alloc_item(a, Scope); - - proc_params->Tuple.variables = gb_alloc_array(a, Entity *, 4); - proc_params->Tuple.variable_count = 4; - - proc_results->Tuple.variables = gb_alloc_array(a, Entity *, 1); - proc_results->Tuple.variable_count = 1; - - proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); - proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false); - proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_u8_ptr, false); - proc_params->Tuple.variables[3] = make_entity_param(a, proc_scope, blank_token, t_i32, false); - - proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false); - - - Type *proc_type = make_type_proc(a, proc_scope, - proc_params, 4, - proc_results, 1, false, ProcCC_Std); - - AstNode *body = gb_alloc_item(a, AstNode); - Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); - llirValue *p = llir_make_value_procedure(a, m, e, proc_type, NULL, body, name); - - m->entry_point_entity = e; - - map_llir_value_set(&m->values, hash_pointer(e), p); - map_llir_value_set(&m->members, hash_string(name), p); - - llirProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? - e->Procedure.link_name = name; - - llir_begin_procedure_body(proc); - llir_emit_global_call(proc, "main", NULL, 0); - llir_emit_return(proc, v_one32); - llir_end_procedure_body(proc); - } -#endif - { // Startup Runtime - // Cleanup(bill): probably better way of doing code insertion - String name = str_lit(LLIR_STARTUP_RUNTIME_PROC_NAME); - Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), - NULL, 0, - NULL, 0, false, ProcCC_Odin); - AstNode *body = gb_alloc_item(a, AstNode); - Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0); - llirValue *p = llir_make_value_procedure(a, m, e, proc_type, NULL, body, name); - - map_llir_value_set(&m->values, hash_pointer(e), p); - map_llir_value_set(&m->members, hash_string(name), p); - - llirProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? - - llir_begin_procedure_body(proc); - - // TODO(bill): Should do a dependency graph do check which order to initialize them in? - for_array(i, global_variables) { - llirGlobalVariable *var = &global_variables.e[i]; - if (var->decl->init_expr != NULL) { - var->init = llir_build_expr(proc, var->decl->init_expr); - } - } - - // NOTE(bill): Initialize constants first - for_array(i, global_variables) { - llirGlobalVariable *var = &global_variables.e[i]; - if (var->init != NULL) { - if (var->init->kind == llirValue_Constant) { - llir_emit_store(proc, var->var, var->init); - } - } - } - - for_array(i, global_variables) { - llirGlobalVariable *var = &global_variables.e[i]; - if (var->init != NULL) { - if (var->init->kind != llirValue_Constant) { - llir_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 - llirValue *type_info_data = NULL; - llirValue *type_info_member_data = NULL; - - llirValue **found = NULL; - found = map_llir_value_get(&proc->module->members, hash_string(str_lit(LLIR_TYPE_INFO_DATA_NAME))); - GB_ASSERT(found != NULL); - type_info_data = *found; - - found = map_llir_value_get(&proc->module->members, hash_string(str_lit(LLIR_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; - - llirValue *tag = NULL; - - switch (t->kind) { - case Type_Named: { - tag = llir_add_local_generated(proc, t_type_info_named); - - // TODO(bill): Which is better? The mangled name or actual name? - llirValue *name = llir_make_const_string(a, t->Named.type_name->token.string); - llirValue *gtip = llir_get_type_info_ptr(proc, type_info_data, t->Named.base); - - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), name); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), gtip); - } break; - - case Type_Basic: - switch (t->Basic.kind) { - case Basic_bool: - tag = llir_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 = llir_add_local_generated(proc, t_type_info_integer); - bool is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; - llirValue *bits = llir_make_const_int(a, type_size_of(m->sizes, a, t)); - llirValue *is_signed = llir_make_const_bool(a, !is_unsigned); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), bits); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), is_signed); - } break; - - // case Basic_f16: - case Basic_f32: - case Basic_f64: - // case Basic_f128: - { - tag = llir_add_local_generated(proc, t_type_info_float); - llirValue *bits = llir_make_const_int(a, type_size_of(m->sizes, a, t)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), bits); - } break; - - case Basic_rawptr: - tag = llir_add_local_generated(proc, t_type_info_pointer); - break; - - case Basic_string: - tag = llir_add_local_generated(proc, t_type_info_string); - break; - - case Basic_any: - tag = llir_add_local_generated(proc, t_type_info_any); - break; - } - break; - - case Type_Pointer: { - tag = llir_add_local_generated(proc, t_type_info_pointer); - llirValue *gep = llir_get_type_info_ptr(proc, type_info_data, t->Pointer.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), gep); - } break; - case Type_Maybe: { - tag = llir_add_local_generated(proc, t_type_info_maybe); - llirValue *gep = llir_get_type_info_ptr(proc, type_info_data, t->Maybe.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), gep); - } break; - case Type_Array: { - tag = llir_add_local_generated(proc, t_type_info_array); - llirValue *gep = llir_get_type_info_ptr(proc, type_info_data, t->Array.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Array.elem); - llirValue *elem_size = llir_emit_struct_ep(proc, tag, 1); - llir_emit_store(proc, elem_size, llir_make_const_int(a, ez)); - - llirValue *count = llir_emit_struct_ep(proc, tag, 2); - llir_emit_store(proc, count, llir_make_const_int(a, t->Array.count)); - - } break; - case Type_Slice: { - tag = llir_add_local_generated(proc, t_type_info_slice); - llirValue *gep = llir_get_type_info_ptr(proc, type_info_data, t->Slice.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Slice.elem); - llirValue *elem_size = llir_emit_struct_ep(proc, tag, 1); - llir_emit_store(proc, elem_size, llir_make_const_int(a, ez)); - - } break; - case Type_Vector: { - tag = llir_add_local_generated(proc, t_type_info_vector); - llirValue *gep = llir_get_type_info_ptr(proc, type_info_data, t->Vector.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Vector.elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), llir_make_const_int(a, ez)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 2), llir_make_const_int(a, t->Vector.count)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 3), llir_make_const_int(a, type_align_of(m->sizes, a, t))); - - } break; - case Type_Record: { - switch (t->Record.kind) { - case TypeRecord_Struct: { - tag = llir_add_local_generated(proc, t_type_info_struct); - - { - llirValue *packed = llir_make_const_bool(a, t->Record.struct_is_packed); - llirValue *ordered = llir_make_const_bool(a, t->Record.struct_is_ordered); - llirValue *size = llir_make_const_int(a, type_size_of(m->sizes, a, t)); - llirValue *align = llir_make_const_int(a, type_align_of(m->sizes, a, t)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), size); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 2), align); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 3), packed); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 4), ordered); - } - - llirValue *memory = llir_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]; - llirValue *tip = llir_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); - - llirValue *field = llir_emit_ptr_offset(proc, memory, llir_make_const_int(a, source_index)); - llirValue *name = llir_emit_struct_ep(proc, field, 0); - llirValue *type_info = llir_emit_struct_ep(proc, field, 1); - llirValue *offset = llir_emit_struct_ep(proc, field, 2); - - if (f->token.string.len > 0) { - llir_emit_store(proc, name, llir_make_const_string(a, f->token.string)); - } - llir_emit_store(proc, type_info, tip); - llir_emit_store(proc, offset, llir_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); - llirValue *slice = llir_emit_struct_ep(proc, tag, 0); - llirValue *field_count = llir_make_const_int(a, t->Record.field_count); - - llirValue *elem = llir_emit_struct_ep(proc, slice, 0); - llirValue *len = llir_emit_struct_ep(proc, slice, 1); - llirValue *cap = llir_emit_struct_ep(proc, slice, 2); - - llir_emit_store(proc, elem, memory); - llir_emit_store(proc, len, field_count); - llir_emit_store(proc, cap, field_count); - } break; - case TypeRecord_Union: - tag = llir_add_local_generated(proc, t_type_info_union); - { - llirValue *size = llir_make_const_int(a, type_size_of(m->sizes, a, t)); - llirValue *align = llir_make_const_int(a, type_align_of(m->sizes, a, t)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), size); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 2), align); - } - break; - case TypeRecord_RawUnion: { - tag = llir_add_local_generated(proc, t_type_info_raw_union); - { - llirValue *size = llir_make_const_int(a, type_size_of(m->sizes, a, t)); - llirValue *align = llir_make_const_int(a, type_align_of(m->sizes, a, t)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 1), size); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 2), align); - } - - llirValue *memory = llir_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++) { - llirValue *field = llir_emit_ptr_offset(proc, memory, llir_make_const_int(a, i)); - llirValue *name = llir_emit_struct_ep(proc, field, 0); - llirValue *type_info = llir_emit_struct_ep(proc, field, 1); - llirValue *offset = llir_emit_struct_ep(proc, field, 2); - - Entity *f = t->Record.fields[i]; - llirValue *tip = llir_get_type_info_ptr(proc, type_info_data, f->type); - - if (f->token.string.len > 0) { - llir_emit_store(proc, name, llir_make_const_string(a, f->token.string)); - } - llir_emit_store(proc, type_info, tip); - llir_emit_store(proc, offset, llir_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); - llirValue *slice = llir_emit_struct_ep(proc, tag, 0); - llirValue *field_count = llir_make_const_int(a, t->Record.field_count); - - llirValue *elem = llir_emit_struct_ep(proc, slice, 0); - llirValue *len = llir_emit_struct_ep(proc, slice, 1); - llirValue *cap = llir_emit_struct_ep(proc, slice, 2); - - llir_emit_store(proc, elem, memory); - llir_emit_store(proc, len, field_count); - llir_emit_store(proc, cap, field_count); - } break; - case TypeRecord_Enum: - tag = llir_add_local_generated(proc, t_type_info_enum); - { - GB_ASSERT(t->Record.enum_base_type != NULL); - llirValue *base = llir_type_info(proc, t->Record.enum_base_type); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 0), base); - - if (t->Record.field_count > 0) { - Entity **fields = t->Record.fields; - isize count = t->Record.field_count; - llirValue *name_array = NULL; - - { - Token token = {Token_Ident}; - 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 = llir_make_value_global(a, e, NULL); - name_array->Global.is_private = true; - llir_module_add_value(m, e, name_array); - map_llir_value_set(&m->members, hash_string(token.string), name_array); - } - - for (isize i = 0; i < count; i++) { - llirValue *name_ep = llir_emit_array_epi(proc, name_array, i); - llir_emit_store(proc, name_ep, llir_make_const_string(a, fields[i]->token.string)); - } - - llirValue *v_count = llir_make_const_int(a, count); - - llirValue *names = llir_emit_struct_ep(proc, tag, 1); - llirValue *name_array_elem = llir_array_elem(proc, name_array); - - llir_emit_store(proc, llir_emit_struct_ep(proc, names, 0), name_array_elem); - llir_emit_store(proc, llir_emit_struct_ep(proc, names, 1), v_count); - llir_emit_store(proc, llir_emit_struct_ep(proc, names, 2), v_count); - } - } - break; - } - } break; - - case Type_Tuple: { - tag = llir_add_local_generated(proc, t_type_info_tuple); - - { - llirValue *align = llir_make_const_int(a, type_align_of(m->sizes, a, t)); - llir_emit_store(proc, llir_emit_struct_ep(proc, tag, 2), align); - } - - llirValue *memory = llir_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++) { - llirValue *field = llir_emit_ptr_offset(proc, memory, llir_make_const_int(a, i)); - llirValue *name = llir_emit_struct_ep(proc, field, 0); - llirValue *type_info = llir_emit_struct_ep(proc, field, 1); - // NOTE(bill): offset is not used for tuples - - Entity *f = t->Tuple.variables[i]; - llirValue *tip = llir_get_type_info_ptr(proc, type_info_data, f->type); - - if (f->token.string.len > 0) { - llir_emit_store(proc, name, llir_make_const_string(a, f->token.string)); - } - llir_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); - llirValue *slice = llir_emit_struct_ep(proc, tag, 0); - llirValue *variable_count = llir_make_const_int(a, t->Tuple.variable_count); - - llirValue *elem = llir_emit_struct_ep(proc, slice, 0); - llirValue *len = llir_emit_struct_ep(proc, slice, 1); - llirValue *cap = llir_emit_struct_ep(proc, slice, 2); - - llir_emit_store(proc, elem, memory); - llir_emit_store(proc, len, variable_count); - llir_emit_store(proc, cap, variable_count); - } break; - - case Type_Proc: { - tag = llir_add_local_generated(proc, t_type_info_procedure); - - llirValue *params = llir_emit_struct_ep(proc, tag, 0); - llirValue *results = llir_emit_struct_ep(proc, tag, 1); - llirValue *variadic = llir_emit_struct_ep(proc, tag, 2); - - if (t->Proc.params) { - llir_emit_store(proc, params, llir_get_type_info_ptr(proc, type_info_data, t->Proc.params)); - } - if (t->Proc.results) { - llir_emit_store(proc, results, llir_get_type_info_ptr(proc, type_info_data, t->Proc.results)); - } - llir_emit_store(proc, variadic, llir_make_const_bool(a, t->Proc.variadic)); - - // TODO(bill): Type_Info for procedures - } break; - } - - if (tag != NULL) { - llirValue *gep = llir_emit_array_epi(proc, type_info_data, entry_index); - llirValue *val = llir_emit_conv(proc, llir_emit_load(proc, tag), t_type_info); - llir_emit_store(proc, gep, val); - } - } - } - - llir_end_procedure_body(proc); - } - - for_array(i, m->procs_to_generate) { - llir_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent); - } - - - // 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/llir_print.c b/src/llir_print.c deleted file mode 100644 index 1699e6e40..000000000 --- a/src/llir_print.c +++ /dev/null @@ -1,1515 +0,0 @@ -typedef struct llirFileBuffer { - gbVirtualMemory vm; - isize offset; - gbFile * output; -} llirFileBuffer; - -void llir_file_buffer_init(llirFileBuffer *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 llir_file_buffer_destroy(llirFileBuffer *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 llir_file_buffer_write(llirFileBuffer *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 llir_fprintf(llirFileBuffer *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); - llir_file_buffer_write(f, buf, len-1); - va_end(va); -} - - -void llir_file_write(llirFileBuffer *f, void *data, isize len) { - llir_file_buffer_write(f, data, len); -} - - -bool llir_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 llir_print_escape_string(llirFileBuffer *f, String name, bool print_quotes) { - isize extra = 0; - for (isize i = 0; i < name.len; i++) { - u8 c = name.text[i]; - if (!llir_valid_char(c)) { - extra += 2; - } - } - - if (extra == 0) { - llir_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 (llir_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++] = '"'; - } - - llir_file_write(f, buf, j); - - gb_temp_arena_memory_end(tmp); -} - - - -void llir_print_encoded_local(llirFileBuffer *f, String name) { - llir_fprintf(f, "%%"); - llir_print_escape_string(f, name, true); -} - -void llir_print_encoded_global(llirFileBuffer *f, String name, bool remove_prefix) { - llir_fprintf(f, "@"); - if (!remove_prefix) { - llir_fprintf(f, "."); - } - llir_print_escape_string(f, name, true); -} - - -void llir_print_type(llirFileBuffer *f, llirModule *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: llir_fprintf(f, "i1"); break; - case Basic_i8: llir_fprintf(f, "i8"); break; - case Basic_u8: llir_fprintf(f, "i8"); break; - case Basic_i16: llir_fprintf(f, "i16"); break; - case Basic_u16: llir_fprintf(f, "i16"); break; - case Basic_i32: llir_fprintf(f, "i32"); break; - case Basic_u32: llir_fprintf(f, "i32"); break; - case Basic_i64: llir_fprintf(f, "i64"); break; - case Basic_u64: llir_fprintf(f, "i64"); break; - // case Basic_i128: llir_fprintf(f, "i128"); break; - // case Basic_u128: llir_fprintf(f, "i128"); break; - // case Basic_f16: llir_fprintf(f, "half"); break; - case Basic_f32: llir_fprintf(f, "float"); break; - case Basic_f64: llir_fprintf(f, "double"); break; - // case Basic_f128: llir_fprintf(f, "fp128"); break; - case Basic_rawptr: llir_fprintf(f, "%%..rawptr"); break; - case Basic_string: llir_fprintf(f, "%%..string"); break; - case Basic_uint: llir_fprintf(f, "i%lld", word_bits); break; - case Basic_int: llir_fprintf(f, "i%lld", word_bits); break; - case Basic_any: llir_fprintf(f, "%%..any"); break; - } - break; - case Type_Pointer: - llir_print_type(f, m, t->Pointer.elem); - llir_fprintf(f, "*"); - break; - case Type_Maybe: - llir_fprintf(f, "{"); - llir_print_type(f, m, t->Maybe.elem); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_bool); - llir_fprintf(f, "}"); - break; - case Type_Array: - llir_fprintf(f, "[%lld x ", t->Array.count); - llir_print_type(f, m, t->Array.elem); - llir_fprintf(f, "]"); - break; - case Type_Vector: - llir_fprintf(f, "<%lld x ", t->Vector.count); - llir_print_type(f, m, t->Vector.elem); - llir_fprintf(f, ">"); - break; - case Type_Slice: - llir_fprintf(f, "{"); - llir_print_type(f, m, t->Slice.elem); - llir_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) { - llir_fprintf(f, "<"); - } - llir_fprintf(f, "{"); - for (isize i = 0; i < t->Record.field_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, t->Record.fields[i]->type); - } - llir_fprintf(f, "}"); - if (t->Record.struct_is_packed) { - llir_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); - llir_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); - llir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union); - } break; - case TypeRecord_Enum: - llir_print_type(f, m, base_enum_type(t)); - 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)); - llir_print_encoded_local(f, *name); - } else { - llir_print_type(f, m, base_type(t)); - } - break; - case Type_Tuple: - if (t->Tuple.variable_count == 1) { - llir_print_type(f, m, t->Tuple.variables[0]->type); - } else { - llir_fprintf(f, "{"); - for (isize i = 0; i < t->Tuple.variable_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, t->Tuple.variables[i]->type); - } - llir_fprintf(f, "}"); - } - break; - case Type_Proc: { - if (t->Proc.result_count == 0) { - llir_fprintf(f, "void"); - } else { - llir_print_type(f, m, t->Proc.results); - } - llir_fprintf(f, " ("); - TypeTuple *params = &t->Proc.params->Tuple; - for (isize i = 0; i < t->Proc.param_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, params->variables[i]->type); - } - llir_fprintf(f, ")*"); - } break; - } -} - -void llir_print_exact_value(llirFileBuffer *f, llirModule *m, ExactValue value, Type *type); - -void llir_print_compound_element(llirFileBuffer *f, llirModule *m, ExactValue v, Type *elem_type) { - llir_print_type(f, m, elem_type); - llir_fprintf(f, " "); - - if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { - Type *t = base_type(elem_type)->Maybe.elem; - llir_fprintf(f, "{"); - llir_print_type(f, m, t); - llir_fprintf(f, " "); - } - - if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) { - llir_fprintf(f, "zeroinitializer"); - } else { - llir_print_exact_value(f, m, v, elem_type); - } - - if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) { - llir_fprintf(f, ", "); - llir_print_type(f, m, t_bool); - llir_fprintf(f, " "); - llir_fprintf(f, "true}"); - } -} - -void llir_print_exact_value(llirFileBuffer *f, llirModule *m, ExactValue value, Type *type) { - type = base_type(base_enum_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: - llir_fprintf(f, "%s", (value.value_bool ? "true" : "false")); - break; - case ExactValue_String: { - String str = value.value_string; - if (str.len == 0) { - llir_fprintf(f, "zeroinitializer"); - break; - } - if (!is_type_string(type)) { - GB_ASSERT(is_type_array(type)); - llir_fprintf(f, "c\""); - llir_print_escape_string(f, str, false); - llir_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 - llirValue *str_array = llir_add_global_string_array(m, str); - - llir_fprintf(f, "{i8* getelementptr inbounds ("); - llir_print_type(f, m, str_array->Global.entity->type); - llir_fprintf(f, ", "); - llir_print_type(f, m, str_array->Global.entity->type); - llir_fprintf(f, "* "); - llir_print_encoded_global(f, str_array->Global.entity->token.string, false); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " 0, i32 0), "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " %lld}", cast(i64)str.len); - } - } break; - case ExactValue_Integer: { - if (is_type_pointer(type)) { - if (value.value_integer == 0) { - llir_fprintf(f, "null"); - } else { - llir_fprintf(f, "inttoptr ("); - llir_print_type(f, m, t_int); - llir_fprintf(f, " %llu to ", value.value_integer); - llir_print_type(f, m, t_rawptr); - llir_fprintf(f, ")"); - } - } else { - llir_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 mantillir: 52 bits - // 32 bit mantillir: 23 bits - // 29 == 52-23 - u >>= 29; - u <<= 29; - break; - } - - switch (type->Basic.kind) { - case 0: break; -#if 0 - case Basic_f16: - llir_fprintf(f, "bitcast ("); - llir_print_type(f, m, t_u16); - llir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float)); - llir_print_type(f, m, t_f16); - llir_fprintf(f, ")"); - break; - case Basic_f128: - llir_fprintf(f, "bitcast ("); - llir_fprintf(f, "i128"); - // TODO(bill): Actually support f128 - llir_fprintf(f, " %llu to ", u); - llir_print_type(f, m, t_f128); - llir_fprintf(f, ")"); - break; -#endif - default: - llir_fprintf(f, "0x%016llx", u); - break; - } - } break; - case ExactValue_Pointer: - if (value.value_pointer == 0) { - llir_fprintf(f, "null"); - } else { - llir_fprintf(f, "inttoptr ("); - llir_print_type(f, m, t_int); - llir_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer); - llir_print_type(f, m, t_rawptr); - llir_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) { - llir_fprintf(f, "zeroinitializer"); - break; - } - - llir_fprintf(f, "["); - Type *elem_type = type->Array.elem; - - for (isize i = 0; i < elem_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); - GB_ASSERT(tav != NULL); - llir_print_compound_element(f, m, tav->value, elem_type); - } - for (isize i = elem_count; i < type->Array.count; i++) { - if (i >= elem_count) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, elem_type); - llir_fprintf(f, " zeroinitializer"); - } - - llir_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) { - llir_fprintf(f, "zeroinitializer"); - break; - } - - llir_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) { - llir_fprintf(f, ", "); - } - llir_print_compound_element(f, m, tav->value, elem_type); - } - } else { - for (isize i = 0; i < elem_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]); - GB_ASSERT(tav != NULL); - llir_print_compound_element(f, m, tav->value, elem_type); - } - } - - llir_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) { - llir_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) { - llir_fprintf(f, "<"); - } - llir_fprintf(f, "{"); - - - for (isize i = 0; i < value_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - Type *elem_type = type->Record.fields[i]->type; - - llir_print_compound_element(f, m, values[i], elem_type); - } - - - llir_fprintf(f, "}"); - if (type->Record.struct_is_packed) { - llir_fprintf(f, ">"); - } - - gb_temp_arena_memory_end(tmp); - } else { - llir_fprintf(f, "zeroinitializer"); - } - - } break; - - default: - llir_fprintf(f, "zeroinitializer"); - // GB_PANIC("Invalid ExactValue: %d", value.kind); - break; - } -} - -void llir_print_block_name(llirFileBuffer *f, llirBlock *b) { - if (b != NULL) { - llir_print_escape_string(f, b->label, false); - llir_fprintf(f, "-%td", b->index); - } else { - llir_fprintf(f, "<INVALID-BLOCK>"); - } -} - -bool llir_print_is_proc_global(llirModule *m, llirProcedure *proc) { - if (proc->entity != NULL && - proc->entity->kind == Entity_Procedure) { - if (m->entry_point_entity == proc->entity) { - // TODO(bill): This may not be needed during windows - return true; - } - if (proc->entity->Procedure.link_name.len > 0) { - return true; - } - } - return (proc->tags & (ProcTag_foreign|ProcTag_export)) != 0; -} - -void llir_print_value(llirFileBuffer *f, llirModule *m, llirValue *value, Type *type_hint) { - if (value == NULL) { - llir_fprintf(f, "!!!NULL_VALUE"); - return; - } - switch (value->kind) { - default: GB_PANIC("Unknown llirValue kind"); break; - - case llirValue_Constant: - llir_print_exact_value(f, m, value->Constant.value, type_hint); - break; - - case llirValue_ConstantSlice: { - llirValueConstantSlice *cs = &value->ConstantSlice; - if (cs->backing_array == NULL || cs->count == 0) { - llir_fprintf(f, "zeroinitializer"); - } else { - Type *at = base_type(type_deref(llir_type(cs->backing_array))); - Type *et = at->Array.elem; - llir_fprintf(f, "{"); - llir_print_type(f, m, et); - llir_fprintf(f, "* getelementptr inbounds ("); - llir_print_type(f, m, at); - llir_fprintf(f, ", "); - llir_print_type(f, m, at); - llir_fprintf(f, "* "); - llir_print_value(f, m, cs->backing_array, at); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " 0, i32 0), "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " %lld, ", cs->count); - llir_print_type(f, m, t_int); - llir_fprintf(f, " %lld}", cs->count); - } - } break; - - case llirValue_Nil: - llir_fprintf(f, "zeroinitializer"); - break; - - case llirValue_TypeName: - llir_print_encoded_local(f, value->TypeName.name); - break; - case llirValue_Global: { - Scope *scope = value->Global.entity->scope; - bool in_global_scope = false; - if (scope != NULL) { - in_global_scope = scope->is_global || scope->is_init; - } - llir_print_encoded_global(f, value->Global.entity->token.string, in_global_scope); - } break; - case llirValue_Param: - llir_print_encoded_local(f, value->Param.entity->token.string); - break; - case llirValue_Proc: - llir_print_encoded_global(f, value->Proc.name, llir_print_is_proc_global(m, &value->Proc)); - break; - case llirValue_Instr: - llir_fprintf(f, "%%%d", value->index); - break; - } -} - -void llir_print_calling_convention(llirFileBuffer *f, llirModule *m, ProcCallingConvention cc) { - switch (cc) { - case ProcCC_Odin: llir_fprintf(f, ""); break; - case ProcCC_C: llir_fprintf(f, "ccc "); break; - case ProcCC_Std: llir_fprintf(f, "cc 64 "); break; - case ProcCC_Fast: llir_fprintf(f, "cc 65 "); break; - default: GB_PANIC("unknown calling convention: %d", cc); - } -} - -void llir_print_instr(llirFileBuffer *f, llirModule *m, llirValue *value) { - GB_ASSERT(value->kind == llirValue_Instr); - llirInstr *instr = &value->Instr; - - llir_fprintf(f, "\t"); - - switch (instr->kind) { - case llirInstr_StartupRuntime: { - llir_fprintf(f, "call void "); - llir_print_encoded_global(f, str_lit(LLIR_STARTUP_RUNTIME_PROC_NAME), false); - llir_fprintf(f, "()\n"); - } break; - - case llirInstr_Comment: - llir_fprintf(f, "; %.*s\n", LIT(instr->Comment.text)); - break; - - case llirInstr_Local: { - Type *type = instr->Local.entity->type; - llir_fprintf(f, "%%%d = alloca ", value->index); - llir_print_type(f, m, type); - llir_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); - } break; - - case llirInstr_ZeroInit: { - Type *type = type_deref(llir_type(instr->ZeroInit.address)); - llir_fprintf(f, "store "); - llir_print_type(f, m, type); - llir_fprintf(f, " zeroinitializer, "); - llir_print_type(f, m, type); - llir_fprintf(f, "* %%%d\n", instr->ZeroInit.address->index); - } break; - - case llirInstr_Store: { - Type *type = llir_type(instr->Store.value); - llir_fprintf(f, "store "); - llir_print_type(f, m, type); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->Store.value, type); - llir_fprintf(f, ", "); - llir_print_type(f, m, type); - llir_fprintf(f, "* "); - llir_print_value(f, m, instr->Store.address, type); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_Load: { - Type *type = instr->Load.type; - llir_fprintf(f, "%%%d = load ", value->index); - llir_print_type(f, m, type); - llir_fprintf(f, ", "); - llir_print_type(f, m, type); - llir_fprintf(f, "* "); - llir_print_value(f, m, instr->Load.address, type); - llir_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type)); - } break; - - case llirInstr_ArrayElementPtr: { - Type *et = llir_type(instr->ArrayElementPtr.address); - llir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); - - llir_print_type(f, m, type_deref(et)); - llir_fprintf(f, ", "); - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->ArrayElementPtr.address, et); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " 0, "); - - llirValue *index =instr->ArrayElementPtr.elem_index; - Type *t = llir_type(index); - llir_print_type(f, m, t); - llir_fprintf(f, " "); - llir_print_value(f, m, index, t); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_StructElementPtr: { - Type *et = llir_type(instr->StructElementPtr.address); - llir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); - - llir_print_type(f, m, type_deref(et)); - llir_fprintf(f, ", "); - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->StructElementPtr.address, et); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " 0, "); - llir_print_type(f, m, t_i32); - llir_fprintf(f, " %d", instr->StructElementPtr.elem_index); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_PtrOffset: { - Type *pt = llir_type(instr->PtrOffset.address); - llir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); - llir_print_type(f, m, type_deref(pt)); - llir_fprintf(f, ", "); - llir_print_type(f, m, pt); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->PtrOffset.address, pt); - - llirValue *offset = instr->PtrOffset.offset; - Type *t = llir_type(offset); - llir_fprintf(f, ", "); - llir_print_type(f, m, t); - llir_fprintf(f, " "); - llir_print_value(f, m, offset, t); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_Phi: { - llir_fprintf(f, "%%%d = phi ", value->index); - llir_print_type(f, m, instr->Phi.type); - llir_fprintf(f, " ", value->index); - - for (isize i = 0; i < instr->Phi.edges.count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - - llirValue *edge = instr->Phi.edges.e[i]; - llirBlock *block = NULL; - if (instr->parent != NULL && - i < instr->parent->preds.count) { - block = instr->parent->preds.e[i]; - } - - llir_fprintf(f, "[ "); - llir_print_value(f, m, edge, instr->Phi.type); - llir_fprintf(f, ", %%"); - llir_print_block_name(f, block); - llir_fprintf(f, " ]"); - } - llir_fprintf(f, "\n"); - } break; - - case llirInstr_ArrayExtractValue: { - Type *et = llir_type(instr->ArrayExtractValue.address); - llir_fprintf(f, "%%%d = extractvalue ", value->index); - - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->ArrayExtractValue.address, et); - llir_fprintf(f, ", %d\n", instr->ArrayExtractValue.index); - } break; - - case llirInstr_StructExtractValue: { - Type *et = llir_type(instr->StructExtractValue.address); - llir_fprintf(f, "%%%d = extractvalue ", value->index); - - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->StructExtractValue.address, et); - llir_fprintf(f, ", %d\n", instr->StructExtractValue.index); - } break; - - case llirInstr_UnionTagPtr: { - Type *et = llir_type(instr->UnionTagPtr.address); - llir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); - - llir_print_type(f, m, type_deref(et)); - llir_fprintf(f, ", "); - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->UnionTagPtr.address, et); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_int); - llir_fprintf(f, " 0, "); - llir_print_type(f, m, t_i32); - llir_fprintf(f, " %d", 2); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_UnionTagValue: { - Type *et = llir_type(instr->UnionTagValue.address); - llir_fprintf(f, "%%%d = extractvalue ", value->index); - - llir_print_type(f, m, et); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->UnionTagValue.address, et); - llir_fprintf(f, ", %d\n", 2); - } break; - - case llirInstr_Jump: {; - llir_fprintf(f, "br label %%"); - llir_print_block_name(f, instr->Jump.block); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_If: {; - llir_fprintf(f, "br "); - llir_print_type(f, m, t_bool); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->If.cond, t_bool); - llir_fprintf(f, ", ", instr->If.cond->index); - llir_fprintf(f, "label %%"); llir_print_block_name(f, instr->If.true_block); - llir_fprintf(f, ", label %%"); llir_print_block_name(f, instr->If.false_block); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_Return: { - llirInstrReturn *ret = &instr->Return; - llir_fprintf(f, "ret "); - if (ret->value == NULL) { - llir_fprintf(f, "void"); - } else { - Type *t = llir_type(ret->value); - llir_print_type(f, m, t); - llir_fprintf(f, " "); - llir_print_value(f, m, ret->value, t); - } - - llir_fprintf(f, "\n"); - - } break; - - case llirInstr_Conv: { - llirInstrConv *c = &instr->Conv; - llir_fprintf(f, "%%%d = %.*s ", value->index, LIT(llir_conv_strings[c->kind])); - llir_print_type(f, m, c->from); - llir_fprintf(f, " "); - llir_print_value(f, m, c->value, c->from); - llir_fprintf(f, " to "); - llir_print_type(f, m, c->to); - llir_fprintf(f, "\n"); - - } break; - - case llirInstr_Unreachable: { - llir_fprintf(f, "unreachable\n"); - } break; - - case llirInstr_UnaryOp: { - llirInstrUnaryOp *uo = &value->Instr.UnaryOp; - Type *type = base_type(llir_type(uo->expr)); - Type *elem_type = type; - while (elem_type->kind == Type_Vector) { - elem_type = base_type(elem_type->Vector.elem); - } - - llir_fprintf(f, "%%%d = ", value->index); - switch (uo->op) { - case Token_Sub: - if (is_type_float(elem_type)) { - llir_fprintf(f, "fsub"); - } else { - llir_fprintf(f, "sub"); - } - break; - case Token_Xor: - case Token_Not: - GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); - llir_fprintf(f, "xor"); - break; - default: - GB_PANIC("Unknown unary operator"); - break; - } - - llir_fprintf(f, " "); - llir_print_type(f, m, type); - llir_fprintf(f, " "); - switch (uo->op) { - case Token_Sub: - if (is_type_float(elem_type)) { - llir_print_exact_value(f, m, make_exact_value_float(0), type); - } else { - llir_fprintf(f, "0"); - } - break; - case Token_Xor: - case Token_Not: - GB_ASSERT(is_type_integer(type) || is_type_boolean(type)); - llir_fprintf(f, "-1"); - break; - } - llir_fprintf(f, ", "); - llir_print_value(f, m, uo->expr, type); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_BinaryOp: { - llirInstrBinaryOp *bo = &value->Instr.BinaryOp; - Type *type = base_type(llir_type(bo->left)); - Type *elem_type = type; - while (elem_type->kind == Type_Vector) { - elem_type = base_type(elem_type->Vector.elem); - } - - llir_fprintf(f, "%%%d = ", value->index); - - if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { - if (is_type_string(elem_type)) { - llir_fprintf(f, "call "); - llir_print_calling_convention(f, m, ProcCC_Odin); - llir_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; - } - - llir_fprintf(f, " "); - llir_print_encoded_global(f, make_string_c(runtime_proc), false); - llir_fprintf(f, "("); - llir_print_type(f, m, type); - llir_fprintf(f, " "); - llir_print_value(f, m, bo->left, type); - llir_fprintf(f, ", "); - llir_print_type(f, m, type); - llir_fprintf(f, " "); - llir_print_value(f, m, bo->right, type); - llir_fprintf(f, ")\n"); - return; - - } else if (is_type_float(elem_type)) { - llir_fprintf(f, "fcmp "); - switch (bo->op) { - case Token_CmpEq: llir_fprintf(f, "oeq"); break; - case Token_NotEq: llir_fprintf(f, "one"); break; - case Token_Lt: llir_fprintf(f, "olt"); break; - case Token_Gt: llir_fprintf(f, "ogt"); break; - case Token_LtEq: llir_fprintf(f, "ole"); break; - case Token_GtEq: llir_fprintf(f, "oge"); break; - } - } else { - llir_fprintf(f, "icmp "); - if (bo->op != Token_CmpEq && - bo->op != Token_NotEq) { - if (is_type_unsigned(elem_type)) { - llir_fprintf(f, "u"); - } else { - llir_fprintf(f, "s"); - } - } - switch (bo->op) { - case Token_CmpEq: llir_fprintf(f, "eq"); break; - case Token_NotEq: llir_fprintf(f, "ne"); break; - case Token_Lt: llir_fprintf(f, "lt"); break; - case Token_Gt: llir_fprintf(f, "gt"); break; - case Token_LtEq: llir_fprintf(f, "le"); break; - case Token_GtEq: llir_fprintf(f, "ge"); break; - default: GB_PANIC("invalid comparison");break; - } - } - } else { - if (is_type_float(elem_type)) { - llir_fprintf(f, "f"); - } - - switch (bo->op) { - case Token_Add: llir_fprintf(f, "add"); break; - case Token_Sub: llir_fprintf(f, "sub"); break; - case Token_And: llir_fprintf(f, "and"); break; - case Token_Or: llir_fprintf(f, "or"); break; - case Token_Xor: llir_fprintf(f, "xor"); break; - case Token_Shl: llir_fprintf(f, "shl"); break; - case Token_Shr: llir_fprintf(f, "lshr"); break; - case Token_Mul: llir_fprintf(f, "mul"); break; - case Token_Not: llir_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)) { - llir_fprintf(f, "u"); - } else { - llir_fprintf(f, "s"); - } - } - - switch (bo->op) { - case Token_Quo: llir_fprintf(f, "div"); break; - case Token_Mod: llir_fprintf(f, "rem"); break; - } - } break; - } - } - - llir_fprintf(f, " "); - llir_print_type(f, m, type); - llir_fprintf(f, " "); - llir_print_value(f, m, bo->left, type); - llir_fprintf(f, ", "); - llir_print_value(f, m, bo->right, type); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_Call: { - llirInstrCall *call = &instr->Call; - Type *proc_type = base_type(llir_type(call->value)); - GB_ASSERT(is_type_proc(proc_type)); - Type *result_type = call->type; - if (result_type) { - llir_fprintf(f, "%%%d = ", value->index); - } - llir_fprintf(f, "call "); - llir_print_calling_convention(f, m, proc_type->Proc.calling_convention); - if (result_type) { - llir_print_type(f, m, result_type); - } else { - llir_fprintf(f, "void"); - } - llir_fprintf(f, " "); - llir_print_value(f, m, call->value, call->type); - - - llir_fprintf(f, "("); - if (call->arg_count > 0) { - Type *proc_type = base_type(llir_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) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, t); - llir_fprintf(f, " "); - llirValue *arg = call->args[i]; - llir_print_value(f, m, arg, t); - } - } - llir_fprintf(f, ")\n"); - - } break; - - case llirInstr_Select: { - llir_fprintf(f, "%%%d = select i1 ", value->index); - llir_print_value(f, m, instr->Select.cond, t_bool); - llir_fprintf(f, ", "); - llir_print_type(f, m, llir_type(instr->Select.true_value)); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->Select.true_value, llir_type(instr->Select.true_value)); - llir_fprintf(f, ", "); - llir_print_type(f, m, llir_type(instr->Select.false_value)); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->Select.false_value, llir_type(instr->Select.false_value)); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_VectorExtractElement: { - Type *vt = llir_type(instr->VectorExtractElement.vector); - Type *it = llir_type(instr->VectorExtractElement.index); - llir_fprintf(f, "%%%d = extractelement ", value->index); - - llir_print_type(f, m, vt); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->VectorExtractElement.vector, vt); - llir_fprintf(f, ", "); - llir_print_type(f, m, it); - llir_fprintf(f, " "); - llir_print_value(f, m, instr->VectorExtractElement.index, it); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_VectorInsertElement: { - llirInstrVectorInsertElement *ie = &instr->VectorInsertElement; - Type *vt = llir_type(ie->vector); - llir_fprintf(f, "%%%d = insertelement ", value->index); - - llir_print_type(f, m, vt); - llir_fprintf(f, " "); - llir_print_value(f, m, ie->vector, vt); - llir_fprintf(f, ", "); - - llir_print_type(f, m, llir_type(ie->elem)); - llir_fprintf(f, " "); - llir_print_value(f, m, ie->elem, llir_type(ie->elem)); - llir_fprintf(f, ", "); - - llir_print_type(f, m, llir_type(ie->index)); - llir_fprintf(f, " "); - llir_print_value(f, m, ie->index, llir_type(ie->index)); - - llir_fprintf(f, "\n"); - } break; - - case llirInstr_VectorShuffle: { - llirInstrVectorShuffle *sv = &instr->VectorShuffle; - Type *vt = llir_type(sv->vector); - llir_fprintf(f, "%%%d = shufflevector ", value->index); - - llir_print_type(f, m, vt); - llir_fprintf(f, " "); - llir_print_value(f, m, sv->vector, vt); - llir_fprintf(f, ", "); - - llir_print_type(f, m, vt); - llir_fprintf(f, " "); - llir_print_value(f, m, sv->vector, vt); - llir_fprintf(f, ", "); - - llir_fprintf(f, "<%td x i32> <", sv->index_count); - for (isize i = 0; i < sv->index_count; i++) { - if (i > 0) { - llir_fprintf(f, ", "); - } - llir_fprintf(f, "i32 %d", sv->indices[i]); - } - llir_fprintf(f, ">"); - llir_fprintf(f, "\n"); - } break; - - case llirInstr_BoundsCheck: { - llirInstrBoundsCheck *bc = &instr->BoundsCheck; - llir_fprintf(f, "call void "); - llir_print_encoded_global(f, str_lit("__bounds_check_error"), false); - llir_fprintf(f, "("); - llir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_value(f, m, bc->index, t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_value(f, m, bc->len, t_int); - - llir_fprintf(f, ")\n"); - } break; - - case llirInstr_SliceBoundsCheck: { - llirInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck; - llir_fprintf(f, "call void "); - if (bc->is_substring) { - llir_print_encoded_global(f, str_lit("__substring_expr_error"), false); - } else { - llir_print_encoded_global(f, str_lit("__slice_expr_error"), false); - } - - llir_fprintf(f, "("); - llir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_value(f, m, bc->low, t_int); - llir_fprintf(f, ", "); - - llir_print_type(f, m, t_int); - llir_fprintf(f, " "); - llir_print_value(f, m, bc->high, t_int); - - llir_fprintf(f, ")\n"); - } break; - - - default: { - GB_PANIC("<unknown instr> %d\n", instr->kind); - llir_fprintf(f, "; <unknown instr> %d\n", instr->kind); - } break; - } -} - - -void llir_print_proc(llirFileBuffer *f, llirModule *m, llirProcedure *proc) { - if (proc->body == NULL) { - llir_fprintf(f, "declare "); - if (proc->tags & ProcTag_dll_import) { - llir_fprintf(f, "dllimport "); - } - } else { - llir_fprintf(f, "\n"); - llir_fprintf(f, "define "); - if (m->build_context->is_dll) { - // if (proc->tags & (ProcTag_export|ProcTag_dll_export)) { - if (proc->tags & (ProcTag_export)) { - llir_fprintf(f, "dllexport "); - } - } - } - - TypeProc *proc_type = &proc->type->Proc; - - llir_print_calling_convention(f, m, proc_type->calling_convention); - - if (proc_type->result_count == 0) { - llir_fprintf(f, "void"); - } else { - llir_print_type(f, m, proc_type->results); - } - - llir_fprintf(f, " "); - llir_print_encoded_global(f, proc->name, llir_print_is_proc_global(m, proc)); - llir_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) { - llir_fprintf(f, ", "); - } - llir_print_type(f, m, e->type); - if (proc->body != NULL) { - if (!str_eq(e->token.string, str_lit("")) && - !str_eq(e->token.string, str_lit("_"))) { - llir_fprintf(f, " %%%.*s", LIT(e->token.string)); - } else { - llir_fprintf(f, " %%_.param_%td", i); - } - } - } - } - - llir_fprintf(f, ") "); - - if (proc->tags & ProcTag_inline) { - llir_fprintf(f, "alwaysinline "); - } - if (proc->tags & ProcTag_no_inline) { - llir_fprintf(f, "noinline "); - } - - - if (proc->module->generate_debug_info && proc->entity != NULL) { - if (proc->body != NULL) { - llirDebugInfo *di = *map_llir_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity)); - GB_ASSERT(di->kind == llirDebugInfo_Proc); - llir_fprintf(f, "!dbg !%d ", di->id); - } - } - - - if (proc->body != NULL) { - // llir_fprintf(f, "nounwind uwtable {\n"); - - llir_fprintf(f, "{\n"); - for_array(i, proc->blocks) { - llirBlock *block = proc->blocks.e[i]; - - if (i > 0) llir_fprintf(f, "\n"); - llir_print_block_name(f, block); - llir_fprintf(f, ":\n"); - - for_array(j, block->instrs) { - llirValue *value = block->instrs.e[j]; - llir_print_instr(f, m, value); - } - } - llir_fprintf(f, "}\n"); - } else { - llir_fprintf(f, "\n"); - } - - for_array(i, proc->children) { - llir_print_proc(f, m, proc->children.e[i]); - } -} - -void llir_print_type_name(llirFileBuffer *f, llirModule *m, llirValue *v) { - GB_ASSERT(v->kind == llirValue_TypeName); - Type *bt = base_type(llir_type(v)); - if (!is_type_struct(bt) && !is_type_union(bt)) { - return; - } - llir_print_encoded_local(f, v->TypeName.name); - llir_fprintf(f, " = type "); - llir_print_type(f, m, base_type(v->TypeName.type)); - llir_fprintf(f, "\n"); -} - -void print_llvm_ir(llirGen *llir) { - llirModule *m = &llir->module; - llirFileBuffer buf = {0}, *f = &buf; - llir_file_buffer_init(f, &llir->output_file); - - if (m->layout.len > 0) { - llir_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); - } - - llir_print_encoded_local(f, str_lit("..string")); - llir_fprintf(f, " = type {i8*, "); - llir_print_type(f, m, t_int); - llir_fprintf(f, "} ; Basic_string\n"); - llir_print_encoded_local(f, str_lit("..rawptr")); - llir_fprintf(f, " = type i8* ; Basic_rawptr\n"); - - llir_print_encoded_local(f, str_lit("..any")); - llir_fprintf(f, " = type {"); - llir_print_type(f, m, t_type_info_ptr); - llir_fprintf(f, ", "); - llir_print_type(f, m, t_rawptr); - llir_fprintf(f, "} ; Basic_any\n"); - - - for_array(member_index, m->members.entries) { - MapSsaValueEntry *entry = &m->members.entries.e[member_index]; - llirValue *v = entry->value; - if (v->kind != llirValue_TypeName) { - continue; - } - llir_print_type_name(f, m, v); - } - - llir_fprintf(f, "\n"); - - bool dll_main_found = false; - - for_array(member_index, m->members.entries) { - MapSsaValueEntry *entry = &m->members.entries.e[member_index]; - llirValue *v = entry->value; - if (v->kind != llirValue_Proc) { - continue; - } - - if (v->Proc.body == NULL) { - llir_print_proc(f, m, &v->Proc); - } - } - - for_array(member_index, m->members.entries) { - MapSsaValueEntry *entry = &m->members.entries.e[member_index]; - llirValue *v = entry->value; - if (v->kind != llirValue_Proc) { - continue; - } - - if (v->Proc.body != NULL) { - llir_print_proc(f, m, &v->Proc); - } - } - - for_array(member_index, m->members.entries) { - MapSsaValueEntry *entry = &m->members.entries.e[member_index]; - llirValue *v = entry->value; - if (v->kind != llirValue_Global) { - continue; - } - llirValueGlobal *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; - } - llir_print_encoded_global(f, g->entity->token.string, in_global_scope); - llir_fprintf(f, " = "); - if (g->is_thread_local) { - llir_fprintf(f, "thread_local "); - } - - if (g->is_private) { - llir_fprintf(f, "private "); - } - if (g->is_constant) { - if (g->is_unnamed_addr) { - llir_fprintf(f, "unnamed_addr "); - } - llir_fprintf(f, "constant "); - } else { - llir_fprintf(f, "global "); - } - - - llir_print_type(f, m, g->entity->type); - llir_fprintf(f, " "); - if (g->value != NULL) { - llir_print_value(f, m, g->value, g->entity->type); - } else { - llir_fprintf(f, "zeroinitializer"); - } - llir_fprintf(f, "\n"); - } - - -#if 0 - if (m->generate_debug_info) { - llir_fprintf(f, "\n"); - llir_fprintf(f, "!llvm.dbg.cu = !{!0}\n"); - - for_array(di_index, m->debug_info.entries) { - MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[di_index]; - llirDebugInfo *di = entry->value; - llir_fprintf(f, "!%d = ", di->id); - - switch (di->kind) { - case llirDebugInfo_CompileUnit: { - auto *cu = &di->CompileUnit; - llirDebugInfo *file = *map_llir_debug_info_get(&m->debug_info, hash_pointer(cu->file)); - llir_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 llirDebugInfo_File: - llir_fprintf(f, "!DIFile(filename: \""); - llir_print_escape_string(f, di->File.filename, false); - llir_fprintf(f, "\", directory: \""); - llir_print_escape_string(f, di->File.directory, false); - llir_fprintf(f, "\")"); - break; - case llirDebugInfo_Proc: - llir_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 llirDebugInfo_AllProcs: - llir_fprintf(f, "!{"); - for_array(proc_index, di->AllProcs.procs) { - llirDebugInfo *p = di->AllProcs.procs.e[proc_index]; - if (proc_index > 0) {llir_fprintf(f, ",");} - llir_fprintf(f, "!%d", p->id); - } - llir_fprintf(f, "}"); - break; - } - - llir_fprintf(f, "\n"); - } - } -#endif - llir_file_buffer_destroy(f); -} diff --git a/src/main.c b/src/main.c index 64e99aa57..db238235c 100644 --- a/src/main.c +++ b/src/main.c @@ -10,9 +10,10 @@ extern "C" { #include "parser.c" // #include "printer.c" #include "checker/checker.c" -#include "llir.c" -#include "llir_opt.c" -#include "llir_print.c" +// #include "ssa.c" +#include "ir.c" +#include "ir_opt.c" +#include "ir_print.c" // #include "vm.c" // NOTE(bill): `name` is used in debugging and profiling modes @@ -158,29 +159,39 @@ int main(int argc, char **argv) { #endif + // if (global_error_collector.count != 0) { + // return 1; + // } + + // if (checker.parser->total_token_count < 2) { + // return 1; + // } + + // ssa_generate(&checker.info, &build_context); + #if 1 - llirGen llir = {0}; - if (!llir_gen_init(&llir, &checker, &build_context)) { + irGen ir_gen = {0}; + if (!ir_gen_init(&ir_gen, &checker, &build_context)) { return 1; } - // defer (ssa_gen_destroy(&llir)); + // defer (ssa_gen_destroy(&ir_gen)); timings_start_section(&timings, str_lit("llvm ir gen")); - llir_gen_tree(&llir); + ir_gen_tree(&ir_gen); timings_start_section(&timings, str_lit("llvm ir opt tree")); - llir_opt_tree(&llir); + ir_opt_tree(&ir_gen); timings_start_section(&timings, str_lit("llvm ir print")); - print_llvm_ir(&llir); + print_llvm_ir(&ir_gen); // prof_print_all(); #if 1 timings_start_section(&timings, str_lit("llvm-opt")); - char const *output_name = llir.output_file.filename; + char const *output_name = ir_gen.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); diff --git a/src/parser.c b/src/parser.c index b68836d23..96978d1c4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -257,13 +257,6 @@ 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(GenericDecl, "declaration", struct { \ - Token token; \ - Token open, close; \ - AstNodeArray specs; \ - u64 tags; \ - bool is_using; \ - }) \ AST_NODE_KIND(ValueDecl, "value declaration", struct { \ bool is_var; \ AstNodeArray names; \ @@ -491,8 +484,6 @@ Token ast_node_token(AstNode *node) { case AstNode_BadDecl: return node->BadDecl.begin; - case AstNode_GenericDecl: - return node->GenericDecl.token; case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]); case AstNode_ImportDecl: @@ -1066,18 +1057,6 @@ AstNode *make_foreign_library(AstFile *f, Token token, Token filepath, AstNode * return result; } - -AstNode *make_generic_decl(AstFile *f, Token token, Token open, Token close, AstNodeArray specs, u64 tags, bool is_using) { - AstNode *result = make_node(f, AstNode_GenericDecl); - result->GenericDecl.token = token; - result->GenericDecl.open = open; - result->GenericDecl.close = close; - result->GenericDecl.specs = specs; - result->GenericDecl.tags = tags; - result->GenericDecl.is_using = is_using; - return result; -} - AstNode *make_value_decl(AstFile *f, bool is_var, AstNodeArray names, AstNode *type, AstNodeArray values) { AstNode *result = make_node(f, AstNode_ValueDecl); result->ValueDecl.is_var = is_var; @@ -1281,16 +1260,6 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) { } } break; - - case AstNode_GenericDecl: - if (s->GenericDecl.close.kind == Token_CloseBrace) { - return true; - } else if (s->GenericDecl.token.kind == Token_type) { - if (f->prev_token.kind == Token_CloseBrace) { - return true; - } - } - break; } return false; |