Browse Source

Add hasher proc generation

gingerBill 2 years ago
parent
commit
4a71603a77
4 changed files with 241 additions and 2 deletions
  1. 5 2
      src/tilde.hpp
  2. 6 0
      src/tilde_builtin.cpp
  3. 42 0
      src/tilde_expr.cpp
  4. 188 0
      src/tilde_proc.cpp

+ 5 - 2
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<cgValue> const &args);
 gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);

+ 6 - 0
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);
 	}
 
 

+ 42 - 0
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 = {};

+ 188 - 0
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<cgValue>(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<u32> 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<cgValue>(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;
 }