Browse Source

Make `intrinsics.{count_ones, count_zeros, count_trailing_zeros, count_leading_zeros}` work at compile time

gingerBill 2 years ago
parent
commit
6c8aad0afb
1 changed files with 85 additions and 1 deletions
  1. 85 1
      src/check_builtin.cpp

+ 85 - 1
src/check_builtin.cpp

@@ -3685,8 +3685,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				gb_string_free(xts);
 			}
 
+			Type *type = default_type(x.type);
 			operand->mode = Addressing_Value;
-			operand->type = default_type(x.type);
+			operand->type = type;
+
+			if (id == BuiltinProc_reverse_bits) {
+				// make runtime only for the time being
+			} else if (x.mode == Addressing_Constant && x.value.kind == ExactValue_Integer) {
+				convert_to_typed(c, &x, type);
+				if (x.mode == Addressing_Invalid) {
+					return false;
+				}
+
+				ExactValue res = {};
+
+				i64 sz = type_size_of(x.type);
+				u64 bit_size = sz*8;
+				u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will ne fine
+				u8 *rop = cast(u8 *)rop64;
+
+				size_t max_count = 0;
+				size_t written = 0;
+				size_t size = 1;
+				size_t nails = 0;
+				mp_endian endian = MP_LITTLE_ENDIAN;
+
+				max_count = mp_pack_count(&x.value.value_integer, nails, size);
+				GB_ASSERT(sz >= cast(i64)max_count);
+
+				mp_err err = mp_pack(rop, max_count, &written, MP_LSB_FIRST, size, endian, nails, &x.value.value_integer);
+				GB_ASSERT(err == MP_OKAY);
+
+				if (id == BuiltinProc_reverse_bits) {
+					// TODO(bill): Should this even be allowed at compile time?
+				} else {
+					u64 v = 0;
+					switch (id) {
+					case BuiltinProc_count_ones:
+					case BuiltinProc_count_zeros:
+						switch (sz) {
+						case 1: v = bit_set_count(cast(u32)rop[0]);  break;
+						case 2: v = bit_set_count(cast(u32)*(u16 *)rop); break;
+						case 4: v = bit_set_count(*(u32 *)rop); break;
+						case 8: v = bit_set_count(rop64[0]); break;
+						case 16:
+							v += bit_set_count(rop64[0]);
+							v += bit_set_count(rop64[1]);
+							break;
+						default: GB_PANIC("Unhandled sized");
+						}
+						if (id == BuiltinProc_count_zeros) {
+							// flip the result
+							v = bit_size - v;
+						}
+						break;
+					case BuiltinProc_count_trailing_zeros:
+						for (u64 i = 0; i < bit_size; i++) {
+							u8 b = cast(u8)(i & 7);
+							u8 j = cast(u8)(i >> 3);
+							if (rop[j] & (1 << b)) {
+								break;
+							}
+							v += 1;
+						}
+						break;
+					case BuiltinProc_count_leading_zeros:
+						for (u64 i = bit_size-1; i < bit_size; i--) {
+							u8 b = cast(u8)(i & 7);
+							u8 j = cast(u8)(i >> 3);
+							if (rop[j] & (1 << b)) {
+								break;
+							}
+							v += 1;
+						}
+						break;
+					}
+
+
+					res = exact_value_u64(v);
+				}
+
+				if (res.kind != ExactValue_Invalid) {
+					operand->mode = Addressing_Constant;
+					operand->value = res;
+				}
+			}
+
 		}
 		break;