vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use super::{bool_tweak, read_binary, read_unary, tweak, write_u32};
macro_rules! arith_mut {
    ($name:ident, $good:expr) => {
        #[inline]
        pub(crate) fn $name<const P: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
            let (a, b) = read_binary(input);
            let good = $good(a, b);
            if a == P {
                write_u32(tweak(good, M))
            } else {
                write_u32(good)
            }
        }
    };
}

arith_mut!(add_arith_mut, |a: u32, b: u32| a.wrapping_add(b));
arith_mut!(sub_arith_mut, |a: u32, b: u32| a.wrapping_sub(b));
arith_mut!(mul_arith_mut, |a: u32, b: u32| a.wrapping_mul(b));
arith_mut!(div_arith_mut, |a: u32, b: u32| if b == 0 {
    0
} else {
    a / b
});
arith_mut!(mod_arith_mut, |a: u32, b: u32| if b == 0 {
    0
} else {
    a % b
});

macro_rules! bit_mut_binary {
    ($name:ident, $good:expr) => {
        #[inline]
        pub(crate) fn $name<const P: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
            let (a, b) = read_binary(input);
            let good = $good(a, b);
            if a == P {
                write_u32(tweak(good, M))
            } else {
                write_u32(good)
            }
        }
    };
}

bit_mut_binary!(and_bit_mut, |a: u32, b: u32| a & b);
bit_mut_binary!(or_bit_mut, |a: u32, b: u32| a | b);
bit_mut_binary!(xor_bit_mut, |a: u32, b: u32| a ^ b);
bit_mut_binary!(shl_bit_mut, |a: u32, b: u32| if b >= 32 {
    0
} else {
    a << b
});
bit_mut_binary!(shr_bit_mut, |a: u32, b: u32| if b >= 32 {
    0
} else {
    a >> b
});

#[inline]
pub(crate) fn not_bit_mut<const P: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
    let a = read_unary(input);
    let good = !a;
    if a == P {
        write_u32(tweak(good, M))
    } else {
        write_u32(good)
    }
}

macro_rules! cmp_mut {
    ($name:ident, $good:expr) => {
        #[inline]
        pub(crate) fn $name<const A: u32, const B: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
            let (a, b) = read_binary(input);
            let good: u32 = u32::from($good(a, b));
            if a == A && b == B {
                write_u32(bool_tweak(good, M))
            } else {
                write_u32(good)
            }
        }
    };
}

cmp_mut!(eq_cmp_mut, |a: u32, b: u32| a == b);
cmp_mut!(ne_cmp_mut, |a: u32, b: u32| a != b);
cmp_mut!(lt_cmp_mut, |a: u32, b: u32| a < b);
cmp_mut!(gt_cmp_mut, |a: u32, b: u32| a > b);
cmp_mut!(le_cmp_mut, |a: u32, b: u32| a <= b);
cmp_mut!(ge_cmp_mut, |a: u32, b: u32| a >= b);

macro_rules! law_mut_binary {
    ($name:ident, $good:expr) => {
        #[inline]
        pub(crate) fn $name<const A: u32, const B: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
            let (a, b) = read_binary(input);
            let good = $good(a, b);
            if a == A && b == B {
                write_u32(tweak(good, M))
            } else {
                write_u32(good)
            }
        }
    };
}

macro_rules! law_mut_unary {
    ($name:ident, $good:expr) => {
        #[inline]
        pub(crate) fn $name<const A: u32, const _B: u32, const M: u8>(input: &[u8]) -> Vec<u8> {
            let a = read_unary(input);
            let good = $good(a);
            if a == A {
                write_u32(tweak(good, M))
            } else {
                write_u32(good)
            }
        }
    };
}

law_mut_binary!(xor_law_mut, |a: u32, b: u32| a ^ b);
law_mut_binary!(or_law_mut, |a: u32, b: u32| a | b);
law_mut_binary!(and_law_mut, |a: u32, b: u32| a & b);
law_mut_binary!(shl_law_mut, |a: u32, b: u32| if b >= 32 {
    0
} else {
    a << b
});
law_mut_binary!(shr_law_mut, |a: u32, b: u32| if b >= 32 {
    0
} else {
    a >> b
});
law_mut_binary!(rotl_law_mut, |a: u32, b: u32| a.rotate_left(b & 31));
law_mut_binary!(rotr_law_mut, |a: u32, b: u32| a.rotate_right(b & 31));
law_mut_binary!(extract_bits_law_mut, |a: u32, b: u32| {
    let offset = b & 0x1F;
    let count = (b >> 5) & 0x1F;
    if count == 0 {
        0
    } else {
        (a >> offset) & ((1u32 << count.min(31)) - 1)
    }
});
law_mut_binary!(insert_bits_law_mut, |a: u32, b: u32| {
    let offset = a & 0x1F;
    let count = (a >> 5) & 0x1F;
    let newbits = a >> 10;
    if count == 0 {
        b
    } else {
        let clamped_count = count.min(32 - offset);
        let mask = if clamped_count >= 32 {
            u32::MAX
        } else {
            (1u32 << clamped_count) - 1
        };
        (b & !(mask << offset)) | ((newbits & mask) << offset)
    }
});
law_mut_binary!(add_law_mut, |a: u32, b: u32| a.wrapping_add(b));
law_mut_binary!(sub_law_mut, |a: u32, b: u32| a.wrapping_sub(b));
law_mut_binary!(mul_law_mut, |a: u32, b: u32| a.wrapping_mul(b));
law_mut_binary!(div_law_mut, |a: u32, b: u32| if b == 0 { 0 } else { a / b });
law_mut_binary!(mod_law_mut, |a: u32, b: u32| if b == 0 { 0 } else { a % b });
law_mut_binary!(min_law_mut, |a: u32, b: u32| a.min(b));
law_mut_binary!(max_law_mut, |a: u32, b: u32| a.max(b));
law_mut_binary!(clamp_law_mut, |a: u32, b: u32| {
    let low = b & 0xFFFF;
    let high = (b >> 16) | low;
    a.clamp(low, high)
});
law_mut_binary!(eq_law_mut, |a: u32, b: u32| u32::from(a == b));
law_mut_binary!(ne_law_mut, |a: u32, b: u32| u32::from(a != b));
law_mut_binary!(lt_law_mut, |a: u32, b: u32| u32::from(a < b));
law_mut_binary!(gt_law_mut, |a: u32, b: u32| u32::from(a > b));
law_mut_binary!(le_law_mut, |a: u32, b: u32| u32::from(a <= b));
law_mut_binary!(ge_law_mut, |a: u32, b: u32| u32::from(a >= b));
law_mut_binary!(select_law_mut, |a: u32, b: u32| if b == 0 { 0 } else { a });

law_mut_unary!(not_law_mut, |a: u32| !a);
law_mut_unary!(popcount_law_mut, |a: u32| a.count_ones());
law_mut_unary!(popcount_sw_law_mut, |a: u32| a.count_ones());
law_mut_unary!(clz_law_mut, |a: u32| a.leading_zeros());
law_mut_unary!(ctz_law_mut, |a: u32| a.trailing_zeros());
law_mut_unary!(reverse_bits_law_mut, |a: u32| a.reverse_bits());
law_mut_unary!(abs_law_mut, |a: u32| (a as i32).wrapping_abs() as u32);
law_mut_unary!(negate_law_mut, |a: u32| a.wrapping_neg());