浏览代码

Fill out more of the tilde expression code

gingerBill 1 年之前
父节点
当前提交
915db6d228
共有 4 个文件被更改,包括 296 次插入104 次删除
  1. 8 8
      src/llvm_backend_expr.cpp
  2. 3 0
      src/tilde.hpp
  3. 283 96
      src/tilde_expr.cpp
  4. 2 0
      src/tilde_proc.cpp

+ 8 - 8
src/llvm_backend_expr.cpp

@@ -1691,6 +1691,14 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		return res;
 	}
 
+	// integer -> boolean
+	if (is_type_integer(src) && is_type_boolean(dst)) {
+		lbValue res = {};
+		res.value = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, value.type)), "");
+		res.type = t_llvm_bool;
+		return lb_emit_conv(p, res, t);
+	}
+
 	if (is_type_cstring(src) && is_type_u8_ptr(dst)) {
 		return lb_emit_transmute(p, value, dst);
 	}
@@ -1721,14 +1729,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	}
 
 
-	// integer -> boolean
-	if (is_type_integer(src) && is_type_boolean(dst)) {
-		lbValue res = {};
-		res.value = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, value.type)), "");
-		res.type = t_llvm_bool;
-		return lb_emit_conv(p, res, t);
-	}
-
 	// float -> float
 	if (is_type_float(src) && is_type_float(dst)) {
 		i64 sz = type_size_of(src);

+ 3 - 0
src/tilde.hpp

@@ -375,6 +375,9 @@ gb_internal isize cg_append_tuple_values(cgProcedure *p, Array<cgValue> *dst_val
 gb_internal cgValue cg_handle_param_value(cgProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
 
 gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value);
+gb_internal cgValue cg_builtin_mem_zero(cgProcedure *p, cgValue const &ptr, cgValue const &len);
+gb_internal cgValue cg_builtin_mem_copy(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len);
+gb_internal cgValue cg_builtin_mem_copy_non_overlapping(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len);
 gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &x);
 gb_internal cgValue cg_builtin_map_info(cgProcedure *p, Type *map_type);
 gb_internal cgValue cg_builtin_map_cell_info(cgProcedure *p, Type *type);

+ 283 - 96
src/tilde_expr.cpp

@@ -15,6 +15,11 @@ gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value) {
 	return value;
 }
 
+gb_internal void cg_emit_if(cgProcedure *p, cgValue const &cond, TB_Node *true_region, TB_Node *false_region) {
+	GB_ASSERT(cond.kind == cgValue_Value);
+	tb_inst_if(p->func, cond.node, true_region, false_region);
+}
+
 gb_internal cgValue cg_emit_select(cgProcedure *p, cgValue const &cond, cgValue const &x, cgValue const &y) {
 	GB_ASSERT(x.kind == y.kind);
 	GB_ASSERT(cond.kind == cgValue_Value);
@@ -487,7 +492,27 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left
 	}
 
 	if (is_type_string(a)) {
-		if (is_type_cstring(a)) {
+		if (is_type_cstring(a) && is_type_cstring(b)) {
+			left  = cg_emit_conv(p, left, t_cstring);
+			right = cg_emit_conv(p, right, t_cstring);
+			char const *runtime_procedure = nullptr;
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "cstring_eq"; break;
+			case Token_NotEq: runtime_procedure = "cstring_ne"; break;
+			case Token_Lt:    runtime_procedure = "cstring_lt"; break;
+			case Token_Gt:    runtime_procedure = "cstring_gt"; break;
+			case Token_LtEq:  runtime_procedure = "cstring_le"; break;
+			case Token_GtEq:  runtime_procedure = "cstring_gt"; break;
+			}
+			GB_ASSERT(runtime_procedure != nullptr);
+
+			auto args = slice_make<cgValue>(permanent_allocator(), 2);
+			args[0] = left;
+			args[1] = right;
+			return cg_emit_runtime_call(p, runtime_procedure, args);
+		}
+
+		if (is_type_cstring(a) ^ is_type_cstring(b)) {
 			left  = cg_emit_conv(p, left, t_string);
 			right = cg_emit_conv(p, right, t_string);
 		}
@@ -534,11 +559,10 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left
 		}
 		GB_ASSERT(runtime_procedure != nullptr);
 
