Browse Source

Add comparisons to structs where all fields are comparable `==` and `!=`

gingerBill 4 years ago
parent
commit
9e42cb1595
7 changed files with 292 additions and 53 deletions
  1. 3 2
      core/runtime/internal.odin
  2. 1 0
      src/checker.cpp
  3. 114 1
      src/ir.cpp
  4. 10 50
      src/llvm_abi.cpp
  5. 98 0
      src/llvm_backend.cpp
  6. 2 0
      src/llvm_backend.hpp
  7. 64 0
      src/types.cpp

+ 3 - 2
core/runtime/internal.odin

@@ -180,8 +180,9 @@ mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int =
 	}
 	}
 	return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
 	return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
 }
 }
-
-
+memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
+	return memory_compare(a, b, n) == 0;
+}
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
 memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
 	x := uintptr(a);
 	x := uintptr(a);
 	y := uintptr(b);
 	y := uintptr(b);

+ 1 - 0
src/checker.cpp

@@ -1768,6 +1768,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("memcpy"),
 		str_lit("memcpy"),
 		str_lit("memmove"),
 		str_lit("memmove"),
 
 
+		str_lit("memory_equal"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare"),
 		str_lit("memory_compare_zero"),
 		str_lit("memory_compare_zero"),
 
 

+ 114 - 1
src/ir.cpp

@@ -24,6 +24,7 @@ struct irModule {
 	Map<String>           entity_names;        // Key: Entity * of the typename
 	Map<String>           entity_names;        // Key: Entity * of the typename
 	Map<irDebugInfo *>    debug_info;          // Key: Unique pointer
 	Map<irDebugInfo *>    debug_info;          // Key: Unique pointer
 	Map<irValue *>        anonymous_proc_lits; // Key: Ast *
 	Map<irValue *>        anonymous_proc_lits; // Key: Ast *
+	Map<irValue *>        compare_procs; // Key: Type *
 
 
 	irDebugInfo *         debug_compile_unit;
 	irDebugInfo *         debug_compile_unit;
 	Array<irDebugInfo *>  debug_location_stack;
 	Array<irDebugInfo *>  debug_location_stack;
@@ -161,6 +162,7 @@ struct irProcedure {
 	Ast *    return_ptr_hint_ast;
 	Ast *    return_ptr_hint_ast;
 	bool     return_ptr_hint_used;
 	bool     return_ptr_hint_used;
 
 
+	bool ignore_dead_instr;
 
 
 	Array<irBranchBlocks> branch_blocks;
 	Array<irBranchBlocks> branch_blocks;
 
 
@@ -525,6 +527,8 @@ struct irAddr {
 
 
 Type *ir_type(irValue *value);
 Type *ir_type(irValue *value);
 irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
 irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, Ast *expr, irProcedure *proc = nullptr);
+void ir_begin_procedure_body(irProcedure *proc);
+void ir_end_procedure_body(irProcedure *proc);
 
 
 irAddr ir_addr(irValue *addr) {
 irAddr ir_addr(irValue *addr) {
 	irAddr v = {irAddr_Default, addr};
 	irAddr v = {irAddr_Default, addr};
@@ -4859,6 +4863,87 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
 	return nullptr;
 	return nullptr;
 }
 }
 
 
+irValue *ir_get_compare_proc_for_type(irModule *m, Type *type) {
+	Type *original_type = type;
+	type = base_type(type);
+	GB_ASSERT(type->kind == Type_Struct);
+	type_set_offsets(type);
+	Type *pt = alloc_type_pointer(type);
+
+	auto key = hash_type(type);
+	irValue **found = map_get(&m->compare_procs, key);
+	if (found) {
+		return *found;
+	}
+	static Type *proc_type = nullptr;
+	if (proc_type == nullptr) {
+		Type *args[2] = {t_rawptr, t_rawptr};
+		proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless);
+		set_procedure_abi_types(proc_type);
+	}
+
+	static u32 proc_index = 0;
+
+	char buf[16] = {};
+	isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index);
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	Ast *body = alloc_ast_node(nullptr, Ast_Invalid);
+	Entity *e = alloc_entity_procedure(nullptr, make_token_ident(proc_name), proc_type, 0);
+	e->Procedure.link_name = proc_name;
+	irValue *p = ir_value_procedure(m, e, proc_type, nullptr, body, proc_name);
+	map_set(&m->values, hash_entity(e), p);
+	string_map_set(&m->members, proc_name, p);
+
+	irProcedure *proc = &p->Proc;
+	proc->is_startup = true;
+	proc->ignore_dead_instr = true;
+	ir_begin_procedure_body(proc);
+	// ir_start_block(proc, proc->decl_block);
+	GB_ASSERT(proc->curr_block != nullptr);
+
+	irBlock *done = ir_new_block(proc, nullptr, "done"); // NOTE(bill): Append later
+
+	irValue *x = proc->params[0];
+	irValue *y = proc->params[1];
+	irValue *lhs = ir_emit_conv(proc, x, pt);
+	irValue *rhs = ir_emit_conv(proc, y, pt);
+
+	irBlock *block_false = ir_new_block(proc, nullptr, "bfalse");
+
+	for_array(i, type->Struct.fields) {
+		irBlock *next_block = ir_new_block(proc, nullptr, "btrue");
+
+		irValue *pleft  = ir_emit_struct_ep(proc, lhs, cast(i32)i);
+		irValue *pright = ir_emit_struct_ep(proc, rhs, cast(i32)i);
+		irValue *left = ir_emit_load(proc, pleft);
+		irValue *right = ir_emit_load(proc, pright);
+		irValue *ok = ir_emit_comp(proc, Token_CmpEq, left, right);
+
+		ir_emit_if(proc, ok, next_block, block_false);
+
+		ir_emit_jump(proc, next_block);
+		ir_start_block(proc, next_block);
+	}
+
+	ir_emit_jump(proc, done);
+	ir_start_block(proc, block_false);
+
+	ir_emit(proc, ir_instr_return(proc, ir_const_bool(false)));
+
+	ir_emit_jump(proc, done);
+	ir_start_block(proc, done);
+	ir_emit(proc, ir_instr_return(proc, ir_const_bool(true)));
+
+	ir_end_procedure_body(proc);
+
+	map_set(&m->compare_procs, key, p);
+
+	return p;
+}
+
 irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
 irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irValue *right) {
 	Type *a = base_type(ir_type(left));
 	Type *a = base_type(ir_type(left));
 	Type *b = base_type(ir_type(right));
 	Type *b = base_type(ir_type(right));
@@ -4992,6 +5077,30 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
 		}
 		}
 	}
 	}
 
 
