From acc010cba5df63b38704245254e721187335a7b2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 Aug 2018 17:07:56 +0100 Subject: [PATCH] Add `bit_set` type --- core/fmt/fmt.odin | 88 +++++++++++++++++++- core/runtime/core.odin | 5 +- core/runtime/procs_windows_amd64.odin | 50 +++++++++++ core/strconv/strconv.odin | 10 +-- examples/demo/demo.odin | 20 +++++ src/check_expr.cpp | 114 +++++++++++++++++++------- src/check_type.cpp | 71 +++++++++++++++- src/checker.cpp | 6 ++ src/ir.cpp | 52 ++++++++++-- src/ir_print.cpp | 45 ++++++++++ src/parser.cpp | 21 +++++ src/parser.hpp | 4 + src/tokenizer.cpp | 1 + src/types.cpp | 67 +++++++++++++++ 14 files changed, 509 insertions(+), 45 deletions(-) create mode 100644 core/runtime/procs_windows_amd64.odin diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index fb5bb2f70..316761010 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -70,7 +70,7 @@ write_rune :: proc(buf: ^String_Buffer, r: rune) { write_i64 :: proc(buf: ^String_Buffer, i: i64, base: int) { b: [129]byte; - s := strconv.append_bits(b[:], u64(i), base, true, 64, strconv.digits, 0); + s := strconv.append_bits(b[:], u64(i), base, true, 64, strconv.digits, nil); write_string(buf, s); } @@ -312,6 +312,11 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { write_i64(buf, i64(info.bits[i]), 10); } write_string(buf, "}"); + + case runtime.Type_Info_Bit_Set: + write_string(buf, "bit_set["); + write_type(buf, info.base); + write_string(buf, "]"); } } @@ -714,6 +719,82 @@ fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) { } + +enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 { + switch i in ev { + case rune: return u64(i); + case i8: return u64(i); + case i16: return u64(i); + case i32: return u64(i); + case i64: return u64(i); + case int: return u64(i); + case u8: return u64(i); + case u16: return u64(i); + case u32: return u64(i); + case u64: return u64(i); + case uint: return u64(i); + case uintptr: return u64(i); + case f32: return u64(i); + case f64: return u64(i); + } + return 0; +} + +fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") { + type_info := type_info_of(v.typeid); + switch info in type_info.variant { + case runtime.Type_Info_Named: + val := v; + val.typeid = info.base.id; + fmt_bit_set(fi, val, info.name); + + case runtime.Type_Info_Bit_Set: + bits: u64; + bit_size := u64(8*type_info.size); + verb := 'b'; + + switch bit_size { + case 0: bits = 0; + case 8: bits = u64( (^u8)(v.data)^); + case 16: bits = u64((^u16)(v.data)^); + case 32: bits = u64((^u32)(v.data)^); + case 64: bits = u64((^u64)(v.data)^); + case: panic("unknown bit_size size"); + } + + et := runtime.type_info_base(info.base); + e := et.variant.(runtime.Type_Info_Enum); + + if name != "" { + write_string(fi.buf, name); + } else { + write_type(fi.buf, type_info); + } + write_byte(fi.buf, '{'); + defer write_byte(fi.buf, '}'); + + commas := 0; + loop: for i in 0 .. bit_size-1 { + if bits & (1< 0 do write_string(fi.buf, ", "); + + defer commas += 1; + + for ev, evi in e.values { + v := enum_value_to_u64(ev); + if v == i { + write_string(fi.buf, e.names[evi]); + continue loop; + } + } + write_i64(fi.buf, i64(i), 10); + } + } +} + fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { if v.data == nil || v.typeid == nil { write_string(fi.buf, ""); @@ -765,6 +846,8 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { if hash do for in 0..indent-1 do write_byte(fi.buf, '\t'); write_byte(fi.buf, '}'); + case runtime.Type_Info_Bit_Set: + fmt_bit_set(fi, v); case: fmt_value(fi, any{v.data, typeid_of(info.base)}, verb); } @@ -929,6 +1012,9 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^; write_typeid(fi.buf, id); + + case runtime.Type_Info_Bit_Set: + fmt_bit_set(fi, v); } } diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 3d19fe0eb..0ecf297e9 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -99,7 +99,9 @@ Type_Info_Bit_Field :: struct { bits: []i32, offsets: []i32, }; - +Type_Info_Bit_Set :: struct { + base: ^Type_Info, +}; Type_Info :: struct { size: int, @@ -127,6 +129,7 @@ Type_Info :: struct { Type_Info_Enum, Type_Info_Map, Type_Info_Bit_Field, + Type_Info_Bit_Set, }, } diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin new file mode 100644 index 000000000..6151e75d8 --- /dev/null +++ b/core/runtime/procs_windows_amd64.odin @@ -0,0 +1,50 @@ +package runtime + +foreign import kernel32 "system:Kernel32.lib" + +@(link_name="memcpy") +memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr { + foreign kernel32 { + RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) --- + } + RtlCopyMemory(dst, src, len); + return dst; +} + +@(link_name="memmove") +memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr { + foreign kernel32 { + RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) --- + } + RtlMoveMemory(dst, src, len); + return dst; +} + +@(link_name="memset") +memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { + foreign kernel32 { + RtlFillMemory :: proc "c" (dst: rawptr, len: int, fill: byte) --- + } + RtlFillMemory(ptr, len, byte(val)); + return ptr; +} + +// @(link_name="memcmp") +// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 { +// if dst == nil || src == nil { +// return 0; +// } +// if dst == src { +// return 0; +// } +// d, s := uintptr(dst), uintptr(src); +// n := uintptr(len); + +// for i := uintptr(0); i < n; i += 1 { +// x, y := (^byte)(d+i)^, (^byte)(s+i)^; +// if x != y { +// return x < y ? -1 : +1; +// } +// } +// return 0; +// } diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 6e64d3e71..b5e57613c 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -198,10 +198,10 @@ append_bool :: proc(buf: []byte, b: bool) -> string { } append_uint :: proc(buf: []byte, u: u64, base: int) -> string { - return append_bits(buf, u64(u), base, false, 8*size_of(uint), digits, 0); + return append_bits(buf, u64(u), base, false, 8*size_of(uint), digits, nil); } append_int :: proc(buf: []byte, i: i64, base: int) -> string { - return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, 0); + return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil); } itoa :: proc(buf: []byte, i: int) -> string do return append_int(buf, i64(i), 10); @@ -474,7 +474,7 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i } i-=1; a[i] = digits[u % b]; - if flags&Int_Flag.Prefix != 0 { + if flags&Int_Flag.Prefix != nil { ok := true; switch base { case 2: i-=1; a[i] = 'b'; @@ -492,9 +492,9 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i switch { case neg: i-=1; a[i] = '-'; - case flags&Int_Flag.Plus != 0: + case flags&Int_Flag.Plus != nil: i-=1; a[i] = '+'; - case flags&Int_Flag.Space != 0: + case flags&Int_Flag.Space != nil: i-=1; a[i] = ' '; } diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 4860d9414..5ae59848d 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -712,6 +712,25 @@ deprecated_attribute :: proc() { // foo_v1(1); } +bit_set_type :: proc() { + using Day :: enum { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + } + + Days :: distinct bit_set[Day]; + d: Days; + d = Days{Sunday}; + x := Tuesday; + d |= Days{Saturday, x}; + fmt.println(d); +} + main :: proc() { when true { general_stuff(); @@ -725,5 +744,6 @@ main :: proc() { complete_switch(); cstring_example(); deprecated_attribute(); + bit_set_type(); } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d6ef8027f..d12f162ed 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -841,6 +841,9 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, case Type_Enum: return false; + case Type_BitSet: + return false; + case Type_Union: if (source->kind == Type_Union) { TypeUnion *x = &poly->Union; @@ -1115,8 +1118,8 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { break; case Token_Xor: - if (!is_type_integer(type) && !is_type_boolean(type)) { - error(op, "Operator '%.*s' is only allowed with integers or booleans", LIT(op.string)); + if (!is_type_integer(type) && !is_type_boolean(type) && !is_type_bit_set(type)) { + error(op, "Operator '%.*s' is only allowed with integers, booleans, or bit sets", LIT(op.string)); } break; @@ -1142,33 +1145,17 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { switch (op.kind) { case Token_Sub: case Token_SubEq: - if (!is_type_numeric(type) && !is_type_pointer(type)) { - error(op, "Operator '%.*s' is only allowed with numeric or pointer expressions", LIT(op.string)); - return false; - } -#if defined(NO_POINTER_ARITHMETIC) - if (is_type_pointer(type)) { - error(o->expr, "Pointer arithmetic is not supported"); - return false; - } -#else - if (is_type_pointer(type)) { - o->type = t_int; - } - if (base_type(type) == t_rawptr) { - gbString str = type_to_string(type); - error(o->expr, "Invalid pointer type for pointer arithmetic: '%s'", str); - gb_string_free(str); + if (!is_type_numeric(type)) { + error(op, "Operator '%.*s' is only allowed with numeric expressions", LIT(op.string)); return false; } break; -#endif case Token_Mul: case Token_Quo: - case Token_AddEq: case Token_MulEq: case Token_QuoEq: + case Token_AddEq: if (!is_type_numeric(type)) { error(op, "Operator '%.*s' is only allowed with numeric expressions", LIT(op.string)); return false; @@ -1194,24 +1181,30 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { case Token_OrEq: case Token_Xor: case Token_XorEq: - if (!is_type_integer(type) && !is_type_boolean(type)) { - error(op, "Operator '%.*s' is only allowed with integers or booleans", LIT(op.string)); + if (!is_type_integer(type) && !is_type_boolean(type) && !is_type_bit_set(type)) { + error(op, "Operator '%.*s' is only allowed with integers, booleans, or bit sets", LIT(op.string)); return false; } break; case Token_Mod: case Token_ModMod: - case Token_AndNot: case Token_ModEq: case Token_ModModEq: - case Token_AndNotEq: if (!is_type_integer(type)) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; } break; + case Token_AndNot: + case Token_AndNotEq: + if (!is_type_integer(type) && !is_type_bit_set(type)) { + error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string)); + return false; + } + break; + case Token_CmpAnd: case Token_CmpOr: case Token_CmpAndEq: @@ -2329,7 +2322,11 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { return; } - Type *t = core_type(target_type); + Type *t = base_type(target_type); + if (c->in_enum_type) { + t = core_type(target_type); + } + switch (t->kind) { case Type_Basic: if (operand->mode == Addressing_Constant) { @@ -4264,6 +4261,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } else { pt = type_to_string(t); } + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } String name = proc->token.string; char const *sep = "::"; @@ -4271,7 +4274,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type sep = ":="; } // gb_printf_err("\t%.*s %s %s at %.*s(%td:%td) with score %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score); - gb_printf_err("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + gb_printf_err("\t%.*s%.*s%.*s %s %s at %.*s(%td:%td)\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); } if (procs.count > 0) { gb_printf_err("\n"); @@ -5096,9 +5099,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type switch (t->kind) { case Type_Struct: { - if (is_type_union(t)) { - is_constant = false; - } if (cl->elems.count == 0) { break; // NOTE(bill): No need to init } @@ -5111,7 +5111,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - { // Checker values + if (is_type_struct(t)) { // Checker values isize field_count = t->Struct.fields.count; isize min_field_count = t->Struct.fields.count; for (isize i = min_field_count-1; i >= 0; i--) { @@ -5220,6 +5220,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } } } + break; } @@ -5409,6 +5410,39 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } + case Type_BitSet: { + if (cl->elems.count == 0) { + break; // NOTE(bill): No need to init + } + Type *et = base_type(t->BitSet.base_type); + isize field_count = 0; + if (et->kind == Type_Enum) { + field_count = et->Enum.fields.count; + } + + if (cl->elems[0]->kind == Ast_FieldValue) { + error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); + } else { + for_array(index, cl->elems) { + Entity *field = nullptr; + Ast *elem = cl->elems[index]; + if (elem->kind == Ast_FieldValue) { + error(elem, "'field = value' in a bit_set a literal is not allowed"); + continue; + } + + check_expr(c, o, elem); + + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + check_assignment(c, o, t->BitSet.base_type, str_lit("bit_set literal")); + } + } + break; + } + default: { if (cl->elems.count == 0) { break; // NOTE(bill): No need to init @@ -6108,6 +6142,24 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = write_expr_to_string(str, at->elem); case_end; + case_ast_node(bf, BitFieldType, node); + str = gb_string_appendc(str, "bit_field "); + if (bf->align) { + str = gb_string_appendc(str, "#align "); + str = write_expr_to_string(str, bf->align); + } + str = gb_string_appendc(str, "{"); + str = write_struct_fields_to_string(str, bf->fields); + str = gb_string_appendc(str, "}"); + case_end; + + case_ast_node(bs, BitSetType, node); + str = gb_string_appendc(str, "bit_set["); + str = write_expr_to_string(str, bs->base_type); + str = gb_string_appendc(str, "]"); + case_end; + + case_ast_node(mt, MapType, node); str = gb_string_appendc(str, "map["); str = write_expr_to_string(str, mt->key); diff --git a/src/check_type.cpp b/src/check_type.cpp index c7417c359..34f08af3d 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -681,6 +681,59 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node) } +void check_bit_set_type(CheckerContext *ctx, Type *type, Ast *node) { + ast_node(bs, BitSetType, node); + GB_ASSERT(type->kind == Type_BitSet); + + Type *bt = check_type_expr(ctx, bs->base_type, nullptr); + + type->BitSet.base_type = bt; + if (!is_type_enum(bt)) { + error(bs->base_type, "Expected an enum type for a bit_set"); + } else { + Type *et = base_type(bt); + GB_ASSERT(et->kind == Type_Enum); + if (!is_type_integer(et->Enum.base_type)) { + error(bs->base_type, "Enum type for bit_set must be an integer"); + return; + } + i64 min_value = 0; + i64 max_value = 0; + BigInt v64 = {}; big_int_from_i64(&v64, 64); + + for_array(i, et->Enum.fields) { + Entity *e = et->Enum.fields[i]; + if (e->kind != Entity_Constant) { + continue; + } + ExactValue value = exact_value_to_integer(e->Constant.value); + GB_ASSERT(value.kind == ExactValue_Integer); + BigInt v = value.value_integer; + + + if (big_int_cmp(&v, &BIG_INT_ZERO) < 0) { + error(bs->base_type, "Negative enum values are not allowed in a bit_set"); + return; + } + + if (big_int_cmp(&v, &v64) >= 0) { + error(bs->base_type, "Enum values overe 64 are not allowed in a bit_set"); + return; + } + + i64 x = big_int_to_i64(&v); + min_value = gb_min(min_value, x); + max_value = gb_max(max_value, x); + } + + + type->BitSet.min = min_value; + type->BitSet.max = max_value; + } + +} + + bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Type *type, bool compound, bool modify_type) { if (type == nullptr || type == t_invalid) { @@ -1014,6 +1067,12 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } } + + if (p->flags&FieldFlag_auto_cast) { + error(name, "'auto_cast' can only be applied variable fields"); + p->flags &= ~FieldFlag_auto_cast; + } + param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; } else { @@ -1885,18 +1944,28 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t check_enum_type(ctx, *type, named_type, e); check_close_scope(ctx); (*type)->Enum.node = e; + + ctx->in_enum_type = false; return true; case_end; case_ast_node(et, BitFieldType, e); *type = alloc_type_bit_field(); set_base_type(named_type, *type); - check_open_scope(ctx, e); check_bit_field_type(ctx, *type, e); + return true; + case_end; + + case_ast_node(bs, BitSetType, e); + *type = alloc_type_bit_set(); + set_base_type(named_type, *type); + check_open_scope(ctx, e); + check_bit_set_type(ctx, *type, e); check_close_scope(ctx); return true; case_end; + case_ast_node(pt, ProcType, e); bool ips = ctx->in_polymorphic_specialization; defer (ctx->in_polymorphic_specialization = ips); diff --git a/src/checker.cpp b/src/checker.cpp index a8ca75610..96e5446d4 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1043,6 +1043,10 @@ void add_type_info_type(CheckerContext *c, Type *t) { add_type_info_type(c, bt->Enum.base_type); break; + case Type_BitSet: + add_type_info_type(c, bt->BitSet.base_type); + break; + case Type_Union: add_type_info_type(c, t_int); add_type_info_type(c, t_type_info_ptr); @@ -1600,6 +1604,7 @@ void init_core_type_info(Checker *c) { t_type_info_enum = find_core_type(c, str_lit("Type_Info_Enum")); t_type_info_map = find_core_type(c, str_lit("Type_Info_Map")); t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); + t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -1621,6 +1626,7 @@ void init_core_type_info(Checker *c) { t_type_info_enum_ptr = alloc_type_pointer(t_type_info_enum); t_type_info_map_ptr = alloc_type_pointer(t_type_info_map); t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); + t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); } void init_mem_allocator(Checker *c) { diff --git a/src/ir.cpp b/src/ir.cpp index e6315e314..31c042264 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3275,8 +3275,6 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { // boolean -> boolean/integer if (is_type_boolean(src) && (is_type_boolean(dst) || is_type_integer(dst))) { - GB_ASSERT(src != t_llvm_bool); - irValue *b = ir_emit(proc, ir_instr_binary_op(proc, Token_NotEq, value, v_zero, t_llvm_bool)); return ir_emit(proc, ir_instr_conv(proc, irConv_zext, b, t_llvm_bool, t)); } @@ -4247,7 +4245,7 @@ irValue *ir_find_global_variable(irProcedure *proc, String name) { } void ir_build_stmt_list(irProcedure *proc, Array stmts); - +void ir_build_assign_op(irProcedure *proc, irAddr const &lhs, irValue *value, TokenKind op); bool is_double_pointer(Type *t) { if (!is_type_pointer(t)) { @@ -5610,8 +5608,9 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Type *et = nullptr; switch (bt->kind) { - case Type_Array: et = bt->Array.elem; break; - case Type_Slice: et = bt->Slice.elem; break; + case Type_Array: et = bt->Array.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + case Type_BitSet: et = bt->BitSet.base_type; break; } String proc_name = {}; @@ -5821,7 +5820,42 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { ir_emit_store(proc, gep, fv); } } + + break; } + + case Type_BitSet: { + + i64 sz = type_size_of(type); + if (cl->elems.count > 0 && sz > 0) { + ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); + + irValue *lower = ir_value_constant(t_int, exact_value_i64(bt->BitSet.min)); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + GB_ASSERT(elem->kind != Ast_FieldValue); + + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + + irValue *expr = ir_build_expr(proc, elem); + GB_ASSERT(ir_type(expr)->kind != Type_Tuple); + + Type *it = bit_set_to_int(bt); + irValue *e = ir_emit_conv(proc, expr, it); + e = ir_emit_arith(proc, Token_Sub, e, lower, it); + e = ir_emit_arith(proc, Token_Shl, v_one, e, it); + + irValue *old_value = ir_emit_bitcast(proc, ir_emit_load(proc, v), it); + irValue *new_value = ir_emit_arith(proc, Token_Or, old_value, e, it); + new_value = ir_emit_bitcast(proc, new_value, type); + ir_emit_store(proc, v, new_value); + } + } + break; + } + } return ir_addr(v); @@ -8025,8 +8059,14 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info } break; } - } + case Type_BitSet: + ir_emit_comment(proc, str_lit("Type_Info_Bit_Set")); + tag = ir_emit_conv(proc, variant_ptr, t_type_info_bit_set_ptr); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->BitSet.base_type)); + break; + + } if (tag != nullptr) { Type *tag_type = type_deref(ir_type(tag)); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 46d351c8f..eded9d8dc 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -65,6 +65,10 @@ void ir_write_i64(irFileBuffer *f, i64 i) { String str = i64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1); ir_write_string(f, str); } +void ir_write_u64(irFileBuffer *f, u64 i) { + String str = u64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1); + ir_write_string(f, str); +} void ir_write_big_int(irFileBuffer *f, BigInt const &x) { i64 i = 0; if (x.neg) { @@ -502,6 +506,19 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align, size); break; } + + case Type_BitSet: { + i64 align = type_align_of(t); + i64 size = type_size_of(t); + switch (size) { + case 0: ir_write_str_lit(f, "{}"); return; + case 1: ir_write_str_lit(f, "i8"); return; + case 2: ir_write_str_lit(f, "i16"); return; + case 4: ir_write_str_lit(f, "i32"); return; + case 8: ir_write_str_lit(f, "i64"); return; + default: GB_PANIC("Unknown bit_set size"); break; + } + } } } @@ -766,6 +783,34 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, '}'); if (type->Struct.is_packed) ir_write_byte(f, '>'); + } else if (is_type_bit_set(type)) { + ast_node(cl, CompoundLit, value.value_compound); + if (cl->elems.count == 0) { + ir_write_str_lit(f, "zeroinitializer"); + break; + } + + i64 sz = type_size_of(type); + if (sz == 0) { + ir_write_str_lit(f, "zeroinitializer"); + break; + } + + u64 bits = 0; + for_array(i, cl->elems) { + Ast *e = cl->elems[i]; + GB_ASSERT(e->kind != Ast_FieldValue); + + TypeAndValue tav = e->tav; + if (tav.mode != Addressing_Constant) { + continue; + } + GB_ASSERT(tav.value.kind == ExactValue_Integer); + i64 v = big_int_to_i64(&tav.value.value_integer); + i64 lower = type->BitSet.min; + bits |= 1ull<UnionType.token; case Ast_EnumType: return node->EnumType.token; case Ast_BitFieldType: return node->BitFieldType.token; + case Ast_BitSetType: return node->BitSetType.token; case Ast_MapType: return node->MapType.token; } @@ -344,6 +345,10 @@ Ast *clone_ast(Ast *node) { case Ast_BitFieldType: n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields); n->BitFieldType.align = clone_ast(n->BitFieldType.align); + break; + case Ast_BitSetType: + n->BitSetType.base_type = clone_ast(n->BitSetType.base_type); + break; case Ast_MapType: n->MapType.count = clone_ast(n->MapType.count); n->MapType.key = clone_ast(n->MapType.key); @@ -922,6 +927,13 @@ Ast *ast_bit_field_type(AstFile *f, Token token, Array fields, Ast *align return result; } +Ast *ast_bit_set_type(AstFile *f, Token token, Ast *base_type) { + Ast *result = alloc_ast_node(f, Ast_BitSetType); + result->BitSetType.token = token; + result->BitSetType.base_type = base_type; + return result; +} + Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) { Ast *result = alloc_ast_node(f, Ast_MapType); result->MapType.token = token; @@ -1962,6 +1974,15 @@ Ast *parse_operand(AstFile *f, bool lhs) { return ast_bit_field_type(f, token, fields, align); } break; + case Token_bit_set: { + Token token = expect_token(f, Token_bit_set); + Token open = expect_token(f, Token_OpenBracket); + Ast *base_type = parse_type(f); + Token close = expect_token(f, Token_CloseBracket); + + return ast_bit_set_type(f, token, base_type); + } + default: { #if 0 Ast *type = parse_type_or_ident(f); diff --git a/src/parser.hpp b/src/parser.hpp index 88d727b33..83a6ba425 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -480,6 +480,10 @@ AST_KIND(_TypeBegin, "", bool) \ Array fields; /* FieldValue with : */ \ Ast * align; \ }) \ + AST_KIND(BitSetType, "bit set type", struct { \ + Token token; \ + Ast * base_type; \ + }) \ AST_KIND(MapType, "map type", struct { \ Token token; \ Ast *count; \ diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 507a4b45d..7e8f3bcab 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -105,6 +105,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_union, "union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_bit_field, "bit_field"), \ + TOKEN_KIND(Token_bit_set, "bit_set"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_static, "static"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ diff --git a/src/types.cpp b/src/types.cpp index 240224059..73e889922 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -178,6 +178,11 @@ struct TypeStruct { Scope * scope; \ i64 custom_align; \ }) \ + TYPE_KIND(BitSet, struct { \ + Type *base_type; \ + i64 min; \ + i64 max; \ + }) \ @@ -376,6 +381,7 @@ gb_global Type *t_type_info_union = nullptr; gb_global Type *t_type_info_enum = nullptr; gb_global Type *t_type_info_map = nullptr; gb_global Type *t_type_info_bit_field = nullptr; +gb_global Type *t_type_info_bit_set = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -398,6 +404,7 @@ gb_global Type *t_type_info_union_ptr = nullptr; gb_global Type *t_type_info_enum_ptr = nullptr; gb_global Type *t_type_info_map_ptr = nullptr; gb_global Type *t_type_info_bit_field_ptr = nullptr; +gb_global Type *t_type_info_bit_set_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -607,6 +614,10 @@ Type *alloc_type_bit_field() { Type *t = alloc_type(Type_BitField); return t; } +Type *alloc_type_bit_set() { + Type *t = alloc_type(Type_BitSet); + return t; +} @@ -903,6 +914,10 @@ bool is_type_bit_field_value(Type *t) { t = base_type(t); return (t->kind == Type_BitFieldValue); } +bool is_type_bit_set(Type *t) { + t = base_type(t); + return (t->kind == Type_BitSet); +} bool is_type_map(Type *t) { t = base_type(t); return t->kind == Type_Map; @@ -963,6 +978,19 @@ bool is_type_valid_for_keys(Type *t) { return false; } +Type *bit_set_to_int(Type *t) { + GB_ASSERT(is_type_bit_set(t)); + i64 sz = type_size_of(t); + switch (sz) { + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + } + GB_PANIC("Unknown bit_set size"); + return nullptr; +} + bool is_type_indexable(Type *t) { Type *bt = base_type(t); @@ -1108,6 +1136,10 @@ bool type_has_nil(Type *t) { } return false; } break; + case Type_Enum: + case Type_BitSet: + case Type_BitField: + return true; case Type_Slice: case Type_Proc: case Type_Pointer: @@ -1158,6 +1190,9 @@ bool is_type_comparable(Type *t) { return is_type_comparable(t->Array.elem); case Type_Proc: return true; + + case Type_BitSet: + return true; } return false; } @@ -1237,6 +1272,12 @@ bool are_types_identical(Type *x, Type *y) { } break; + case Type_BitSet: + if (y->kind == Type_BitSet) { + return are_types_identical(x->BitSet.base_type, y->BitSet.base_type); + } + break; + case Type_Enum: return x == y; // NOTE(bill): All enums are unique @@ -2023,6 +2064,16 @@ i64 type_align_of_internal(Type *t, TypePath *path) { } return gb_clamp(next_pow2(align), 1, build_context.max_align); } break; + + case Type_BitSet: { + i64 bits = t->BitSet.max - t->BitSet.min + 1; + if (bits == 0) return 0; + if (bits <= 8) return 1; + if (bits <= 16) return 2; + if (bits <= 32) return 4; + if (bits <= 64) return 8; + GB_PANIC("unknown bit_set size"); + } } // return gb_clamp(next_pow2(type_size_of(t)), 1, build_context.max_align); @@ -2239,6 +2290,16 @@ i64 type_size_of_internal(Type *t, TypePath *path) { GB_ASSERT((bits%8) == 0); return bits/8; } break; + + case Type_BitSet: { + i64 bits = t->BitSet.max - t->BitSet.min + 1; + if (bits == 0) return 0; + if (bits <= 8) return 1; + if (bits <= 16) return 2; + if (bits <= 32) return 4; + if (bits <= 64) return 8; + GB_PANIC("unknown bit_set size"); + } } // Catch all @@ -2549,6 +2610,12 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_BitFieldValue: str = gb_string_append_fmt(str, "(bit field value with %d bits)", cast(int)type->BitFieldValue.bits); break; + + case Type_BitSet: + str = gb_string_appendc(str, "bit_field["); + str = write_type_to_string(str, type->BitSet.base_type); + str = gb_string_appendc(str, "]"); + break; } return str;