typedef S32 Register_Index; union Register{ F64 f64; S64 s64; S64 *pointer_s64; F64 *pointer_f64; U8 *pointer; U64 *pointer64; U64 *pointer_u64; U32 *pointer_u32; U16 *pointer_u16; U8 *pointer_u8; U64 u64; U32 u32; U16 u16; U8 u8; }; static_assert(sizeof(Register) == 8, "not 8 bytes"); enum Ir_Storage_Kind{ STORAGE_NULL, STORAGE_GLOBAL, STORAGE_CONSTANT, STORAGE_REGISTER, STORAGE_STACK, }; struct Ir_Storage{ Ast_Type *type; Ir_Storage *next; Ir_Storage_Kind kind; union{ S64 register_index; S64 offset; Register constant; }; }; enum Ir_Instruction_Kind{ IR_NOOP, IR_STORE, IR_LOAD, IR_ADD, IR_SUB, IR_MUL, IR_DIV, }; struct Ir_Instruction{ Ir_Instruction_Kind kind; Ir_Storage *a; Ir_Storage *b; Ir_Storage *c; }; struct Ir_Block{ Simple_Bucket_Array instructions; }; struct Ir_Call; struct Ir_Var{ Ast_Decl *decl; Ir_Storage storage; Ir_Call *call; Simple_Bucket_Array blocks; }; struct Ir_Call{ Ast_Decl *decl; Array used_registers; Array free_registers; Register_Index register_ids; Ir_Storage return_value; Simple_Bucket_Array args; Simple_Bucket_Array blocks; }; struct Ir_Builder{ #define IR_NEW(T) exp_alloc_type(ir->arena, T, AF_ZeroMemory) Arena _arena; Arena *arena; Simple_Bucket_Array global_variables; Simple_Bucket_Array calls; }; function Register_Index allocate_register(Ir_Call *call){ Register_Index result = -1; if(call->free_registers.len > 0){ result = call->free_registers.pop(); } else{ result = call->register_ids++; } assert(result != -1); call->used_registers.add(result); return result; } function void release_register(Ir_Call *call, Register_Index index){ if(index == -1) return; B32 found = false; For(call->used_registers){ if(index == it){ call->used_registers.unordered_remove(&it); found = true; break; } } assert_msg(found, "Internal compiler error: releasing register that is not marked as used"); call->free_registers.add(index); } function void build_bytecode(){ Ir_Builder ir_builder = {}; arena_init(&ir_builder._arena, "Ir builder arena"_s); ir_builder.arena = &ir_builder._arena; Ir_Builder *ir = &ir_builder; For_Named(pctx->ordered_decls, ast){ switch(ast->kind){ CASE(LAMBDA, Decl){ Ir_Call *call = ir->calls.allocate(ir->arena); call->decl = node; // @todo multiple return values call->return_value.type = node->lambda->ret[0]->resolved_type; call->free_registers = array_make(ir->arena, 32); call->used_registers = array_make(ir->arena, 32); For_It(node->lambda->args){ Ir_Var *arg = call->args.allocate(ir->arena); arg->decl = *it.it; arg->call = call; arg->storage.kind = STORAGE_REGISTER; arg->storage.register_index = allocate_register(call); } // @todo: Allocate all variables in function // // For(node->lambda->scope->stmts){ CASE(VAR, Decl){ BREAK(); } } BREAK(); } CASE(VAR, Decl){ if(is_flag_set(node->flags, AST_FOREIGN)){ break; // @todo } if(!is_flag_set(node->flags, AST_VAR_IS_CONST)){ compiler_error(node->pos, "Global variable has value assigned that is not constant and requires computation"); } Ir_Var *v = ir->global_variables.allocate(ir->arena); v->decl = node; v->storage.kind = STORAGE_GLOBAL; v->storage.type = node->type; if(node->type->size <= 8){ switch(node->type->kind){ CASE_UINT: v->storage.constant.u64 = bigint_as_unsigned(&node->big_int_val); break; CASE_SINT: v->storage.constant.s64 = bigint_as_signed(&node->big_int_val); break; CASE_FLOAT: v->storage.constant.f64 = node->f64_val; break; invalid_default_case; } } else { // Doesn't fit in register invalid_codepath; } BREAK(); } default: {}; } } }