+	if (is_type_struct(a) && is_type_comparable(a)) {
+		irValue *left_ptr  = ir_address_from_load_or_generate_local(proc, left);
+		irValue *right_ptr = ir_address_from_load_or_generate_local(proc, right);
+		irValue *res = {};
+		if (is_type_simple_compare(a)) {
+			// TODO(bill): Test to see if this is actually faster!!!!
+			auto args = array_make<irValue *>(permanent_allocator(), 3);
+			args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+			args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+			args[2] = ir_const_int(type_size_of(a));
+			res = ir_emit_runtime_call(proc, "memory_equal", args);
+		} else {
+			irValue *value = ir_get_compare_proc_for_type(proc->module, a);
+			auto args = array_make<irValue *>(permanent_allocator(), 2);
+			args[0] = ir_emit_conv(proc, left_ptr, t_rawptr);
+			args[1] = ir_emit_conv(proc, right_ptr, t_rawptr);
+			res = ir_emit_call(proc, value, args);
+		}
+		if (op_kind == Token_NotEq) {
+			res = ir_emit_unary_arith(proc, Token_Not, res, ir_type(res));
+		}
+		return res;
+	}
+
 	if (is_type_string(a)) {
 	if (is_type_string(a)) {
 		if (is_type_cstring(a)) {
 		if (is_type_cstring(a)) {
 			left  = ir_emit_conv(proc, left, t_string);
 			left  = ir_emit_conv(proc, left, t_string);
@@ -11350,6 +11459,9 @@ void ir_begin_procedure_body(irProcedure *proc) {
 
 
 
 
 bool ir_remove_dead_instr(irProcedure *proc) {
 bool ir_remove_dead_instr(irProcedure *proc) {
+	if (proc->ignore_dead_instr) {
+		return false;
+	}
 	isize elimination_count = 0;
 	isize elimination_count = 0;
 retry:
 retry:
 #if 1
 #if 1
@@ -11476,7 +11588,7 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
 
 
 	proc->parent = parent;
 	proc->parent = parent;
 
 
-	if (proc->body != nullptr) {
+	if (proc->body != nullptr && proc->body->kind != Ast_Invalid) {
 		u64 prev_state_flags = proc->module->state_flags;
 		u64 prev_state_flags = proc->module->state_flags;
 
 
 		if (proc->tags != 0) {
 		if (proc->tags != 0) {
@@ -11578,6 +11690,7 @@ void ir_init_module(irModule *m, Checker *c) {
 	map_init(&m->debug_info,               heap_allocator());
 	map_init(&m->debug_info,               heap_allocator());
 	map_init(&m->entity_names,             heap_allocator());
 	map_init(&m->entity_names,             heap_allocator());
 	map_init(&m->anonymous_proc_lits,      heap_allocator());
 	map_init(&m->anonymous_proc_lits,      heap_allocator());
+	map_init(&m->compare_procs,            heap_allocator());
 	array_init(&m->procs,                  heap_allocator());
 	array_init(&m->procs,                  heap_allocator());
 	array_init(&m->procs_to_generate,      heap_allocator());
 	array_init(&m->procs_to_generate,      heap_allocator());
 	array_init(&m->foreign_library_paths,  heap_allocator());
 	array_init(&m->foreign_library_paths,  heap_allocator());

+ 10 - 50
src/llvm_abi.cpp

@@ -267,56 +267,6 @@ i64 lb_alignof(LLVMTypeRef type) {
 	return 1;
 	return 1;
 }
 }
 
 
-Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) {
-	Type *t = alloc_type_struct();
-	t->Struct.fields = array_make<Entity *>(heap_allocator(), field_count);
-
-	Scope *scope = nullptr;
-	for_array(i, t->Struct.fields) {
-		t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved);
-	}
-	t->Struct.is_packed = is_packed;
-
-	return t;
-}
-
-Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) {
-	if (field_count == 0) {
-		return nullptr;
-	}
-	if (!must_be_tuple && field_count == 1) {
-		return field_types[0];
-	}
-
-	Type *t = alloc_type_tuple();
-	t->Tuple.variables = array_make<Entity *>(heap_allocator(), field_count);
-
-	Scope *scope = nullptr;
-	for_array(i, t->Tuple.variables) {
-		t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false);
-	}
-	t->Tuple.is_packed = is_packed;
-
-	return t;
-}
-
-Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg) {
-
-	Type *params  = alloc_type_tuple_from_field_types(param_types, param_count, false, true);
-	isize results_count = 0;
-	if (results != nullptr) {
-		if (results->kind != Type_Tuple) {
-			results = alloc_type_tuple_from_field_types(&results, 1, false, true);
-		}
-		results_count = results->Tuple.variables.count;
-	}
-
-	Scope *scope = nullptr;
-	Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, /*not sure what to put here*/ProcCC_CDecl);
-	t->Proc.c_vararg = is_c_vararg;
-	return t;
-}
-
 #if 0
 #if 0
 Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) {
 Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) {
 	Type **found = map_get(&m->llvm_types, hash_pointer(type));
 	Type **found = map_get(&m->llvm_types, hash_pointer(type));
@@ -959,6 +909,16 @@ namespace lbAbiAmd64SysV {
 };
 };
 
 
 
 
+namespace lbAbiAarch64 {
+	LB_ABI_INFO(abi_info) {
+		lbFunctionType *ft = gb_alloc_item(heap_allocator(), lbFunctionType);
+		ft->ctx = c;
+		// ft->args = compute_arg_types(c, arg_types, arg_count);
+		// ft->ret = lbAbi386::compute_return_type(c, return_type, return_is_defined);
+		// ft->calling_convention = calling_convention;
+		return ft;
+	}
+}
 
 
 
 
 LB_ABI_INFO(lb_get_abi_info) {
 LB_ABI_INFO(lb_get_abi_info) {

+ 98 - 0
src/llvm_backend.cpp

@@ -9143,6 +9143,78 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 	return {};
 	return {};
 }
 }
 
 
+lbValue lb_get_compare_proc_for_type(lbModule *m, Type *type) {
+	Type *original_type = type;
+	type = base_type(type);
+	GB_ASSERT(type->kind == Type_Struct);
+	type_set_offsets(type);
+	Type *pt = alloc_type_pointer(type);
+	LLVMTypeRef ptr_type = lb_type(m, pt);
+
+	auto key = hash_type(type);
+	lbProcedure **found = map_get(&m->compare_procs, key);
+	lbProcedure *compare_proc = nullptr;
+	if (found) {
+		compare_proc = *found;
+	} else {
+		static Type *proc_type = nullptr;
+		if (proc_type == nullptr) {
+			Type *args[2] = {t_rawptr, t_rawptr};
+			proc_type = alloc_type_proc_from_types(args, 2, t_bool, false, ProcCC_Contextless);
+			set_procedure_abi_types(proc_type);
+		}
+
+		static u32 proc_index = 0;
+
+		char buf[16] = {};
+		isize n = gb_snprintf(buf, 16, "__$cmp%u", ++proc_index);
+		char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+		String proc_name = make_string_c(str);
+
+		lbProcedure *p = lb_create_dummy_procedure(m, proc_name, proc_type);
+		lb_begin_procedure_body(p);
+
+		LLVMValueRef x = LLVMGetParam(p->value, 0);
+		LLVMValueRef y = LLVMGetParam(p->value, 1);
+		x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
+		y = LLVMBuildPointerCast(p->builder, y, ptr_type, "");
+		lbValue lhs = {x, pt};
+		lbValue rhs = {y, pt};
+
+		lbBlock *block_false = lb_create_block(p, "bfalse");
+
+		lbValue res = lb_const_bool(m, t_bool, true);
+		for_array(i, type->Struct.fields) {
+			lbBlock *next_block = lb_create_block(p, "btrue");
+
+			lbValue pleft  = lb_emit_struct_ep(p, lhs, cast(i32)i);
+			lbValue pright = lb_emit_struct_ep(p, rhs, cast(i32)i);
+			lbValue left = lb_emit_load(p, pleft);
+			lbValue right = lb_emit_load(p, pright);
+			lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right);
+
+			lb_emit_if(p, ok, next_block, block_false);
+
+			lb_emit_jump(p, next_block);
+			lb_start_block(p, next_block);
+		}
+
+		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false));
+
+		lb_start_block(p, block_false);
+
+		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false));
+
+		lb_end_procedure_body(p);
+
+		map_set(&m->compare_procs, key, p);
+
+		compare_proc = p;
+	}
+	GB_ASSERT(compare_proc != nullptr);
+
+	return {compare_proc->value, compare_proc->type};
+}
 
 
 lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
 lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
 	Type *a = core_type(left.type);
 	Type *a = core_type(left.type);
