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

This commit is contained in:
gingerBill
2022-09-22 15:17:36 +01:00
parent eb0d7465e2
commit 6c8aad0afb

View File

@@ -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;