diff --git a/src/tilde.hpp b/src/tilde.hpp index a70d03a6c..5944c9ef7 100644 --- a/src/tilde.hpp +++ b/src/tilde.hpp @@ -350,9 +350,12 @@ gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, 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 void cg_emit_increment(cgProcedure *p, cgValue addr); -gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type); - +gb_internal cgProcedure *cg_equal_proc_for_type (cgModule *m, Type *type); +gb_internal cgProcedure *cg_hasher_proc_for_type(cgModule *m, Type *type); +gb_internal cgValue cg_hasher_proc_value_for_type(cgProcedure *p, Type *type); +gb_internal cgValue cg_equal_proc_value_for_type(cgProcedure *p, 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_builtin.cpp b/src/tilde_builtin.cpp index edf436424..9dbc3f7c5 100644 --- a/src/tilde_builtin.cpp +++ b/src/tilde_builtin.cpp @@ -432,6 +432,12 @@ gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr return cg_emit_runtime_call(p, "__type_info_of", args); } + + case BuiltinProc_type_equal_proc: + return cg_equal_proc_value_for_type(p, ce->args[0]->tav.type); + + case BuiltinProc_type_hasher_proc: + return cg_hasher_proc_value_for_type(p, ce->args[0]->tav.type); } diff --git a/src/tilde_expr.cpp b/src/tilde_expr.cpp index fdd26e3db..b654eb5e9 100644 --- a/src/tilde_expr.cpp +++ b/src/tilde_expr.cpp @@ -1780,6 +1780,48 @@ gb_internal void cg_emit_if(cgProcedure *p, cgValue const &cond, TB_Node *true_r tb_inst_if(p->func, cond.node, true_region, false_region); } + +struct cgLoopData { + cgAddr index_addr; + cgValue index; + TB_Node *body; + TB_Node *done; + TB_Node *loop; +}; + +gb_internal cgLoopData cg_loop_start(cgProcedure *p, isize count, Type *index_type) { + cgLoopData data = {}; + + cgValue max = cg_const_int(p, index_type, count); + + data.index_addr = cg_add_local(p, index_type, nullptr, true); + + data.body = cg_control_region(p, "loop_body"); + data.done = cg_control_region(p, "loop_done"); + data.loop = cg_control_region(p, "loop_loop"); + + cg_emit_goto(p, data.loop); + tb_inst_set_control(p->func, data.loop); + + data.index = cg_addr_load(p, data.index_addr); + + cgValue cond = cg_emit_comp(p, Token_Lt, data.index, max); + cg_emit_if(p, cond, data.body, data.done); + tb_inst_set_control(p->func, data.body); + + return data; +} + +gb_internal void cg_loop_end(cgProcedure *p, cgLoopData const &data) { + if (data.index_addr.addr.node != nullptr) { + cg_emit_increment(p, data.index_addr.addr); + cg_emit_goto(p, data.loop); + tb_inst_set_control(p->func, data.done); + } +} + + + gb_internal void cg_build_try_lhs_rhs(cgProcedure *p, Ast *arg, Type *final_type, cgValue *lhs_, cgValue *rhs_) { cgValue lhs = {}; cgValue rhs = {}; diff --git a/src/tilde_proc.cpp b/src/tilde_proc.cpp index 00062d708..1981d32ce 100644 --- a/src/tilde_proc.cpp +++ b/src/tilde_proc.cpp @@ -984,6 +984,17 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) { +gb_internal cgValue cg_hasher_proc_value_for_type(cgProcedure *p, Type *type) { + cgProcedure *found = cg_hasher_proc_for_type(p->module, type); + return cg_value(tb_inst_get_symbol_address(p->func, found->symbol), found->type); +} + +gb_internal cgValue cg_equal_proc_value_for_type(cgProcedure *p, Type *type) { + cgProcedure *found = cg_equal_proc_for_type(p->module, type); + return cg_value(tb_inst_get_symbol_address(p->func, found->symbol), found->type); +} + + gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) { type = base_type(type); @@ -1115,5 +1126,182 @@ gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) { cg_procedure_end(p); + return p; +} + + +gb_internal cgValue cg_simple_compare_hash(cgProcedure *p, Type *type, cgValue data, cgValue seed) { + TEMPORARY_ALLOCATOR_GUARD(); + + GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); + + auto args = slice_make(temporary_allocator(), 3); + args[0] = data; + args[1] = seed; + args[2] = cg_const_int(p, t_int, type_size_of(type)); + return cg_emit_runtime_call(p, "default_hasher", args); +} + + + + + +gb_internal cgProcedure *cg_hasher_proc_for_type(cgModule *m, Type *type) { + type = base_type(type); + GB_ASSERT(is_type_valid_for_keys(type)); + + mutex_lock(&m->generated_procs_mutex); + defer (mutex_unlock(&m->generated_procs_mutex)); + + cgProcedure **found = map_get(&m->hasher_procs, type); + if (found) { + return *found; + } + + static std::atomic proc_index; + + char buf[32] = {}; + isize n = gb_snprintf(buf, 32, "__$hasher%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_hasher_proc); + map_set(&m->hasher_procs, type, p); + + cg_procedure_begin(p); + defer (cg_procedure_end(p)); + + TB_Node *x = tb_inst_param(p->func, 0); // data + TB_Node *y = tb_inst_param(p->func, 1); // seed + + cgValue data = cg_value(x, t_rawptr); + cgValue seed = cg_value(y, t_uintptr); + + if (is_type_simple_compare(type)) { + cgValue res = cg_simple_compare_hash(p, type, data, seed); + cg_build_return_stmt_internal_single(p, res); + return p; + } + + TEMPORARY_ALLOCATOR_GUARD(); + + auto args = slice_make(temporary_allocator(), 2); + + if (type->kind == Type_Struct) { + type_set_offsets(type); + for_array(i, type->Struct.fields) { + i64 offset = type->Struct.offsets[i]; + Entity *field = type->Struct.fields[i]; + cgValue field_hasher = cg_hasher_proc_value_for_type(p, field->type); + + TB_Node *ptr = tb_inst_member_access(p->func, data.node, offset); + + args[0] = cg_value(ptr, alloc_type_pointer(field->type)); + args[1] = seed; + seed = cg_emit_call(p, field_hasher, args); + } + + cg_build_return_stmt_internal_single(p, seed); + } else if (type->kind == Type_Union) { + if (type_size_of(type) == 0) { + cg_build_return_stmt_internal_single(p, seed); + } else if (is_type_union_maybe_pointer(type)) { + Type *v = type->Union.variants[0]; + cgValue variant_hasher = cg_hasher_proc_value_for_type(p, v); + + args[0] = data; + args[1] = seed; + cgValue res = cg_emit_call(p, variant_hasher, args); + cg_build_return_stmt_internal_single(p, seed); + } else { + TB_Node *end_region = cg_control_region(p, "bend"); + TB_Node *switch_region = cg_control_region(p, "bswitch"); + + cg_emit_goto(p, switch_region); + + size_t entry_count = type->Union.variants.count; + TB_SwitchEntry *keys = gb_alloc_array(temporary_allocator(), TB_SwitchEntry, entry_count); + for (size_t i = 0; i < entry_count; i++) { + TB_Node *region = cg_control_region(p, "bcase"); + Type *variant = type->Union.variants[i]; + keys[i].key = union_variant_index(type, variant); + keys[i].value = region; + + tb_inst_set_control(p->func, region); + + cgValue variant_hasher = cg_hasher_proc_value_for_type(p, variant); + + args[0] = data; + args[1] = seed; + cgValue res = cg_emit_call(p, variant_hasher, args); + cg_build_return_stmt_internal_single(p, res); + } + + tb_inst_set_control(p->func, switch_region); + + cgValue tag_ptr = cg_emit_union_tag_ptr(p, data); + cgValue tag = cg_emit_load(p, tag_ptr); + + TB_DataType tag_dt = cg_data_type(tag.type); + GB_ASSERT(tag.kind == cgValue_Value); + tb_inst_branch(p->func, tag_dt, tag.node, end_region, entry_count, keys); + + tb_inst_set_control(p->func, end_region); + cg_build_return_stmt_internal_single(p, seed); + } + } else if (type->kind == Type_Array) { + cgAddr pres = cg_add_local(p, t_uintptr, nullptr, false); + cg_addr_store(p, pres, seed); + + cgValue elem_hasher = cg_hasher_proc_value_for_type(p, type->Array.elem); + + auto loop_data = cg_loop_start(p, type->Array.count, t_int); + + i64 stride = type_size_of(type->Array.elem); + TB_Node *ptr = tb_inst_array_access(p->func, data.node, loop_data.index.node, stride); + args[0] = cg_value(ptr, alloc_type_pointer(type->Array.elem)); + args[1] = cg_addr_load(p, pres); + + cgValue new_seed = cg_emit_call(p, elem_hasher, args); + cg_addr_store(p, pres, new_seed); + + cg_loop_end(p, loop_data); + + cgValue res = cg_addr_load(p, pres); + cg_build_return_stmt_internal_single(p, res); + } else if (type->kind == Type_EnumeratedArray) { + cgAddr pres = cg_add_local(p, t_uintptr, nullptr, false); + cg_addr_store(p, pres, seed); + + cgValue elem_hasher = cg_hasher_proc_value_for_type(p, type->EnumeratedArray.elem); + + auto loop_data = cg_loop_start(p, type->EnumeratedArray.count, t_int); + + i64 stride = type_size_of(type->EnumeratedArray.elem); + TB_Node *ptr = tb_inst_array_access(p->func, data.node, loop_data.index.node, stride); + args[0] = cg_value(ptr, alloc_type_pointer(type->EnumeratedArray.elem)); + args[1] = cg_addr_load(p, pres); + + cgValue new_seed = cg_emit_call(p, elem_hasher, args); + cg_addr_store(p, pres, new_seed); + + cg_loop_end(p, loop_data); + + cgValue res = cg_addr_load(p, pres); + cg_build_return_stmt_internal_single(p, res); + } else if (is_type_cstring(type)) { + args[0] = data; + args[1] = seed; + cgValue res = cg_emit_runtime_call(p, "default_hasher_cstring", args); + cg_build_return_stmt_internal_single(p, seed); + } else if (is_type_string(type)) { + args[0] = data; + args[1] = seed; + cgValue res = cg_emit_runtime_call(p, "default_hasher_string", args); + cg_build_return_stmt_internal_single(p, seed); + } else { + GB_PANIC("Unhandled type for hasher: %s", type_to_string(type)); + } return p; } \ No newline at end of file