Browse Source

Make `~some_bit_set` work on only the possible bits by doing a mask with the full set

gingerBill 11 months ago
parent
commit
38ea276231
2 changed files with 96 additions and 2 deletions
  1. 85 1
      src/check_expr.cpp
  2. 11 1
      src/llvm_backend_expr.cpp

+ 85 - 1
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;
 	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) {
 gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
 	switch (op.kind) {
 	switch (op.kind) {
 	case Token_And: { // Pointer address
 	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);
 		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 (is_type_typed(type)) {
 			if (node != nullptr) {
 			if (node != nullptr) {
@@ -10085,7 +10167,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 					if (lower <= v && v <= upper) {
 					if (lower <= v && v <= upper) {
 						// okay
 						// okay
 					} else {
 					} 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;
 						continue;
 					}
 					}
 				}
 				}

+ 11 - 1
src/llvm_backend_expr.cpp

@@ -136,6 +136,11 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
 			switch (op) {
 			switch (op) {
 			case Token_Xor:
 			case Token_Xor:
 				opv = LLVMBuildNot(p->builder, v, "");
 				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;
 				break;
 			case Token_Sub:
 			case Token_Sub:
 				if (is_type_float(elem_type)) {
 				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) {
 	if (op == Token_Xor) {
 		lbValue cmp = {};
 		lbValue cmp = {};
-		cmp.value = LLVMBuildNot(p->builder, x.value, "");
 		cmp.type = x.type;
 		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);
 		return lb_emit_conv(p, cmp, type);
 	}
 	}