Browse Source

Union tag stored as an integer

Ginger Bill 7 years ago
parent
commit
6424966b7a
7 changed files with 151 additions and 124 deletions
  1. 1 0
      core/_preload.odin
  2. 20 5
      core/fmt.odin
  3. 0 6
      src/check_expr.cpp
  4. 62 0
      src/common.cpp
  5. 27 97
      src/ir.cpp
  6. 2 1
      src/ir_print.cpp
  7. 39 15
      src/types.cpp

+ 1 - 0
core/_preload.odin

@@ -86,6 +86,7 @@ Type_Info_Struct :: struct #ordered {
 Type_Info_Union :: struct #ordered {
 	variants:   []^Type_Info,
 	tag_offset: int,
+	tag_type:   ^Type_Info,
 };
 Type_Info_Enum :: struct #ordered {
 	base:   ^Type_Info,

+ 20 - 5
core/fmt.odin

@@ -920,11 +920,28 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 
 	case Type_Info_Union:
 		data := cast(^u8)v.data;
-		tipp := cast(^^Type_Info)(data + info.tag_offset);
-		if data == nil || tipp == nil {
+		tag_ptr := rawptr(data + info.tag_offset);
+		tag_any := any{tag_ptr, info.tag_type};
+
+		tag: i64 = -1;
+		switch i in tag_any {
+		case u8:   tag = i64(i);
+		case i8:   tag = i64(i);
+		case u16:  tag = i64(i);
+		case i16:  tag = i64(i);
+		case u32:  tag = i64(i);
+		case i32:  tag = i64(i);
+		case u64:  tag = i64(i);
+		case i64:  tag = i64(i);
+		case u128: tag = i64(i);
+		case i128: tag = i64(i);
+		case: panic("Invalid union tag type");
+		}
+
+		if data == nil || tag < 0 {
 			write_string(fi.buf, "(union)");
 		} else {
-			ti := tipp^;
+			ti := info.variants[tag-1];
 			fmt_arg(fi, any{data, ti}, verb);
 		}
 
@@ -1007,10 +1024,8 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
 	case u64:     fmt_int(fi, u128(a), false, 64, verb);
 	case u128:    fmt_int(fi, u128(a), false, 128, verb);
 
-
 	case string:  fmt_string(fi, a, verb);
 
-
 	case:         fmt_value(fi, arg, verb);
 	}
 

+ 0 - 6
src/check_expr.cpp

@@ -6472,12 +6472,6 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = gb_string_append_rune(str, '}');
 	case_end;
 
-	// case_ast_node(st, RawUnionType, node);
-	// 	str = gb_string_appendc(str, "raw_union ");
-	// 	str = gb_string_append_rune(str, '{');
-	// 	str = write_struct_fields_to_string(str, st->fields);
-	// 	str = gb_string_append_rune(str, '}');
-	// case_end;
 
 	case_ast_node(st, UnionType, node);
 		str = gb_string_appendc(str, "union ");

+ 62 - 0
src/common.cpp

@@ -304,6 +304,68 @@ i64 next_pow2(i64 n) {
 	return n;
 }
 
