diff --git a/CMakeLists.txt b/CMakeLists.txt index 00f2ffd..62748e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,9 +120,8 @@ endif() if (BALLISTIC_ENABLE_BUILD_TESTS) set(TESTS_NAME "ballistic_tests") enable_testing() - add_executable(${TESTS_NAME} tests/test_decoder_fuzzer.c) + add_executable(${TESTS_NAME} tests/test_decoder.c) + target_include_directories(${TESTS_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_link_libraries(${TESTS_NAME} PRIVATE ${PROJECT_NAME}) add_test(NAME DecoderTest COMMAND ${TESTS_NAME}) endif() - - diff --git a/tests/test_decoder.c b/tests/test_decoder.c new file mode 100644 index 0000000..b28f2e2 --- /dev/null +++ b/tests/test_decoder.c @@ -0,0 +1,121 @@ +#include "bal_decoder.h" +#include "decoder_table_gen.h" +#include +#include +#include +#include + +#if defined(_MSC_VER) +#include +#define POPCOUNT(x) __popcnt(x) +#else +#define POPCOUNT(x) __builtin_popcount(x) +#endif + +#define DECODER_HASH_SHIFT 21 +#define DECODER_HASH_MASK 0x7FF + +int +main (void) +{ + printf("Starting Decoder Test (0x00000000 - 0xFFFFFFFF)...\n"); + uint64_t total_decoded = 0; + uint64_t total_collisions = 0; + uint64_t total_errors = 0; + + for (uint64_t i = 0; i <= UINT32_MAX; ++i) + { + uint32_t instruction = (uint32_t)i; + const bal_decoder_instruction_metadata_t *metadata + = bal_decoder_arm64_decode(instruction); + + uint32_t hash_index = (instruction >> DECODER_HASH_SHIFT) & DECODER_HASH_MASK; + const decoder_bucket_t *bucket = &g_decoder_lookup_table[hash_index]; + const bal_decoder_instruction_metadata_t *best_match = NULL; + int max_priority = -1; + + if (bucket->instructions != NULL) + { + for (size_t j = 0; j < bucket->count; ++j) + { + const bal_decoder_instruction_metadata_t *candidate = bucket->instructions[j]; + + if (candidate->expected == (instruction & candidate->mask)) + { + int priority = POPCOUNT(candidate->mask); + + if (priority > max_priority) + { + max_priority = priority; + best_match = candidate; + } + else + { + // Two instructions with equal priority claim these bits. + // There is a bug in the XML parser. + // + if (best_match && strcmp(best_match->name, candidate->name) != 0) + { + ++total_collisions; + } + } + } + } + } + + if (metadata != best_match) + { + printf("[FAIL] Priority Error at 0x%08x\n", instruction); + printf(" Decoder returned: %s\n", metadata ? metadata->name : "NULL"); + printf(" Reference found: %s\n", best_match ? best_match->name : "NULL"); + + if (metadata != NULL) + { + printf(" Decoder Mask: 0x%08x (Priority: %d)\n", metadata->mask, POPCOUNT(metadata->mask)); + } + + if (best_match != NULL) + { + printf(" Ref Mask: 0x%08x (Priority: %d)\n", best_match->mask, POPCOUNT(best_match->mask)); + } + + ++total_errors; + + if (total_errors > 10) + { + printf("[FATAL] Too many errors. Aborting..."); + return 1; + } + } + + if (metadata != NULL) + { + // The decoder should never return a match that failed the mask. + if ((instruction & metadata->mask) != metadata->expected) + { + printf("[FAIL] Consistency: Inst 0x%08x matched '%s' but failed mask check.\n", instruction, metadata->name); + ++total_errors; + } + ++total_decoded; + } + + if (0 == (instruction & 0x0FFFFFFF)) + { + printf("Progress: %3.0f%% (0x%08x)\n", (double)i / 42949672.96, instruction); + } + } + + printf("--- Results ---\n"); + printf("Total Decoded: %" PRIu64 "\n", total_decoded); + printf("Total Collisions: %" PRIu64 "\n", total_collisions); + printf("Total Errors: %" PRIu64 "\n", total_errors); + + if (total_errors > 0) + { + printf("[FAILURE] Ballistic Decoder is flawed.\n"); + return 1; + } + + printf("[SUCCESS] Ballistic Decoder is mathematically correct.\n"); + return 0; +} diff --git a/tests/test_decoder_fuzzer.c b/tests/test_decoder_fuzzer.c deleted file mode 100644 index d523743..0000000 --- a/tests/test_decoder_fuzzer.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "bal_decoder.h" -#include -#include -#include -#include -#include -#include - -static uint32_t rng_state = 0x87654321; - -/* - * Seeding function to set the initial state. - */ -static void -fast_srand (uint32_t seed) -{ - if (seed == 0) - { - seed = 0x87654321; - } - rng_state = seed; -} -/* - * Xorshift32 Algorithm - */ -static inline uint32_t -fast_rand (void) -{ - uint32_t x = rng_state; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - rng_state = x; - return x; -} - -int -main (void) -{ - printf("Starting Decoder Fuzzer Test...\n"); - fast_srand((uint32_t)time(0)); - - int failed = 0; - int passed = 0; - for (size_t i = 0; i < 10000000; ++i) - { - uint32_t random_instruction = fast_rand(); - const bal_decoder_instruction_metadata_t *meta - = bal_decoder_arm64_decode(random_instruction); - - if (NULL != meta) - { - if ((random_instruction & meta->mask) != meta->expected) - { - printf("[FAIL] %s, 0x%08x & 0x%08x != 0x%08x", - meta->name, - random_instruction, - meta->mask, - meta->expected); - ++failed; - continue; - } - ++passed; - } - } - - if (failed > 0) - { - printf("FAILED %d tests. \n", failed); - return 1; - } - if (passed > 0) - { - printf("Decoded %d instructions successfully\n", passed); - return 0; - } - else - { - printf("[ERROR] No tests passed or failed. Ballistic's decoding logic is flawed\n"); - } -}