diff --git a/core/runtime/os_specific_windows.odin b/core/runtime/os_specific_windows.odin index 732715793..9f001fa5a 100644 --- a/core/runtime/os_specific_windows.odin +++ b/core/runtime/os_specific_windows.odin @@ -1,9 +1,6 @@ -//+private //+build windows package runtime -import "core:intrinsics" - foreign import kernel32 "system:Kernel32.lib" @(private="file") @@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol allocated_mem: rawptr if old_ptr != nil { - original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^ + original_old_ptr := ([^]rawptr)(old_ptr)[-1] allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) } else { allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory) } - aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) + aligned_mem := ([^]u8)(allocated_mem)[size_of(rawptr):] ptr := uintptr(aligned_mem) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) @@ -116,10 +113,10 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol return nil, .Out_Of_Memory } - aligned_mem = rawptr(aligned_ptr) - intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem + aligned_mem = ([^]byte)(aligned_ptr) + ([^]rawptr)(aligned_mem)[-1] = allocated_mem - return byte_slice(aligned_mem, size), nil + return aligned_mem[:size], nil } _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) { @@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory _windows_default_free :: proc "contextless" (ptr: rawptr) { if ptr != nil { - heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^) + heap_free(([^]rawptr)(ptr)[-1]) } } diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 20788b66f..035d47f15 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS when !ODIN_NO_RTTI { print_any_single :: proc "contextless" (arg: any) { x := arg + if x.data == nil { + print_string("nil") + return + } + if loc, ok := x.(Source_Code_Location); ok { print_caller_location(loc) return @@ -48,6 +53,7 @@ when !ODIN_NO_RTTI { case int: print_int(v) case uint: print_uint(v) case uintptr: print_uintptr(v) + case rawptr: print_uintptr(uintptr(v)) case bool: print_string("true" if v else "false") case b8: print_string("true" if v else "false") @@ -58,7 +64,7 @@ when !ODIN_NO_RTTI { case: ti := type_info_of(x.id) #partial switch v in ti.variant { - case Type_Info_Pointer: + case Type_Info_Pointer, Type_Info_Multi_Pointer: print_uintptr((^uintptr)(x.data)^) return } @@ -67,7 +73,9 @@ when !ODIN_NO_RTTI { } } println_any :: proc "contextless" (args: ..any) { + context = default_context() loop: for arg, i in args { + assert(arg.id != nil) if i != 0 { print_string(" ") } diff --git a/src/tilde.cpp b/src/tilde.cpp index fc51a1156..0cbc975c4 100644 --- a/src/tilde.cpp +++ b/src/tilde.cpp @@ -1,16 +1,16 @@ #include "tilde.hpp" -gb_global Slice global_tb_arenas; +gb_global Slice global_tb_arenas; gb_internal TB_Arena *cg_arena(void) { - return global_tb_arenas[current_thread_index()]; + return &global_tb_arenas[current_thread_index()]; } gb_internal void cg_global_arena_init(void) { - global_tb_arenas = slice_make(permanent_allocator(), global_thread_pool.threads.count); + global_tb_arenas = slice_make(permanent_allocator(), global_thread_pool.threads.count); for_array(i, global_tb_arenas) { - global_tb_arenas[i] = tb_default_arena(); + tb_arena_create(&global_tb_arenas[i], 2ull<<20); } } @@ -426,14 +426,15 @@ gb_internal cgModule *cg_module_create(Checker *c) { map_init(&m->values); map_init(&m->symbols); - map_init(&m->file_id_map); - map_init(&m->debug_type_map); map_init(&m->proc_debug_type_map); map_init(&m->proc_proto_map); - map_init(&m->anonymous_proc_lits_map); + map_init(&m->equal_procs); + map_init(&m->hasher_procs); + map_init(&m->map_get_procs); + map_init(&m->map_set_procs); array_init(&m->single_threaded_procedure_queue, heap_allocator()); @@ -456,6 +457,10 @@ gb_internal void cg_module_destroy(cgModule *m) { map_destroy(&m->proc_debug_type_map); map_destroy(&m->proc_proto_map); map_destroy(&m->anonymous_proc_lits_map); + map_destroy(&m->equal_procs); + map_destroy(&m->hasher_procs); + map_destroy(&m->map_get_procs); + map_destroy(&m->map_set_procs); array_free(&m->single_threaded_procedure_queue); @@ -751,6 +756,19 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) { array_add(&procedures_to_generate, p); } } + for (cgProcedure *p : procedures_to_generate) { + cg_add_procedure_to_queue(p); + } + + if (!m->do_threading) { + for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) { + cgProcedure *p = m->single_threaded_procedure_queue[i]; + cg_procedure_generate(p); + } + } + + thread_pool_wait(); + { cgProcedure *p = cg_startup_runtime_proc; cg_procedure_begin(p); @@ -765,18 +783,6 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) { cg_procedure_end(p); } - for (cgProcedure *p : procedures_to_generate) { - cg_add_procedure_to_queue(p); - } - - if (!m->do_threading) { - for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) { - cgProcedure *p = m->single_threaded_procedure_queue[i]; - cg_procedure_generate(p); - } - } - - thread_pool_wait(); TB_DebugFormat debug_format = TB_DEBUGFMT_NONE; diff --git a/src/tilde.hpp b/src/tilde.hpp index 6ff5602b5..a70d03a6c 100644 --- a/src/tilde.hpp +++ b/src/tilde.hpp @@ -6,6 +6,7 @@ #endif #include "tilde/tb.h" +#include "tilde/tb_arena.h" #define TB_TYPE_F16 TB_DataType{ { TB_INT, 0, 16 } } #define TB_TYPE_I128 TB_DataType{ { TB_INT, 0, 128 } } @@ -230,6 +231,12 @@ struct cgModule { BlockingMutex anonymous_proc_lits_mutex; PtrMap anonymous_proc_lits_map; + RecursiveMutex generated_procs_mutex; + PtrMap equal_procs; + PtrMap hasher_procs; + PtrMap map_get_procs; + PtrMap map_set_procs; + // NOTE(bill): no need to protect this with a mutex PtrMap file_id_map; // Key: AstFile.id (i32 cast to uintptr) @@ -319,6 +326,7 @@ gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr); gb_internal void cg_build_return_stmt(cgProcedure *p, Slice const &return_results); gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice const &results); +gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result); gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node); gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e); @@ -341,6 +349,10 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t); gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x); gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right); gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type); +gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type); + +gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type); + gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice const &args); gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice const &args); diff --git a/src/tilde/tb.h b/src/tilde/tb.h index a68f06554..b20b98b35 100644 --- a/src/tilde/tb.h +++ b/src/tilde/tb.h @@ -15,20 +15,21 @@ #define TB_VERSION_MINOR 2 #define TB_VERSION_PATCH 0 -#ifdef __cplusplus -#define TB_EXTERN extern "C" -#else -#define TB_EXTERN -#endif - -#ifdef TB_DLL -# ifdef TB_IMPORT_DLL -# define TB_API TB_EXTERN __declspec(dllimport) +#ifndef TB_API +# ifdef __cplusplus +# define TB_EXTERN extern "C" # else -# define TB_API TB_EXTERN __declspec(dllexport) +# define TB_EXTERN +# endif +# ifdef TB_DLL +# ifdef TB_IMPORT_DLL +# define TB_API TB_EXTERN __declspec(dllimport) +# else +# define TB_API TB_EXTERN __declspec(dllexport) +# endif +# else +# define TB_API TB_EXTERN # endif -#else -# define TB_API TB_EXTERN #endif // These are flags @@ -171,7 +172,6 @@ typedef enum TB_FloatFormat { typedef union TB_DataType { struct { uint8_t type; - // 2^N where N is the width value. // Only integers and floats can be wide. uint8_t width; // for integers it's the bitwidth @@ -406,8 +406,6 @@ typedef struct TB_Symbol { typedef int TB_Reg; -#define TB_NULL_REG NULL - typedef struct TB_Node TB_Node; struct TB_Node { TB_NodeType type; @@ -586,29 +584,13 @@ typedef struct { typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...); -//////////////////////////////// -// Arena -//////////////////////////////// -// the goal is to move more things to transparent arenas, for now it's just function -// IR which is a big one if you're interested in freeing them in whatever organization -// you please. - -// allocations can make no guarentees about being sequential -// tho it would be greatly appreciated at least to some degree. +// defined in common/arena.h typedef struct TB_Arena TB_Arena; -struct TB_Arena { - // alignment never goes past max_align_t - void* (*alloc)(TB_Arena* arena, size_t size, size_t align); - // clearing but we're not done with it yet, cheap - void (*clear)(TB_Arena* arena); - - // frees everything within the arena, potentially expensive - void (*free)(TB_Arena* arena); -}; - -// allocates in 16MiB chunks and does linear allocation in 'em -TB_API TB_Arena* tb_default_arena(void); +// 0 for default +TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size); +TB_API void tb_arena_destroy(TB_Arena* restrict arena); +TB_API bool tb_arena_is_empty(TB_Arena* arena); //////////////////////////////// // Module management @@ -651,8 +633,7 @@ struct TB_Assembly { // this is where the machine code and other relevant pieces go. typedef struct TB_FunctionOutput TB_FunctionOutput; -// returns NULL if it fails -TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode, bool emit_asm); +TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp); TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length); @@ -865,6 +846,9 @@ TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_ TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type); +TB_API size_t tb_debug_func_return_count(TB_DebugType* type); +TB_API size_t tb_debug_func_param_count(TB_DebugType* type); + // you'll need to fill these if you make a function TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type); TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type); @@ -895,9 +879,6 @@ TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...); TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line); -// this only allows for power of two vector types -TB_API TB_DataType tb_vector_type(TB_DataTypeEnum type, int width); - // if section is NULL, default to .text TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat); @@ -1073,25 +1054,40 @@ TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values); //////////////////////////////// -// Optimizer +// Passes //////////////////////////////// // Function analysis, optimizations, and codegen are all part of this -typedef struct TB_FuncOpt TB_FuncOpt; +typedef struct TB_Passes TB_Passes; -// the arena is used to allocate the nodes -TB_API TB_FuncOpt* tb_funcopt_enter(TB_Function* f, TB_Arena* arena); -TB_API void tb_funcopt_exit(TB_FuncOpt* opt); +// the arena is used to allocate the nodes while passes are being done. +TB_API TB_Passes* tb_pass_enter(TB_Function* f, TB_Arena* arena); +TB_API void tb_pass_exit(TB_Passes* opt); -TB_API bool tb_funcopt_peephole(TB_FuncOpt* opt); -TB_API bool tb_funcopt_mem2reg(TB_FuncOpt* opt); -TB_API bool tb_funcopt_loop(TB_FuncOpt* opt); +// transformation passes: +// peephole: runs most simple reductions on the code, +// should be run after any bigger passes (it's incremental +// so it's not that bad) +// +// mem2reg: lowers TB_LOCALs into SSA values, this makes more +// data flow analysis possible on the code and allows to codegen +// to place variables into registers. +// +// loop: NOT READY +// +TB_API bool tb_pass_peephole(TB_Passes* opt); +TB_API bool tb_pass_mem2reg(TB_Passes* opt); +TB_API bool tb_pass_loop(TB_Passes* opt); -// isn't an optimization, just does the name flat form of IR printing -TB_API bool tb_funcopt_print(TB_FuncOpt* opt); +// analysis +// print: prints IR in a flattened text form. +TB_API bool tb_pass_print(TB_Passes* opt); -TB_API void tb_funcopt_kill(TB_FuncOpt* opt, TB_Node* n); -TB_API bool tb_funcopt_mark(TB_FuncOpt* opt, TB_Node* n); -TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n); +// codegen +TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm); + +TB_API void tb_pass_kill_node(TB_Passes* opt, TB_Node* n); +TB_API bool tb_pass_mark(TB_Passes* opt, TB_Node* n); +TB_API void tb_pass_mark_users(TB_Passes* opt, TB_Node* n); //////////////////////////////// // IR access @@ -1099,8 +1095,6 @@ TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n); TB_API const char* tb_node_get_name(TB_Node* n); TB_API TB_Node* tb_get_parent_region(TB_Node* n); -TB_API bool tb_has_effects(TB_Node* n); - TB_API bool tb_node_is_constant_non_zero(TB_Node* n); TB_API bool tb_node_is_constant_zero(TB_Node* n); diff --git a/src/tilde/tb.lib b/src/tilde/tb.lib index 43e477536..b0886ebfb 100644 Binary files a/src/tilde/tb.lib and b/src/tilde/tb.lib differ diff --git a/src/tilde/tb_arena.h b/src/tilde/tb_arena.h new file mode 100644 index 000000000..d50e777da --- /dev/null +++ b/src/tilde/tb_arena.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include + +#ifndef TB_API +# ifdef __cplusplus +# define TB_EXTERN extern "C" +# else +# define TB_EXTERN +# endif +# ifdef TB_DLL +# ifdef TB_IMPORT_DLL +# define TB_API TB_EXTERN __declspec(dllimport) +# else +# define TB_API TB_EXTERN __declspec(dllexport) +# endif +# else +# define TB_API TB_EXTERN +# endif +#endif + +enum { + TB_ARENA_SMALL_CHUNK_SIZE = 4 * 1024, + TB_ARENA_MEDIUM_CHUNK_SIZE = 512 * 1024, + TB_ARENA_LARGE_CHUNK_SIZE = 2 * 1024 * 1024, + + TB_ARENA_ALIGNMENT = 16, +}; + +typedef struct TB_ArenaChunk TB_ArenaChunk; +struct TB_ArenaChunk { + TB_ArenaChunk* next; + size_t pad; + char data[]; +}; + +typedef struct TB_Arena { + size_t chunk_size; + TB_ArenaChunk* base; + TB_ArenaChunk* top; + + // top of the allocation space + char* watermark; + char* high_point; // &top->data[chunk_size] +} TB_Arena; + +typedef struct TB_ArenaSavepoint { + TB_ArenaChunk* top; + char* watermark; +} TB_ArenaSavepoint; + +#define TB_ARENA_FOR(it, arena) for (TB_ArenaChunk* it = (arena)->base; it != NULL; it = it->next) + +#define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T)) +#define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T)) + +TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size); +TB_API void tb_arena_destroy(TB_Arena* restrict arena); + +TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size); +TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size); + +// asserts if ptr+size != watermark +TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size); + +// in case you wanna mix unaligned and aligned arenas +TB_API void tb_arena_realign(TB_Arena* restrict arena); + +TB_API bool tb_arena_is_empty(TB_Arena* arena); + +// savepoints +TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena); +TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp); + +// resets to only having one chunk +TB_API void tb_arena_clear(TB_Arena* arena); diff --git a/src/tilde_expr.cpp b/src/tilde_expr.cpp index 1966dcd8e..fdd26e3db 100644 --- a/src/tilde_expr.cpp +++ b/src/tilde_expr.cpp @@ -304,6 +304,42 @@ gb_internal cgValue cg_emit_byte_swap(cgProcedure *p, cgValue value, Type *end_t return cg_emit_transmute(p, value, end_type); } +gb_internal cgValue cg_emit_comp_records(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right, Type *type) { + GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type)); + cgValue left_ptr = cg_address_from_load_or_generate_local(p, left); + cgValue right_ptr = cg_address_from_load_or_generate_local(p, right); + cgValue res = {}; + if (type_size_of(type) == 0) { + switch (op_kind) { + case Token_CmpEq: + return cg_const_bool(p, t_bool, true); + case Token_NotEq: + return cg_const_bool(p, t_bool, false); + } + GB_PANIC("invalid operator"); + } + TEMPORARY_ALLOCATOR_GUARD(); + if (is_type_simple_compare(type)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = slice_make(temporary_allocator(), 3); + args[0] = cg_emit_conv(p, left_ptr, t_rawptr); + args[1] = cg_emit_conv(p, right_ptr, t_rawptr); + args[2] = cg_const_int(p, t_int, type_size_of(type)); + res = cg_emit_runtime_call(p, "memory_equal", args); + } else { + cgProcedure *equal_proc = cg_equal_proc_for_type(p->module, type); + cgValue value = cg_value(tb_inst_get_symbol_address(p->func, equal_proc->symbol), equal_proc->type); + auto args = slice_make(temporary_allocator(), 2); + args[0] = cg_emit_conv(p, left_ptr, t_rawptr); + args[1] = cg_emit_conv(p, right_ptr, t_rawptr); + res = cg_emit_call(p, value, args); + } + if (op_kind == Token_NotEq) { + res = cg_emit_unary_arith(p, Token_Not, res, res.type); + } + return res; +} + gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right) { GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1)); @@ -440,13 +476,11 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left } if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) { - GB_PANIC("TODO(bill): cg_compare_records"); - // return cg_compare_records(p, op_kind, left, right, a); + return cg_emit_comp_records(p, op_kind, left, right, a); } if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) { - GB_PANIC("TODO(bill): cg_compare_records"); - // return cg_compare_records(p, op_kind, left, right, b); + return cg_emit_comp_records(p, op_kind, left, right, b); } if (is_type_string(a)) { diff --git a/src/tilde_proc.cpp b/src/tilde_proc.cpp index 8cfc564a7..26c70b508 100644 --- a/src/tilde_proc.cpp +++ b/src/tilde_proc.cpp @@ -368,22 +368,17 @@ gb_internal void cg_procedure_begin(cgProcedure *p) { gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) { cgProcedure *p = cast(cgProcedure *)data; - bool emit_asm = false; + TB_Passes *opt = tb_pass_enter(p->func, cg_arena()); + defer (tb_pass_exit(opt)); - if (false && - string_starts_with(p->name, str_lit("bug@main"))) { - TB_Arena *arena = cg_arena(); - TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena); - defer (tb_funcopt_exit(opt)); - - tb_funcopt_peephole(opt); - tb_funcopt_mem2reg(opt); - tb_funcopt_peephole(opt); - - emit_asm = true; + // optimization passes + if (false) { + tb_pass_peephole(opt); + tb_pass_mem2reg(opt); + tb_pass_peephole(opt); } - + bool emit_asm = false; if ( // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc_or_resize")) || false @@ -391,12 +386,27 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) { emit_asm = true; } - TB_FunctionOutput *output = tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST, emit_asm); + // emit ir + if ( + // string_starts_with(p->name, str_lit("bug@main")) || + // p->name == str_lit("runtime@_windows_default_alloc_or_resize") || + false + ) { // IR Printing + TB_Arena *arena = cg_arena(); + TB_Passes *passes = tb_pass_enter(p->func, arena); + defer (tb_pass_exit(passes)); + + tb_pass_print(passes); + fprintf(stdout, "\n"); + } + if (false) { // GraphViz printing + tb_function_print(p->func, tb_default_print_callback, stdout); + } + + // compile + TB_FunctionOutput *output = tb_pass_codegen(opt, emit_asm); if (emit_asm) { - TB_Assembly *assembly = tb_output_get_asm(output); - for (TB_Assembly *node = assembly; node != nullptr; node = node->next) { - fprintf(stdout, "%.*s", cast(int)node->length, node->data); - } + tb_output_print_asm(output, stdout); fprintf(stdout, "\n"); } @@ -427,27 +437,9 @@ gb_internal void cg_procedure_generate(cgProcedure *p) { return; } - cg_procedure_begin(p); cg_build_stmt(p, p->body); cg_procedure_end(p); - - - if ( - // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc")) || - // p->name == str_lit("runtime@_windows_default_alloc_or_resize") || - false - ) { // IR Printing - TB_Arena *arena = tb_default_arena(); - defer (arena->free(arena)); - TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena); - defer (tb_funcopt_exit(opt)); - tb_funcopt_print(opt); - fprintf(stdout, "\n"); - } - if (false) { // GraphViz printing - tb_function_print(p->func, tb_default_print_callback, stdout); - } } gb_internal void cg_build_nested_proc(cgProcedure *p, AstProcLit *pd, Entity *e) { @@ -989,3 +981,95 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) { return cg_emit_call(p, value, call_args); } + + + + +gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) { + type = base_type(type); + GB_ASSERT(is_type_comparable(type)); + + mutex_lock(&m->generated_procs_mutex); + defer (mutex_unlock(&m->generated_procs_mutex)); + + cgProcedure **found = map_get(&m->equal_procs, type); + if (found) { + return *found; + } + + static std::atomic proc_index; + + char buf[32] = {}; + isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1)); + char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); + String proc_name = make_string_c(str); + + + cgProcedure *p = cg_procedure_create_dummy(m, proc_name, t_equal_proc); + map_set(&m->equal_procs, type, p); + + cg_procedure_begin(p); + + TB_Node *x = tb_inst_param(p->func, 0); + TB_Node *y = tb_inst_param(p->func, 1); + GB_ASSERT(x->dt.type == TB_PTR); + GB_ASSERT(y->dt.type == TB_PTR); + + TB_DataType ret_dt = TB_PROTOTYPE_RETURNS(p->proto)->dt; + + TB_Node *node_true = tb_inst_uint(p->func, ret_dt, true); + TB_Node *node_false = tb_inst_uint(p->func, ret_dt, false); + + TB_Node *same_ptr_region = cg_control_region(p, "same_ptr"); + TB_Node *diff_ptr_region = cg_control_region(p, "diff_ptr"); + + TB_Node *is_same_ptr = tb_inst_cmp_eq(p->func, x, y); + tb_inst_if(p->func, is_same_ptr, same_ptr_region, diff_ptr_region); + + tb_inst_set_control(p->func, same_ptr_region); + tb_inst_ret(p->func, 1, &node_true); + + tb_inst_set_control(p->func, diff_ptr_region); + + Type *pt = alloc_type_pointer(type); + cgValue lhs = cg_value(x, pt); + cgValue rhs = cg_value(y, pt); + + if (type->kind == Type_Struct) { + type_set_offsets(type); + + TB_Node *false_region = cg_control_region(p, "bfalse"); + cgValue res = cg_const_bool(p, t_bool, true); + + for_array(i, type->Struct.fields) { + TB_Node *next_region = cg_control_region(p, "btrue"); + + cgValue plhs = cg_emit_struct_ep(p, lhs, i); + cgValue prhs = cg_emit_struct_ep(p, rhs, i); + cgValue left = cg_emit_load(p, plhs); + cgValue right = cg_emit_load(p, prhs); + cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right); + + cg_emit_if(p, ok, next_region, false_region); + + cg_emit_goto(p, next_region); + tb_inst_set_control(p->func, next_region); + } + + tb_inst_ret(p->func, 1, &node_true); + tb_inst_set_control(p->func, false_region); + tb_inst_ret(p->func, 1, &node_false); + + } else if (type->kind == Type_Union) { + GB_PANIC("TODO(bill): union comparison"); + } else { + cgValue left = cg_lvalue_addr(x, type); + cgValue right = cg_lvalue_addr(y, type); + cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right); + cg_build_return_stmt_internal_single(p, ok); + } + + cg_procedure_end(p); + + return p; +} \ No newline at end of file diff --git a/src/tilde_stmt.cpp b/src/tilde_stmt.cpp index 0b5f122d4..382b4c02d 100644 --- a/src/tilde_stmt.cpp +++ b/src/tilde_stmt.cpp @@ -1047,6 +1047,14 @@ gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) { } } +gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result) { + Slice results = {}; + results.data = &result; + results.count = 1; + cg_build_return_stmt_internal(p, results); +} + + gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice const &results) { TypeTuple *tuple = &p->type->Proc.results->Tuple; isize return_count = p->type->Proc.result_count;