-		GB_PANIC("TODO(bill): cg_emit_runtime_call");
-		// auto args = array_make<lbValue>(permanent_allocator(), 2);
-		// args[0] = left;
-		// args[1] = right;
-		// return lb_emit_runtime_call(p, runtime_procedure, args);
+		auto args = slice_make<cgValue>(permanent_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return cg_emit_runtime_call(p, runtime_procedure, args);
 	}
 
 	if (is_type_quaternion(a)) {
@@ -566,11 +590,10 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left
 		}
 		GB_ASSERT(runtime_procedure != nullptr);
 
-		GB_PANIC("TODO(bill): cg_emit_runtime_call");
-		// auto args = array_make<lbValue>(permanent_allocator(), 2);
-		// args[0] = left;
-		// args[1] = right;
-		// return lb_emit_runtime_call(p, runtime_procedure, args);
+		auto args = slice_make<cgValue>(permanent_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return cg_emit_runtime_call(p, runtime_procedure, args);
 	}
 
 	if (is_type_bit_set(a)) {
@@ -824,57 +847,104 @@ gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind,
 		}
 		break;
 	case Type_Struct:
-		GB_PANIC("TODO(bill): cg_emit_struct_ev");
-		// if (is_type_soa_struct(t)) {
-		// 	Type *bt = base_type(t);
-		// 	if (bt->Struct.soa_kind == StructSoa_Slice) {
-		// 		LLVMValueRef the_value = {};
-		// 		if (bt->Struct.fields.count == 0) {
-		// 			cgValue len = cg_soa_struct_len(p, x);
-		// 			the_value = len.value;
-		// 		} else {
-		// 			cgValue first_field = cg_emit_struct_ev(p, x, 0);
-		// 			the_value = first_field.value;
-		// 		}
-		// 		if (op_kind == Token_CmpEq) {
-		// 			res.value = LLVMBuildIsNull(p->builder, the_value, "");
-		// 			return res;
-		// 		} else if (op_kind == Token_NotEq) {
-		// 			res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
-		// 			return res;
-		// 		}
-		// 	} else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
-		// 		LLVMValueRef the_value = {};
-		// 		if (bt->Struct.fields.count == 0) {
-		// 			cgValue cap = cg_soa_struct_cap(p, x);
-		// 			the_value = cap.value;
-		// 		} else {
-		// 			cgValue first_field = cg_emit_struct_ev(p, x, 0);
-		// 			the_value = first_field.value;
-		// 		}
-		// 		if (op_kind == Token_CmpEq) {
-		// 			res.value = LLVMBuildIsNull(p->builder, the_value, "");
-		// 			return res;
-		// 		} else if (op_kind == Token_NotEq) {
-		// 			res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
-		// 			return res;
-		// 		}
-		// 	}
-		// } else if (is_type_struct(t) && type_has_nil(t)) {
-		// 	auto args = array_make<cgValue>(permanent_allocator(), 2);
-		// 	cgValue lhs = cg_address_from_load_or_generate_local(p, x);
-		// 	args[0] = cg_emit_conv(p, lhs, t_rawptr);
-		// 	args[1] = cg_const_int(p->module, t_int, type_size_of(t));
-		// 	cgValue val = cg_emit_runtime_call(p, "memory_compare_zero", args);
-		// 	cgValue res = cg_emit_comp(p, op_kind, val, cg_const_int(p->module, t_int, 0));
-		// 	return res;
-		// }
+		if (is_type_soa_struct(t)) {
+			GB_PANIC("TODO(bill): #soa cg_emit_comp_against_nil");
+			// Type *bt = base_type(t);
+			// if (bt->Struct.soa_kind == StructSoa_Slice) {
+			// 	LLVMValueRef the_value = {};
+			// 	if (bt->Struct.fields.count == 0) {
+			// 		cgValue len = cg_soa_struct_len(p, x);
+			// 		the_value = len.value;
+			// 	} else {
+			// 		cgValue first_field = cg_emit_struct_ev(p, x, 0);
+			// 		the_value = first_field.value;
+			// 	}
+			// 	if (op_kind == Token_CmpEq) {
+			// 		res.value = LLVMBuildIsNull(p->builder, the_value, "");
+			// 		return res;
+			// 	} else if (op_kind == Token_NotEq) {
+			// 		res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+			// 		return res;
+			// 	}
+			// } else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
+			// 	LLVMValueRef the_value = {};
+			// 	if (bt->Struct.fields.count == 0) {
+			// 		cgValue cap = cg_soa_struct_cap(p, x);
+			// 		the_value = cap.value;
+			// 	} else {
+			// 		cgValue first_field = cg_emit_struct_ev(p, x, 0);
+			// 		the_value = first_field.value;
+			// 	}
+			// 	if (op_kind == Token_CmpEq) {
+			// 		res.value = LLVMBuildIsNull(p->builder, the_value, "");
+			// 		return res;
+			// 	} else if (op_kind == Token_NotEq) {
+			// 		res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+			// 		return res;
+			// 	}
+			// }
+		} else if (is_type_struct(t) && type_has_nil(t)) {
+			auto args = slice_make<cgValue>(permanent_allocator(), 2);
+			cgValue lhs = cg_address_from_load_or_generate_local(p, x);
+			args[0] = cg_emit_conv(p, lhs, t_rawptr);
+			args[1] = cg_const_int(p, t_int, type_size_of(t));
+			cgValue val = cg_emit_runtime_call(p, "memory_compare_zero", args);
+			cgValue res = cg_emit_comp(p, op_kind, val, cg_const_int(p, t_int, 0));
+			return res;
+		}
 		break;
 	}
 	GB_PANIC("Unknown handled type: %s -> %s", type_to_string(t), type_to_string(bt));
 	return {};
 }
 