+i32 bit_set_count(u32 x) {
+	x -= ((x >> 1) & 0x55555555);
+	x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
+	x = (((x >> 4) + x) & 0x0f0f0f0f);
+	x += (x >> 8);
+	x += (x >> 16);
+
+	return cast(i32)(x & 0x0000003f);
+}
+
+i64 bit_set_count(u64 x) {
+	u32 a = *(cast(u32 *)&x);
+	u32 b = *(cast(u32 *)&x + 1);
+	return bit_set_count(a) + bit_set_count(b);
+}
+
+u32 floor_log2(u32 x) {
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+	return cast(u32)(bit_set_count(x) - 1);
+}
+
+u64 floor_log2(u64 x) {
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+	x |= x >> 32;
+	return cast(u64)(bit_set_count(x) - 1);
+}
+
+
+u32 ceil_log2(u32 x) {
+	i32 y = cast(i32)(x & (x-1));
+	y |= -y;
+	y >>= 32-1;
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+	return cast(u32)(bit_set_count(x) - 1 - y);
+}
+
+u64 ceil_log2(u64 x) {
+	i64 y = cast(i64)(x & (x-1));
+	y |= -y;
+	y >>= 64-1;
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+	x |= x >> 32;
+	return cast(u64)(bit_set_count(x) - 1 - y);
+}
+
+
 i32 prev_pow2(i32 n) {
 	if (n <= 0) {
 		return 0;

+ 27 - 97
src/ir.cpp

@@ -966,7 +966,9 @@ irValue *ir_instr_union_tag_ptr(irProcedure *p, irValue *address) {
 	irValue *v = ir_alloc_instr(p, irInstr_UnionTagPtr);
 	irInstr *i = &v->Instr;
 	i->UnionTagPtr.address = address;
-	i->UnionTagPtr.type = make_type_pointer(p->module->allocator, t_type_info_ptr);
+	// i->UnionTagPtr.type = make_type_pointer(p->module->allocator, t_type_info_ptr);
+	Type *u = type_deref(ir_type(address));
+	i->UnionTagPtr.type = make_type_pointer(p->module->allocator, union_tag_type(u));
 	return v;
 }
 
@@ -974,7 +976,10 @@ irValue *ir_instr_union_tag_value(irProcedure *p, irValue *address) {
 	irValue *v = ir_alloc_instr(p, irInstr_UnionTagValue);
 	irInstr *i = &v->Instr;
 	i->UnionTagValue.address = address;
-	i->UnionTagValue.type = t_type_info_ptr;
+	// i->UnionTagValue.type = t_type_info_ptr;
+	// i->UnionTagValue.type = t_int;
+	Type *u = type_deref(ir_type(address));
+	i->UnionTagPtr.type = union_tag_type(u);
 	return v;
 }
 
@@ -2236,10 +2241,6 @@ irValue *ir_emit_union_tag_ptr(irProcedure *proc, irValue *u) {
 	GB_ASSERT_MSG(is_type_pointer(t) &&
 	              is_type_union(type_deref(t)), "%s", type_to_string(t));
 	irValue *tag_ptr = ir_emit(proc, ir_instr_union_tag_ptr(proc, u));
-	Type *tpt = ir_type(tag_ptr);
-	GB_ASSERT(is_type_pointer(tpt));
-	tpt = base_type(type_deref(tpt));
-	GB_ASSERT(tpt == t_type_info_ptr);
 	return tag_ptr;
 }
 
@@ -2784,6 +2785,10 @@ irValue *ir_find_or_add_entity_string(irModule *m, String str) {
 }
 
 
+irValue *ir_const_union_tag(gbAllocator a, Type *u, Type *v) {
+	return ir_value_constant(a, union_tag_type(u), exact_value_i64(union_variant_index(u, v)));
+}
+
 
 String ir_lookup_subtype_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	Type *prev_src = src;
@@ -3011,7 +3016,7 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 				ir_emit_store(proc, underlying, value);
 
 				irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
-				ir_emit_store(proc, tag_ptr, ir_type_info(proc, vt));
+				ir_emit_store(proc, tag_ptr, ir_const_union_tag(a, t, src_type));
 
 				return ir_emit_load(proc, parent);
 			}
@@ -3233,6 +3238,7 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
 }
 
 
