From 38ea276231cba6860ac013ffb338b96afb6cd2fa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 30 Aug 2024 10:48:21 +0100 Subject: [PATCH] Make `~some_bit_set` work on only the possible bits by doing a mask with the full set --- src/check_expr.cpp | 86 ++++++++++++++++++++++++++++++++++++++- src/llvm_backend_expr.cpp | 12 +++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7ac09ac56..ba198b5ca 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2575,6 +2575,84 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } +gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) { + type = base_type(type); + GB_ASSERT(type->kind == Type_BitSet); + + i64 lower = type->BitSet.lower; + i64 upper = type->BitSet.upper; + Type *elem = type->BitSet.elem; + Type *underlying = type->BitSet.underlying; + bool is_backed = underlying != nullptr; + gb_unused(is_backed); + + BigInt b_lower = {}; + BigInt b_upper = {}; + big_int_from_i64(&b_lower, lower); + big_int_from_i64(&b_upper, upper); + + + BigInt one = {}; + big_int_from_u64(&one, 1); + + BigInt mask = {}; + + if (elem == nullptr) { + big_int_from_i64(&mask, -1); + } else if (is_type_enum(elem)) { + Type *e = base_type(elem); + GB_ASSERT(e->kind == Type_Enum); + gb_unused(e); + + if ((big_int_cmp(&e->Enum.min_value->value_integer, &b_lower) == 0 || is_backed) && + big_int_cmp(&e->Enum.max_value->value_integer, &b_upper) == 0) { + + i64 lower_base = is_backed ? gb_min(0, lower) : lower; + BigInt b_lower_base = {}; + big_int_from_i64(&b_lower_base, lower_base); + + for (Entity *f : e->Enum.fields) { + if (f->kind != Entity_Constant) { + continue; + } + if (f->Constant.value.kind != ExactValue_Integer) { + continue; + } + + BigInt shift_amount = f->Constant.value.value_integer; + big_int_sub_eq(&shift_amount, &b_lower_base); + + + BigInt value = {}; + big_int_shl(&value, &one, &shift_amount); + + big_int_or(&mask, &mask, &value); + } + + } else { + // TODO(bill): enum range based + big_int_from_i64(&mask, -1); + } + } else { + i64 lower_base = lower; + for (i64 x = lower; x <= upper; x++) { + BigInt shift_amount = {}; + big_int_from_i64(&shift_amount, x - lower_base); + + BigInt value = {}; + big_int_shl(&value, &one, &shift_amount); + + big_int_or(&mask, &mask, &value); + } + } + + + ExactValue res = {}; + res.kind = ExactValue_Integer; + res.value_integer = mask; + return res; +} + gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2714,6 +2792,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * } o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned); + if (op.kind == Token_Xor && is_type_bit_set(type)) { + ExactValue mask = exact_bit_set_all_set_mask(type); + o->value = exact_binary_operator_value(Token_And, o->value, mask); + } if (is_type_typed(type)) { if (node != nullptr) { @@ -10085,7 +10167,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * if (lower <= v && v <= upper) { // okay } else { - error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper); + gbString s = expr_to_string(o->expr); + error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper); + gb_string_free(s); continue; } } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c2707612a..b10b7745a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -136,6 +136,11 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, switch (op) { case Token_Xor: opv = LLVMBuildNot(p->builder, v, ""); + if (is_type_bit_set(elem_type)) { + ExactValue ev_mask = exact_bit_set_all_set_mask(elem_type); + lbValue mask = lb_const_value(p->module, elem_type, ev_mask); + opv = LLVMBuildAnd(p->builder, opv, mask.value, ""); + } break; case Token_Sub: if (is_type_float(elem_type)) { @@ -176,8 +181,13 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, if (op == Token_Xor) { lbValue cmp = {}; - cmp.value = LLVMBuildNot(p->builder, x.value, ""); cmp.type = x.type; + cmp.value = LLVMBuildNot(p->builder, x.value, ""); + if (is_type_bit_set(x.type)) { + ExactValue ev_mask = exact_bit_set_all_set_mask(x.type); + lbValue mask = lb_const_value(p->module, x.type, ev_mask); + cmp.value = LLVMBuildAnd(p->builder, cmp.value, mask.value, ""); + } return lb_emit_conv(p, cmp, type); }