+gb_internal void cg_emit_store_union_variant_tag(cgProcedure *p, cgValue parent, Type *variant_type) {
+	Type *t = type_deref(parent.type);
+	GB_ASSERT(is_type_union(t));
+
+	if (is_type_union_maybe_pointer(t) || type_size_of(t) == 0) {
+		// No tag needed!
+	} else {
+		cgValue tag_ptr = cg_emit_union_tag_ptr(p, parent);
+		cg_emit_store(p, tag_ptr, cg_const_union_tag(p, t, variant_type));
+	}
+}
+
+gb_internal void cg_emit_store_union_variant(cgProcedure *p, cgValue parent, cgValue variant, Type *variant_type) {
+	Type *pt = base_type(type_deref(parent.type));
+	GB_ASSERT(pt->kind == Type_Union);
+	if (pt->Union.kind == UnionType_shared_nil) {
+		GB_ASSERT(type_size_of(variant_type));
+
+		TB_Node *if_nil     = cg_control_region(p, "shared_nil.if_nil");
+		TB_Node *if_not_nil = cg_control_region(p, "shared_nil.if_not_nil");
+		TB_Node *done       = cg_control_region(p, "shared_nil.done");
+
+		cgValue cond_is_nil = cg_emit_comp_against_nil(p, Token_CmpEq, variant);
+		cg_emit_if(p, cond_is_nil, if_nil, if_not_nil);
+
+		tb_inst_set_control(p->func, if_nil);
+		cg_emit_store(p, parent, cg_const_nil(p, type_deref(parent.type)));
+		cg_emit_goto(p, done);
+
+		tb_inst_set_control(p->func, if_not_nil);
+		cgValue underlying = cg_emit_conv(p, parent, alloc_type_pointer(variant_type));
+		cg_emit_store(p, underlying, variant);
+		cg_emit_store_union_variant_tag(p, parent, variant_type);
+		cg_emit_goto(p, done);
+
+		tb_inst_set_control(p->func, done);
+	} else {
+		if (type_size_of(variant_type) == 0) {
+			cg_builtin_mem_zero(p, parent, cg_const_int(p, t_uint, pt->Union.variant_block_size));
+		} else {
+			cgValue underlying = cg_emit_conv(p, parent, alloc_type_pointer(variant_type));
+			cg_emit_store(p, underlying, variant);
+		}
+		cg_emit_store_union_variant_tag(p, parent, variant_type);
+	}
+}
+
 gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 	t = reduce_tuple_to_single_type(t);
 