+
 irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, TokenPos pos) {
 	gbAllocator a = proc->module->allocator;
 
@@ -3248,77 +3254,6 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 
 	irValue *v = ir_add_local_generated(proc, tuple);
 
-	#if 0
-	if (is_ptr) {
-		Type *src = base_type(type_deref(src_type));
-		Type *src_ptr = src_type;
-		GB_ASSERT(is_type_union(src));
-		Type *dst_ptr = tuple->Tuple.variables[0]->type;
-		Type *dst = type_deref(dst_ptr);
-
-		irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value));
-		irValue *dst_tag = nullptr;
-		for (isize i = 1; i < src->Struct.variant_count; i++) {
-			Type *vt = src->Struct.variants[i];
-			if (are_types_identical(vt, dst)) {
-				dst_tag = ir_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != nullptr);
-
-		irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
-		irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
-		irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ir_emit_if(proc, cond, ok_block, end_block);
-		ir_start_block(proc, ok_block);
-
-		irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
-		irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
-
-		irValue *data = ir_emit_conv(proc, value, dst_ptr);
-		ir_emit_store(proc, gep0, data);
-		ir_emit_store(proc, gep1, v_true);
-
-		ir_emit_jump(proc, end_block);
-		ir_start_block(proc, end_block);
-
-	} else {
-		Type *src = base_type(src_type);
-		GB_ASSERT(is_type_union(src));
-		Type *dst = tuple->Tuple.variables[0]->type;
-		Type *dst_ptr = make_type_pointer(a, dst);
-
-		irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
-
-		irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
-		irValue *dst_tag = nullptr;
-		for (isize i = 1; i < src->Struct.variant_count; i++) {
-			Type *vt = src->Struct.variants[i];
-			if (are_types_identical(vt, dst)) {
-				dst_tag = ir_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != nullptr);
-
-		irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
-		irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
-		irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ir_emit_if(proc, cond, ok_block, end_block);
-		ir_start_block(proc, ok_block);
-
-		irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
-		irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
-
-		irValue *data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
-		ir_emit_store(proc, gep0, data);
-		ir_emit_store(proc, gep1, v_true);
-
-		ir_emit_jump(proc, end_block);
-		ir_start_block(proc, end_block);
-	}
-	#else
 	if (is_ptr) {
 		value = ir_emit_load(proc, value);
 	}
@@ -3326,10 +3261,11 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 	GB_ASSERT(is_type_union(src));
 	Type *dst = tuple->Tuple.variables[0]->type;
 
-	irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
 
-	irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
-	irValue *dst_tag = ir_type_info(proc, dst);
+	irValue *value_  = ir_address_from_load_or_generate_local(proc, value);
+	irValue *tag     = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
+	irValue *dst_tag = ir_const_union_tag(a, src, dst);
+
 
 	irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
 	irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
@@ -3346,7 +3282,7 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
 
 	ir_emit_jump(proc, end_block);
 	ir_start_block(proc, end_block);
-	#endif
+
 	if (!is_tuple) {
 		// NOTE(bill): Panic on invalid conversion
 		Type *dst_type = tuple->Tuple.variables[0]->type;
@@ -7073,18 +7009,8 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 				case_type = type_of_expr(proc->module->info, cc->list[type_index]);
 				irValue *cond = nullptr;
 				if (match_type_kind == MatchType_Union) {
-					Type *bt = type_deref(case_type);
-					irValue *variant_tag = nullptr;
 					Type *ut = base_type(type_deref(parent_type));
-					GB_ASSERT(ut->kind == Type_Union);
-					for_array(variant_index, ut->Union.variants) {
-						Type *vt = ut->Union.variants[variant_index];
-						if (are_types_identical(vt, bt)) {
-							variant_tag = ir_type_info(proc, vt);
-							break;
-						}
-					}
-					GB_ASSERT(variant_tag != nullptr);
+					irValue *variant_tag = ir_const_union_tag(proc->module->allocator, ut, case_type);
 					cond = ir_emit_comp(proc, Token_CmpEq, tag_index, variant_tag);
 				} else if (match_type_kind == MatchType_Any) {
 					irValue *any_ti  = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 1));
@@ -8414,6 +8340,7 @@ void ir_gen_tree(irGen *s) {
 					{
 						irValue *variant_types  = ir_emit_struct_ep(proc, tag, 0);
 						irValue *tag_offset_ptr = ir_emit_struct_ep(proc, tag, 1);
+						irValue *tag_type_ptr   = ir_emit_struct_ep(proc, tag, 2);
 
 						isize variant_count = gb_max(0, t->Union.variants.count);
 						irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count);
@@ -8431,8 +8358,10 @@ void ir_gen_tree(irGen *s) {
 						irValue *count = ir_const_int(a, variant_count);
 						ir_fill_slice(proc, variant_types, memory_types, count, count);
 
-						i64 tag_offset = align_formula(t->Union.variant_block_size, build_context.word_size);
+						i64 tag_size  = union_tag_size(t);
+						i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
 						ir_emit_store(proc, tag_offset_ptr, ir_const_int(a, tag_offset));
+						ir_emit_store(proc, tag_type_ptr,   ir_type_info(proc, union_tag_type(t)));
 					}
 
 					break;
@@ -8556,11 +8485,12 @@ void ir_gen_tree(irGen *s) {
 				if (tag != nullptr) {
 					Type *tag_type = type_deref(ir_type(tag));
 					GB_ASSERT(is_type_named(tag_type));
-					irValue *ti = ir_type_info(proc, tag_type);
+					Type *variant_type = type_deref(ir_type(variant_ptr));
+					irValue *tag = ir_const_union_tag(a, variant_type, tag_type);
 					irValue *ptr = ir_emit_union_tag_ptr(proc, variant_ptr);
-					ir_emit_store(proc, ptr, ti);
+					ir_emit_store(proc, ptr, tag);
 				} else {
-					GB_PANIC("Unhandled TypeInfo type: %s", type_to_string(t));
+					GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
 				}
 			}
 		}

+ 2 - 1
src/ir_print.cpp

@@ -305,7 +305,8 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 
 			ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
 			ir_fprintf(f, "[%lld x i8], ", block_size);
-			ir_print_type(f, m, t_type_info_ptr);
+			// ir_print_type(f, m, t_type_info_ptr);
+			ir_print_type(f, m, union_tag_type(t));
 			ir_write_byte(f, '}');
 		}
 	} return;

