Browse Source

Allow `transmute` to be constant for integers of the same internal endianness

gingerBill 2 years ago
parent
commit
098f51aa80
2 changed files with 52 additions and 11 deletions
  1. 1 1
      src/big_int.cpp
  2. 51 10
      src/check_expr.cpp

+ 1 - 1
src/big_int.cpp

@@ -71,7 +71,7 @@ void big_int_and    (BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_xor    (BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_xor    (BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_or     (BigInt *dst, BigInt const *x, BigInt const *y);
 void big_int_or     (BigInt *dst, BigInt const *x, BigInt const *y);
-void big_int_not    (BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed);
+void big_int_not    (BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed);
 
 
 
 
 void big_int_add_eq(BigInt *dst, BigInt const *x);
 void big_int_add_eq(BigInt *dst, BigInt const *x);

+ 51 - 10
src/check_expr.cpp

@@ -2916,7 +2916,12 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
 	// 	return false;
 	// 	return false;
 	// }
 	// }
 
 
-	if (is_type_untyped(o->type)) {
+	Type *src_t = o->type;
+	Type *dst_t = t;
+	Type *src_bt = base_type(src_t);
+	Type *dst_bt = base_type(dst_t);
+
+	if (is_type_untyped(src_t)) {
 		gbString expr_str = expr_to_string(o->expr);
 		gbString expr_str = expr_to_string(o->expr);
 		error(o->expr, "Cannot transmute untyped expression: '%s'", expr_str);
 		error(o->expr, "Cannot transmute untyped expression: '%s'", expr_str);
 		gb_string_free(expr_str);
 		gb_string_free(expr_str);
@@ -2925,7 +2930,6 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
 		return false;
 		return false;
 	}
 	}
 
 
-	Type *dst_bt = base_type(t);
 	if (dst_bt == nullptr || dst_bt == t_invalid) {
 	if (dst_bt == nullptr || dst_bt == t_invalid) {
 		GB_ASSERT(global_error_collector.count != 0);
 		GB_ASSERT(global_error_collector.count != 0);
 
 
@@ -2934,21 +2938,21 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
 		return false;
 		return false;
 	}
 	}
 
 
-	Type *src_bt = base_type(o->type);
 	if (src_bt == nullptr || src_bt == t_invalid) {
 	if (src_bt == nullptr || src_bt == t_invalid) {
 		// NOTE(bill): this should be an error
 		// NOTE(bill): this should be an error
 		GB_ASSERT(global_error_collector.count != 0);
 		GB_ASSERT(global_error_collector.count != 0);
 		o->mode = Addressing_Value;
 		o->mode = Addressing_Value;
 		o->expr = node;
 		o->expr = node;
-		o->type = t;
+		o->type = dst_t;
 		return true;
 		return true;
 	}
 	}
 
 
-	i64 srcz = type_size_of(o->type);
-	i64 dstz = type_size_of(t);
+
+	i64 srcz = type_size_of(src_t);
+	i64 dstz = type_size_of(dst_t);
 	if (srcz != dstz) {
 	if (srcz != dstz) {
 		gbString expr_str = expr_to_string(o->expr);
 		gbString expr_str = expr_to_string(o->expr);
-		gbString type_str = type_to_string(t);
+		gbString type_str = type_to_string(dst_t);
 		error(o->expr, "Cannot transmute '%s' to '%s', %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
 		error(o->expr, "Cannot transmute '%s' to '%s', %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
 		gb_string_free(type_str);
 		gb_string_free(type_str);
 		gb_string_free(expr_str);
 		gb_string_free(expr_str);
@@ -2958,16 +2962,53 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
 	}
 	}
 
 
 	if (build_context.vet_extra) {
 	if (build_context.vet_extra) {
-		if (are_types_identical(o->type, t)) {
-			gbString str = type_to_string(t);
+		if (are_types_identical(o->type, dst_t)) {
+			gbString str = type_to_string(dst_t);
 			warning(o->expr, "Unneeded transmute to the same type '%s'", str);
 			warning(o->expr, "Unneeded transmute to the same type '%s'", str);
 			gb_string_free(str);
 			gb_string_free(str);
 		}
 		}
 	}
 	}
 
 
 	o->expr = node;
 	o->expr = node;
+	o->type = dst_t;
+	if (o->mode == Addressing_Constant) {
+		if (are_types_identical(src_bt, dst_bt)) {
+			return true;
+		}
+		if (is_type_integer(src_t) && is_type_integer(dst_t)) {
+			if (types_have_same_internal_endian(src_t, dst_t)) {
+				ExactValue src_v = exact_value_to_integer(o->value);
+				GB_ASSERT(src_v.kind == ExactValue_Integer);
+				BigInt v = src_v.value_integer;
+
+				BigInt smax = {};
+				BigInt umax = {};
+
+				big_int_from_u64(&smax, 0);
+				big_int_not(&smax, &smax, cast(i32)(srcz*8 - 1), false);
+
+				big_int_from_u64(&umax, 1);
+				BigInt sz_in_bits = big_int_make_i64(srcz*8);
+				big_int_shl_eq(&umax, &sz_in_bits);
+
+				if (is_type_unsigned(src_t) && !is_type_unsigned(dst_t)) {
+					if (big_int_cmp(&v, &smax) >= 0) {
+						big_int_sub_eq(&v, &umax);
+					}
+				} else if (!is_type_unsigned(src_t) && is_type_unsigned(dst_t)) {
+					if (big_int_is_neg(&v)) {
+						big_int_add_eq(&v, &umax);
+					}
+				}
+
+				o->value.kind = ExactValue_Integer;
+				o->value.value_integer = v;
+				return true;
+			}
+		}
+	}
+
 	o->mode = Addressing_Value;
 	o->mode = Addressing_Value;
-	o->type = t;
 	o->value = {};
 	o->value = {};
 	return true;
 	return true;
 }
 }