@@ -899,7 +969,11 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 	GB_ASSERT(dst != nullptr);
 
 	if (are_types_identical(src, dst)) {
-		return cg_emit_transmute(p, value, t);
+		if (!are_types_identical(src_type, t)) {
+			return cg_emit_transmute(p, value, t);
+		}
+		value.type = t;
+		return value;
 	}
 
 	TB_DataType st = cg_data_type(src);
@@ -1028,27 +1102,75 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 	}
 
 	if (is_type_complex(src) && is_type_complex(dst)) {
-		GB_PANIC("TODO(bill): complex -> complex");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, false);
+		cgValue ptr = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, cg_emit_struct_ev(p, value, 0), ft);
+		cgValue imag = cg_emit_conv(p, cg_emit_struct_ev(p, value, 1), ft);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 0), real);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 1), imag);
+		return cg_addr_load(p, gen);
 	}
 
 	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
 		// @QuaternionLayout
-		GB_PANIC("TODO(bill): quaternion -> quaternion");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, false);
+		cgValue ptr = cg_addr_get_ptr(p, gen);
+		cgValue q0 = cg_emit_conv(p, cg_emit_struct_ev(p, value, 0), ft);
+		cgValue q1 = cg_emit_conv(p, cg_emit_struct_ev(p, value, 1), ft);
+		cgValue q2 = cg_emit_conv(p, cg_emit_struct_ev(p, value, 2), ft);
+		cgValue q3 = cg_emit_conv(p, cg_emit_struct_ev(p, value, 3), ft);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 0), q0);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 1), q1);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 2), q2);
+		cg_emit_store(p, cg_emit_struct_ep(p, ptr, 3), q3);
+		return cg_addr_load(p, gen);
 	}
 	if (is_type_integer(src) && is_type_complex(dst)) {
-		GB_PANIC("TODO(bill): int -> complex");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, true);
+		cgValue gp = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, value, ft);
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 0), real);
+		return cg_addr_load(p, gen);
 	}
 	if (is_type_float(src) && is_type_complex(dst)) {
-		GB_PANIC("TODO(bill): float -> complex");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, true);
+		cgValue gp = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, value, ft);
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 0), real);
+		return cg_addr_load(p, gen);
 	}
 	if (is_type_integer(src) && is_type_quaternion(dst)) {
-		GB_PANIC("TODO(bill): int -> quaternion");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, true);
+		cgValue gp = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, value, ft);
+		// @QuaternionLayout
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 3), real);
+		return cg_addr_load(p, gen);
 	}
 	if (is_type_float(src) && is_type_quaternion(dst)) {
-		GB_PANIC("TODO(bill): float -> quaternion");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, true);
+		cgValue gp = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, value, ft);
+		// @QuaternionLayout
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 3), real);
+		return cg_addr_load(p, gen);
 	}
 	if (is_type_complex(src) && is_type_quaternion(dst)) {
-		GB_PANIC("TODO(bill): complex -> quaternion");
+		Type *ft = base_complex_elem_type(dst);
+		cgAddr gen = cg_add_local(p, t, nullptr, true);
+		cgValue gp = cg_addr_get_ptr(p, gen);
+		cgValue real = cg_emit_conv(p, cg_emit_struct_ev(p, value, 0), ft);
+		cgValue imag = cg_emit_conv(p, cg_emit_struct_ev(p, value, 1), ft);
+		// @QuaternionLayout
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 3), real);
+		cg_emit_store(p, cg_emit_struct_ep(p, gp, 0), imag);
+		return cg_addr_load(p, gen);
 	}
 
 
