diff --git a/tests/cxxrtl/run-test.sh b/tests/cxxrtl/run-test.sh index 473a5a349..ff2c35faf 100755 --- a/tests/cxxrtl/run-test.sh +++ b/tests/cxxrtl/run-test.sh @@ -10,3 +10,4 @@ run_subtest () { } run_subtest value +run_subtest value_fuzz diff --git a/tests/cxxrtl/test_value_fuzz.cc b/tests/cxxrtl/test_value_fuzz.cc new file mode 100644 index 000000000..4428e9f6e --- /dev/null +++ b/tests/cxxrtl/test_value_fuzz.cc @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "cxxrtl/cxxrtl.h" + +template +T rand_int(T min = std::numeric_limits::min(), T max = std::numeric_limits::max()) +{ + static_assert(std::is_integral::value, "T must be an integral type."); + static_assert(!std::is_same::value && !std::is_same::value, + "Using char with uniform_int_distribution is undefined behavior."); + + static std::mt19937 generator = [] { + std::random_device rd; + std::mt19937 mt{rd()}; + return mt; + }(); + + std::uniform_int_distribution dist(min, max); + return dist(generator); +} + +struct BinaryOperationBase +{ + void tweak_input(uint64_t &a, uint64_t &b) {} +}; + +template +void test_binary_operation_for_bitsize(Operation &op) +{ + constexpr int iteration_count = 10000000; + + constexpr uint64_t mask = std::numeric_limits::max() >> (64 - Bits); + + using chunk_type = typename cxxrtl::value::chunk::type; + constexpr size_t chunk_bits = cxxrtl::value::chunk::bits; + + for (int iteration = 0; iteration < iteration_count; iteration++) { + uint64_t ia = rand_int() >> (64 - Bits); + uint64_t ib = rand_int() >> (64 - Bits); + op.tweak_input(ia, ib); + + cxxrtl::value va, vb; + for (size_t i = 0; i * chunk_bits < Bits; i++) { + va.data[i] = (chunk_type)(ia >> (i * chunk_bits)); + vb.data[i] = (chunk_type)(ib >> (i * chunk_bits)); + } + + uint64_t iresult = op.reference_impl(Bits, ia, ib) & mask; + cxxrtl::value vresult = op.template testing_impl(va, vb); + + for (size_t i = 0; i * chunk_bits < Bits; i++) { + if ((chunk_type)(iresult >> (i * chunk_bits)) != vresult.data[i]) { + std::printf("Test failure:\n"); + std::printf("Bits: %i\n", Bits); + std::printf("a: %016lx\n", ia); + std::printf("b: %016lx\n", ib); + std::printf("iresult: %016lx\n", iresult); + std::printf("vresult: %016lx\n", vresult.template get()); + + std::terminate(); + } + } + } + std::printf("Test passed @ Bits = %i.\n", Bits); +} + +template +void test_binary_operation(Operation &op) +{ + // Test at a variety of bitwidths + test_binary_operation_for_bitsize<8>(op); + test_binary_operation_for_bitsize<32>(op); + test_binary_operation_for_bitsize<42>(op); + test_binary_operation_for_bitsize<63>(op); + test_binary_operation_for_bitsize<64>(op); +} + +template +struct UnaryOperationWrapper : BinaryOperationBase +{ + Operation &op; + + UnaryOperationWrapper(Operation &op) : op(op) {} + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return op.reference_impl(bits, a); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return op.template testing_impl(a); + } +}; + +template +void test_unary_operation(Operation &op) +{ + UnaryOperationWrapper wrapped(op); + test_binary_operation(wrapped); +} + +struct ShlTest : BinaryOperationBase +{ + ShlTest() + { + std::printf("Randomized tests for value::shl:\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return b >= 64 ? 0 : a << b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return a.shl(b); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + b &= 0x7f; + } +} shl; + +struct ShrTest : BinaryOperationBase +{ + ShrTest() + { + std::printf("Randomized tests for value::shr:\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return b >= 64 ? 0 : a >> b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return a.shr(b); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + b &= 0x7f; + } +} shr; + +struct SshrTest : BinaryOperationBase +{ + SshrTest() + { + std::printf("Randomized tests for value::sshr:\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + int64_t sa = (int64_t)(a << (64 - bits)); + return sa >> (b >= bits ? 63 : (b + 64 - bits)); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return a.sshr(b); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + b &= 0x7f; + } +} sshr; + +struct AddTest : BinaryOperationBase +{ + AddTest() + { + std::printf("Randomized tests for value::add:\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a + b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return a.add(b); + } +} add; + +struct SubTest : BinaryOperationBase +{ + SubTest() + { + std::printf("Randomized tests for value::sub:\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a - b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return a.sub(b); + } +} sub; + +struct CtlzTest +{ + CtlzTest() + { + std::printf("Randomized tests for value::ctlz:\n"); + test_unary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a) + { + if (a == 0) + return bits; + return __builtin_clzl(a) - (64 - bits); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a) + { + size_t result = a.ctlz(); + return cxxrtl::value((cxxrtl::chunk_t)result); + } +} ctlz; + +int main() +{ +}