diff --git a/core/_preload.odin b/core/_preload.odin index cd6cb3b55..bc202df33 100644 --- a/core/_preload.odin +++ b/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, diff --git a/core/fmt.odin b/core/fmt.odin index d99aa2031..752d74a13 100644 --- a/core/fmt.odin +++ b/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); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7b1b3f271..43a6098b8 100644 --- a/src/check_expr.cpp +++ b/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 "); diff --git a/src/common.cpp b/src/common.cpp index ef2b5f68d..1379db50d 100644 --- a/src/common.cpp +++ b/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; diff --git a/src/ir.cpp b/src/ir.cpp index 455dae9d2..43c1cca31 100644 --- a/src/ir.cpp +++ b/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)); } } } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a1b2a1d03..a8f053bbd 100644 --- a/src/ir_print.cpp +++ b/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; diff --git a/src/types.cpp b/src/types.cpp index 85af08a9b..bb9e95667 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -118,7 +118,6 @@ struct TypeStruct { Array 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);