@@ -1063,18 +1185,18 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 			return cg_emit_conv(p, res, t);
 		}
 
-		// if (is_type_integer_128bit(dst)) {
-		// 	TEMPORARY_ALLOCATOR_GUARD();
+		if (is_type_integer_128bit(dst)) {
+			TEMPORARY_ALLOCATOR_GUARD();
 
-		// 	auto args = array_make<lbValue>(temporary_allocator(), 1);
-		// 	args[0] = value;
-		// 	char const *call = "fixunsdfdi";
-		// 	if (is_type_unsigned(dst)) {
-		// 		call = "fixunsdfti";
-		// 	}
-		// 	lbValue res_i128 = lb_emit_runtime_call(p, call, args);
-		// 	return lb_emit_conv(p, res_i128, t);
-		// }
+			auto args = slice_make<cgValue>(temporary_allocator(), 1);
+			args[0] = value;
+			char const *call = "fixunsdfdi";
+			if (is_type_unsigned(dst)) {
+				call = "fixunsdfti";
+			}
+			cgValue res_i128 = cg_emit_runtime_call(p, call, args);
+			return cg_emit_conv(p, res_i128, t);
+		}
 
 		bool is_signed = !is_type_unsigned(dst);
 		return cg_value(tb_inst_float2int(p->func, value.node, dt, is_signed), t);
@@ -1092,18 +1214,18 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 			return cg_emit_conv(p, res, t);
 		}
 
-		// if (is_type_integer_128bit(src)) {
-		// 	TEMPORARY_ALLOCATOR_GUARD();
+		if (is_type_integer_128bit(src)) {
+			TEMPORARY_ALLOCATOR_GUARD();
 
-		// 	auto args = array_make<lbValue>(temporary_allocator(), 1);
-		// 	args[0] = value;
-		// 	char const *call = "floattidf";
-		// 	if (is_type_unsigned(src)) {
-		// 		call = "floattidf_unsigned";
-		// 	}
-		// 	lbValue res_f64 = lb_emit_runtime_call(p, call, args);
-		// 	return lb_emit_conv(p, res_f64, t);
-		// }
+			auto args = slice_make<cgValue>(temporary_allocator(), 1);
+			args[0] = value;
+			char const *call = "floattidf";
+			if (is_type_unsigned(src)) {
+				call = "floattidf_unsigned";
+			}
+			cgValue res_f64 = cg_emit_runtime_call(p, call, args);
+			return cg_emit_conv(p, res_f64, t);
+		}
 
 		bool is_signed = !is_type_unsigned(dst);
 		return cg_value(tb_inst_int2float(p->func, value.node, dt, is_signed), t);
@@ -1113,6 +1235,22 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 		GB_PANIC("TODO(bill): ? -> #simd vector");
 	}
 