@@ -9272,6 +9344,31 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 		}
 		}
 	}
 	}
 
 
+
+	if (is_type_struct(a) && is_type_comparable(a)) {
+		lbValue left_ptr  = lb_address_from_load_or_generate_local(p, left);
+		lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
+		lbValue res = {};
+		if (is_type_simple_compare(a)) {
+			// TODO(bill): Test to see if this is actually faster!!!!
+			auto args = array_make<lbValue>(permanent_allocator(), 3);
+			args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
+			args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
+			args[2] = lb_const_int(p->module, t_int, type_size_of(a));
+			res = lb_emit_runtime_call(p, "memory_equal", args);
+		} else {
+			lbValue value = lb_get_compare_proc_for_type(p->module, a);
+			auto args = array_make<lbValue>(permanent_allocator(), 2);
+			args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
+			args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
+			res = lb_emit_call(p, value, args);
+		}
+		if (op_kind == Token_NotEq) {
+			res = lb_emit_unary_arith(p, Token_Not, res, res.type);
+		}
+		return res;
+	}
+
 	if (is_type_string(a)) {
 	if (is_type_string(a)) {
 		if (is_type_cstring(a)) {
 		if (is_type_cstring(a)) {
 			left  = lb_emit_conv(p, left, t_string);
 			left  = lb_emit_conv(p, left, t_string);
@@ -11442,6 +11539,7 @@ void lb_init_module(lbModule *m, Checker *c) {
 	string_map_init(&m->const_strings, a);
 	string_map_init(&m->const_strings, a);
 	map_init(&m->anonymous_proc_lits, a);
 	map_init(&m->anonymous_proc_lits, a);
 	map_init(&m->function_type_map, a);
 	map_init(&m->function_type_map, a);
+	map_init(&m->compare_procs, a);
 	array_init(&m->procedures_to_generate, a);
 	array_init(&m->procedures_to_generate, a);
 	array_init(&m->foreign_library_paths, a);
 	array_init(&m->foreign_library_paths, a);
 
 

+ 2 - 0
src/llvm_backend.hpp

@@ -87,6 +87,8 @@ struct lbModule {
 	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
 	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
 	Map<struct lbFunctionType *> function_type_map; // Key: Type *
 	Map<struct lbFunctionType *> function_type_map; // Key: Type *
 
 
+	Map<lbProcedure *> compare_procs; // Key: Type *
+
 	u32 global_array_index;
 	u32 global_array_index;
 	u32 global_generated_index;
 	u32 global_generated_index;
 	u32 nested_type_name_guid;
 	u32 nested_type_name_guid;

+ 64 - 0
src/types.cpp

@@ -1942,6 +1942,15 @@ bool is_type_comparable(Type *t) {
 
 
 	case Type_Opaque:
 	case Type_Opaque:
 		return is_type_comparable(t->Opaque.elem);
 		return is_type_comparable(t->Opaque.elem);
+
+	case Type_Struct:
+		for_array(i, t->Struct.fields) {
+			Entity *f = t->Struct.fields[i];
+			if (!is_type_comparable(f->type)) {
+				return false;
+			}
+		}
+		return true;
 	}
 	}
 	return false;
 	return false;
 }
 }
@@ -3401,6 +3410,58 @@ Type *reduce_tuple_to_single_type(Type *original_type) {
 }
 }
 
 
 
 
+Type *alloc_type_struct_from_field_types(Type **field_types, isize field_count, bool is_packed) {
+	Type *t = alloc_type_struct();
+	t->Struct.fields = array_make<Entity *>(heap_allocator(), field_count);
+
+	Scope *scope = nullptr;
+	for_array(i, t->Struct.fields) {
+		t->Struct.fields[i] = alloc_entity_field(scope, blank_token, field_types[i], false, cast(i32)i, EntityState_Resolved);
+	}
+	t->Struct.is_packed = is_packed;
+
+	return t;
+}
+
+Type *alloc_type_tuple_from_field_types(Type **field_types, isize field_count, bool is_packed, bool must_be_tuple) {
+	if (field_count == 0) {
+		return nullptr;
+	}
+	if (!must_be_tuple && field_count == 1) {
+		return field_types[0];
+	}
+
+	Type *t = alloc_type_tuple();
+	t->Tuple.variables = array_make<Entity *>(heap_allocator(), field_count);
+
+	Scope *scope = nullptr;
+	for_array(i, t->Tuple.variables) {
+		t->Tuple.variables[i] = alloc_entity_param(scope, blank_token, field_types[i], false, false);
+	}
+	t->Tuple.is_packed = is_packed;
+
+	return t;
+}
+
+Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type *results, bool is_c_vararg, ProcCallingConvention calling_convention) {
+
+	Type *params  = alloc_type_tuple_from_field_types(param_types, param_count, false, true);
+	isize results_count = 0;
+	if (results != nullptr) {
+		if (results->kind != Type_Tuple) {
+			results = alloc_type_tuple_from_field_types(&results, 1, false, true);
+		}
+		results_count = results->Tuple.variables.count;
+	}
+
+	Scope *scope = nullptr;
+	Type *t = alloc_type_proc(scope, params, param_count, results, results_count, false, calling_convention);
+	t->Proc.c_vararg = is_c_vararg;
+	return t;
+}
+
+
+
 gbString write_type_to_string(gbString str, Type *type) {
 gbString write_type_to_string(gbString str, Type *type) {
 	if (type == nullptr) {
 	if (type == nullptr) {
 		return gb_string_appendc(str, "<no type>");
 		return gb_string_appendc(str, "<no type>");
@@ -3719,3 +3780,6 @@ gbString type_to_string(Type *type) {
 	return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
 	return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
 }
 }
 
 
+
+
+