+ 39 - 15
src/types.cpp

@@ -118,7 +118,6 @@ struct TypeStruct {
 		Array<Type *> variants;                           \
 		AstNode *node;                                    \
 		Scope *  scope;                                   \
-		Entity * union__type_info;                        \
 		i64      variant_block_size;                      \
 		i64      custom_align;                            \
 	})                                                    \
@@ -1335,6 +1334,42 @@ bool is_type_cte_safe(Type *type) {
 	return false;
 }
 
+i64 union_variant_index(Type *u, Type *v) {
+	u = base_type(u);
+	GB_ASSERT(u->kind == Type_Union);
+
+	for_array(i, u->Union.variants) {
+		Type *vt = u->Union.variants[i];
+		if (are_types_identical(v, vt)) {
+			return cast(i64)(i+1);
+		}
+	}
+	return 0;
+}
+
+i64 union_tag_size(Type *u) {
+	u = base_type(u);
+	GB_ASSERT(u->kind == Type_Union);
+	u64 cl2 = ceil_log2(cast(u64)u->Union.variants.count);
+	i64 s = (next_pow2(cast(i64)cl2) + 7)/8;
+	return gb_clamp(s, 1, build_context.word_size);
+}
+
+Type *union_tag_type(Type *u) {
+	i64 s = union_tag_size(u);
+	switch (s) {
+	case  1: return  t_u8;
+	case  2: return  t_u16;
+	case  4: return  t_u32;
+	case  8: return  t_u64;
+	case 16: return t_u128;
+	}
+	GB_PANIC("Invalid union_tag_size");
+	return t_int;
+}
+
+
+
 enum ProcTypeOverloadKind {
 	ProcOverload_Identical, // The types are identical
 
@@ -1614,20 +1649,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 		}
 
 	} else if (type->kind == Type_Union) {
-		if (field_name == "__type_info") {
-			Entity *e = type->Union.union__type_info;
-			if (e == nullptr) {
-				Entity *__type_info = make_entity_field(a, nullptr, make_token_ident(str_lit("__type_info")), t_type_info_ptr, false, -1);
-				type->Union.union__type_info = __type_info;
-				e = __type_info;
-			}
 
-			GB_ASSERT(e != nullptr);
-			selection_add_index(&sel, -1); // HACK(bill): Leaky memory
-			sel.entity = e;
-
-			return sel;
-		}
 	} else if (type->kind == Type_Struct) {
 		for_array(i, type->Struct.fields) {
 			Entity *f = type->Struct.fields[i];
@@ -1854,7 +1876,9 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 		if (t->Union.custom_align > 0) {
 			return gb_clamp(t->Union.custom_align, 1, build_context.max_align);
 		}
-		i64 max = build_context.word_size;
+
+
+		i64 max = union_tag_size(t);
 		for_array(i, t->Union.variants) {
 			Type *variant = t->Union.variants[i];
 			type_path_push(path, variant);