+	// bit_field <-> backing type
+	if (is_type_bit_field(src)) {
+		if (are_types_identical(src->BitField.backing_type, dst)) {
+			cgValue res = value;
+			res.type = t;
+			return res;
+		}
+	}
+	if (is_type_bit_field(dst)) {
+		if (are_types_identical(src, dst->BitField.backing_type)) {
+			cgValue res = value;
+			res.type = t;
+			return res;
+		}
+	}
+
 
 	// Pointer <-> uintptr
 	if (is_type_pointer(src) && is_type_uintptr(dst)) {
@@ -1129,13 +1267,66 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 	}
 
 	if (is_type_union(dst)) {
-		GB_PANIC("TODO(bill): ? -> union");
+		for (Type *vt : dst->Union.variants) {
+			if (are_types_identical(vt, src_type)) {
+				cgAddr parent = cg_add_local(p, t, nullptr, true);
+				cg_emit_store_union_variant(p, parent.addr, value, vt);
+				return cg_addr_load(p, parent);
+			}
+		}
+		if (dst->Union.variants.count == 1) {
+			Type *vt = dst->Union.variants[0];
+			if (internal_check_is_assignable_to(src_type, vt)) {
+				value = cg_emit_conv(p, value, vt);
+				cgAddr parent = cg_add_local(p, t, nullptr, true);
+				cg_emit_store_union_variant(p, parent.addr, value, vt);
+				return cg_addr_load(p, parent);
+			}
+		}
 	}
 
 	// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
 	// subtype polymorphism casting
 	if (check_is_assignable_to_using_subtype(src_type, t)) {
-		GB_PANIC("TODO(bill): ? -> subtyping");
+		Type *st = type_deref(src_type);
+		st = type_deref(st);
+
+		bool st_is_ptr = is_type_pointer(src_type);
+		st = base_type(st);
+
+		Type *dt = t;
+
+		GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
+		Selection sel = {};
+		sel.index.allocator = heap_allocator();
+		defer (array_free(&sel.index));
+		if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
+			if (sel.entity == nullptr) {
+				GB_PANIC("invalid subtype cast  %s -> ", type_to_string(src_type), type_to_string(t));
+			}
+			if (st_is_ptr) {
+				cgValue res = cg_emit_deep_field_gep(p, value, sel);
+				Type *rt = res.type;
+				if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+					res = cg_emit_load(p, res);
+				}
+				return res;
+			} else {
+				if (is_type_pointer(value.type)) {
+					Type *rt = value.type;
+					if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+						value = cg_emit_load(p, value);
+					} else {
+						value = cg_emit_deep_field_gep(p, value, sel);
+						return cg_emit_load(p, value);
+					}
+				}
+
+				cgValue ptr = cg_address_from_load_or_generate_local(p, value);
+				cgValue res = cg_emit_deep_field_gep(p, ptr, sel);
+				return cg_emit_load(p, res);
+			}
+		}
 	}
 
 	// Pointer <-> Pointer
@@ -1174,6 +1365,10 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 		return cg_emit_transmute(p, value, t);
 	}
 
+	if (is_type_array_like(dst)) {
+		GB_PANIC("TODO(bill): ??? -> array-like");
+	}
+
 	if (is_type_matrix(dst) && !is_type_matrix(src)) {
 		GB_PANIC("TODO(bill): !matrix -> matrix");
 	}
@@ -1197,9 +1392,6 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t) {
 		GB_ASSERT(is_type_typed(st));
 
 		data = cg_emit_conv(p, data, t_rawptr);
-		if (p->name == "main@main") {
-			GB_PANIC("HERE %s %llu", type_to_string(st), cg_typeid_as_u64(p->module, value.type));
-		}
 
 		cgValue id = cg_typeid(p, st);
 		cgValue data_ptr = cg_emit_struct_ep(p, result.addr, 0);
@@ -1781,11 +1973,6 @@ gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x,
 	return res;
 }
 
-gb_internal void cg_emit_if(cgProcedure *p, cgValue const &cond, TB_Node *true_region, TB_Node *false_region) {
-	GB_ASSERT(cond.kind == cgValue_Value);
-	tb_inst_if(p->func, cond.node, true_region, false_region);
-}
-
 
 struct cgLoopData {
 	cgAddr  index_addr;

+ 2 - 0
src/tilde_proc.cpp

@@ -392,9 +392,11 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
 		// feature_set.x64 |= TB_FEATURE_X64_LZCNT;
 	}
 
+#if 0
 	bool emit_asm = false;
 	TB_FunctionOutput *output = tb_codegen(p->func, cg_worklist(), cg_arena(), &feature_set, emit_asm);
 	gb_unused(output);
+#endif
 
 	// tb_print(p->func, cg_arena());
 	// fprintf(stdout, "\n");