This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 5d53a3f9cdfeea9e40ed218aba9d4e36ff206545 Author: Andreas Rheinhardt <[email protected]> AuthorDate: Sun Aug 9 16:24:45 2026 +0200 Commit: Andreas Rheinhardt <[email protected]> CommitDate: Sun Aug 9 16:24:45 2026 +0200 Squashed 'tests/checkasm/ext/' changes from e13b0bb3ff..e822e429f3 e822e429f3 utils: Silence an MSVC warning about conversion from double to float abf8fb0261 utils: Add checkasm_randomize_interval() and float equivalent 7aea236f8f utils: Simplify checkasm_randomize_range() formulation 49972c1c72 utils: Fix advertised value range of checkasm_randf() 11c569cbdc utils: Use appropriate PRNG size for checkasm_init() 6fd9bafd11 utils: Add way more PRNG helper functions d7f10858c8 utils: Use prng() primitive for checkasm_randomize() 781f16f1ac utils: Parallelize PRNG 4681280b65 selftest: Add tests for utils.h functions 02d0b1ee4c Clear CPU state after any checkasm_call() 48821c5d14 Print cpu mask in JSON output 5d500889dc tests/selftest: Test CPU flag masking dbf5380202 tests: Offset arch-specific CPU flags by 10 09b4c6a8c9 include/checkasm.h: Add CheckasmCpuInfo.mask dd1ca4ed09 utils: Document PRNG re-seeding at the start of each test c93961a221 utils: Use substantially more robust PRNG e46c473f73 Re-order conditional (mostly cosmetic) 16738f028d Seed PRNG before running CheckasmTest.init() 9292f9cf6d Bikeshed summary line a bit more, especially for interrupted runs 0f03612ec9 Suppress non-failure output after first iteration bfdb230dd9 checkasm: Print statusline to track current test/bench progress c8996a284b checkasm: Move test iteration variable to global state 46e136ee0e utils: Add self-repainting statusline buffer 800053edca Route all log messages through checkasm_fprintf() f6791fee59 utils: Redefine checkasm_fprintf() as checkasm_vfprintf() 629a211feb Add optional CheckasmTest.init() and uninit() 354f3d3e8d riscv/callcheck: Avoid out-of-range li immediate under LLVM on rv32 6e1b4cd6ca riscv/cpu: Include <asm/unistd.h> for __NR_riscv_hwprobe 8130043b32 Fix CPU detection for AVX512F 9760810b49 Support YMM copy tests on processors with only AVX dc8c320313 Support XMM copy tests on processors with only SSE git-subtree-dir: tests/checkasm/ext git-subtree-split: e822e429f33e4d02e0815bd497952b3f6deb0a7d --- ext-build-test.make | 1 + include/checkasm/checkasm.h | 28 +++++- include/checkasm/test.h | 1 + include/checkasm/utils.h | 78 +++++++++++++++ src/checkasm.c | 193 ++++++++++++++++++++++++------------ src/internal.h | 21 +++- src/riscv/callcheck.S | 2 +- src/riscv/cpu.c | 1 + src/utils.c | 234 +++++++++++++++++++++++++++++++++++--------- src/x86/cpu.c | 2 +- tests/meson.build | 1 + tests/selftest.c | 39 +++++++- tests/tests.h | 30 +++--- tests/utils.c | 187 +++++++++++++++++++++++++++++++++++ tests/x86/tests.c | 33 +++---- tests/x86/tests_asm.asm | 10 +- 16 files changed, 707 insertions(+), 154 deletions(-) diff --git a/ext-build-test.make b/ext-build-test.make index 3bea491490..c3a0deed92 100644 --- a/ext-build-test.make +++ b/ext-build-test.make @@ -31,6 +31,7 @@ OBJS = \ src/utils.o \ tests/selftest.o \ tests/generic.o \ + tests/utils.o \ tests/arm/32/tests.o \ tests/arm/32/tests_asm.o \ tests/arm/64/tests.o \ diff --git a/include/checkasm/checkasm.h b/include/checkasm/checkasm.h index fc40fab4cd..c97785e8f5 100644 --- a/include/checkasm/checkasm.h +++ b/include/checkasm/checkasm.h @@ -106,6 +106,16 @@ typedef struct CheckasmCpuInfo { const char *name; /**< Human-readable name (e.g., "SSE2", "AVX2") */ const char *suffix; /**< Short suffix for function names (e.g., "sse2", "avx2") */ CheckasmCpu flag; /**< Bitmask flag value for this CPU feature */ + + /** @brief Bitmask of prior CPU flags to disable again + * + * Any CPU flags in this mask will be removed from the active CPU flag set + * when testing this and any subsequent CPU configurations. This is + * effectively the opposite of `flag`. + * + * @since v1.3.0 + */ + CheckasmCpu mask; } CheckasmCpuInfo; /** @@ -117,6 +127,20 @@ typedef struct CheckasmCpuInfo { typedef struct CheckasmTest { const char *name; /**< Name of the test (used for filtering and reporting) */ void (*func)(void); /**< Test function to invoke */ + + /** + * @brief Optional initialization function + * + * These functions, if set, are invoked before testing any CPU flags + * and after testing all CPU flags, respectively. + * + * @note If CheckasmConfig.repeat > 1, they will be called for every + * iteration. + * + * @warning These are called even when running checkasm_list_functions(). + */ + void (*init)(void); + void (*uninit)(void); } CheckasmTest; /** @@ -362,7 +386,9 @@ CHECKASM_API void checkasm_list_tests(const CheckasmConfig *config); * @note This requires executing all tests to gather information about the * available functions. During this process, checkasm_check_func() always * returns 0 to skip the actual testing. However, any side effects from - * test functions will still occur, unless properly guarded. + * test functions will still occur, unless properly guarded. In + * particular, CheckasmTest.init() and CheckasmTest.uninit() will still + * be executed. */ CHECKASM_API void checkasm_list_functions(const CheckasmConfig *config); diff --git a/include/checkasm/test.h b/include/checkasm/test.h index d04510c15b..f0bba446b2 100644 --- a/include/checkasm/test.h +++ b/include/checkasm/test.h @@ -251,6 +251,7 @@ CHECKASM_API void checkasm_report(const char *name, ...) CHECKASM_PRINTF(1, 2); */ #define checkasm_call(func, ...) \ (checkasm_set_signal_handler_state(1), (func) (__VA_ARGS__)); \ + checkasm_clear_cpu_state(); \ checkasm_set_signal_handler_state(0) /** diff --git a/include/checkasm/utils.h b/include/checkasm/utils.h index f55a9b16a9..518b4e84e2 100644 --- a/include/checkasm/utils.h +++ b/include/checkasm/utils.h @@ -51,6 +51,12 @@ * * These functions use the seed specified in CheckasmConfig (or a time-based * seed if not specified) to generate deterministic, reproducible random values. + * + * @note The PRNG state is automatically re-seeded at the start of each test + * case (i.e. before each call to CheckasmTest.func), so that different CPU + * flags see the same sequence of random numbers, at least until control flow + * inside the test function diverges. + * * @{ */ @@ -66,18 +72,70 @@ CHECKASM_API int checkasm_rand(void); */ CHECKASM_API double checkasm_randf(void); +/** + * @brief Generate a random 8-bit unsigned integer + * @return Random value in range [0, UINT8_MAX] + */ +CHECKASM_API uint8_t checkasm_rand_uint8(void); + +/** + * @brief Generate a random 16-bit unsigned integer + * @return Random value in range [0, UINT16_MAX] + */ +CHECKASM_API uint16_t checkasm_rand_uint16(void); + /** * @brief Generate a random 32-bit unsigned integer * @return Random value in range [0, UINT32_MAX] */ CHECKASM_API uint32_t checkasm_rand_uint32(void); +/** + * @brief Generate a random 64-bit unsigned integer + * @return Random value in range [0, UINT64_MAX] + */ +CHECKASM_API uint64_t checkasm_rand_uint64(void); + +/** + * @brief Generate a random 8-bit signed integer + * @return Random value in range [INT8_MIN, INT8_MAX] + */ +CHECKASM_API int8_t checkasm_rand_int8(void); + +/** + * @brief Generate a random 16-bit signed integer + * @return Random value in range [INT16_MIN, INT16_MAX] + */ +CHECKASM_API int16_t checkasm_rand_int16(void); + /** * @brief Generate a random 32-bit signed integer * @return Random value in range [INT32_MIN, INT32_MAX] */ CHECKASM_API int32_t checkasm_rand_int32(void); +/** + * @brief Generate a random 64-bit signed integer + * @return Random value in range [INT64_MIN, INT64_MAX] + */ +CHECKASM_API int64_t checkasm_rand_int64(void); + +/** + * @brief Generate a truly random 32-bit float. + * @return Random float value, drawn from the space of all possible float + * representations, including every possible NaN, Infinity etc. + * @see checkasm_randf(), checkasm_rand_norm(), checkasm_rand_dist() + */ +CHECKASM_API float checkasm_rand_float32(void); + +/** + * @brief Generate a truly random 64-bit float. + * @return Random float value, drawn from the space of all possible float + * representations, including every possible NaN, Infinity etc. + * @see checkasm_randf(), checkasm_rand_norm(), checkasm_rand_dist() + */ +CHECKASM_API double checkasm_rand_float64(void); + /** @} */ /* rng */ /** @@ -160,6 +218,26 @@ CHECKASM_API void checkasm_randomize_range(double *buf, int width, double range) */ CHECKASM_API void checkasm_randomize_rangef(float *buf, int width, float range); +/** + * @brief Fill a double buffer with random values chosen uniformly from an interval + * @param[out] buf Buffer to fill + * @param[in] width Number of elements to randomize + * @param[in] low Inclusive lower bound on value + * @param[in] high Inclusive upper bound on value + */ +CHECKASM_API void checkasm_randomize_interval(double *buf, int width, double low, + double high); + +/** + * @brief Fill a float buffer with random values chosen uniformly from an interval + * @param[out] buf Buffer to fill + * @param[in] width Number of elements to randomize + * @param[in] low Inclusive lower bound on value + * @param[in] high Inclusive upper bound on value + */ +CHECKASM_API void checkasm_randomize_intervalf(float *buf, int width, float low, + float high); + /** * @brief Fill a double buffer with normally distributed random values * @param[out] buf Buffer to fill diff --git a/src/checkasm.c b/src/checkasm.c index e10cfa23eb..a47308deac 100644 --- a/src/checkasm.c +++ b/src/checkasm.c @@ -108,6 +108,7 @@ static struct { /* Miscellaneous global state (cosmetic) */ int max_function_name_length; int max_report_name_length; + unsigned test_iter; /* Timing code measurements (aggregated over multiple trials) */ CheckasmMeasurement nop_cycles; @@ -210,6 +211,21 @@ static void cpu_info_json(void *priv, const char *fmt, ...) checkasm_json_str(json, NULL, buf); } +static void cpu_mask_json(CheckasmJson *json, const CheckasmConfig cfg, + const CheckasmCpu cpu_mask) +{ + if (!cpu_mask) + return; + + checkasm_json_push(json, "mask", '['); + for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) { + if ((cpu_mask & info->flag) != info->flag) + continue; + checkasm_json_str(json, NULL, info->suffix); + } + checkasm_json_pop(json, ']'); +} + struct IterState { const char *test; const char *report; @@ -277,6 +293,7 @@ static void print_bench_header(struct IterState *const iter) checkasm_json_push(json, info->suffix, '{'); checkasm_json_str(json, "name", info->name); checkasm_json(json, "available", available ? "true" : "false"); + cpu_mask_json(json, cfg, info->mask); checkasm_json_pop(json, '}'); } checkasm_json_pop(json, '}'); /* close cpuFlags */ @@ -523,6 +540,12 @@ static int wildstrcmp(const char *str, const char *pattern) } static void handle_interrupt(void); +static void update_statusline(void); + +static int test_enabled(const CheckasmTest *test) +{ + return !cfg.test_pattern || !wildstrcmp(test->name, cfg.test_pattern); +} /* Perform tests and benchmarks for the specified * cpu flag if supported by the host */ @@ -530,6 +553,7 @@ static void check_cpu_flag(const CheckasmCpuInfo *cpu) { const CheckasmCpu prev_cpu_flags = current.cpu_flags; if (cpu) { + current.cpu_flags &= ~cpu->mask; current.cpu_flags |= cpu->flag & cfg.cpu; } else { /* Also include any CPU flags not related to the CPU flags list */ @@ -550,9 +574,10 @@ static void check_cpu_flag(const CheckasmCpuInfo *cpu) cfg.set_cpu_flags(current.cpu_flags); for (const CheckasmTest *test = cfg.tests; test->func; test++) { - if (cfg.test_pattern && wildstrcmp(test->name, cfg.test_pattern)) + if (!test_enabled(test)) continue; current.test_name = test->name; + update_statusline(); if (checkasm_save_context(checkasm_context)) { const char *signal = checkasm_get_last_signal_desc(); @@ -592,8 +617,7 @@ static void check_cpu_flag(const CheckasmCpuInfo *cpu) static void print_cpu_name(void) { if (!current.cpu_name_printed) { - checkasm_fprintf(stderr, COLOR_YELLOW, "%s:\n", - current.cpu ? current.cpu->name : "C"); + LOG_COLOR(COLOR_YELLOW, "%s:\n", current.cpu ? current.cpu->name : "C"); current.cpu_name_printed = 1; } } @@ -652,15 +676,15 @@ static int set_cpu_affinity(const unsigned affinity) #else (void) affinity; (void) affinity_err; - fprintf(stderr, "checkasm: --affinity is not supported on your system\n"); + LOG("checkasm: --affinity is not supported on your system\n"); return 1; #endif if (affinity_err) { - fprintf(stderr, "checkasm: invalid cpu affinity (%u)\n", affinity); + LOG("checkasm: invalid cpu affinity (%u)\n", affinity); return 1; } else { - fprintf(stderr, "checkasm: running on cpu %u\n", affinity); + LOG("checkasm: running on cpu %u\n", affinity); return 0; } } @@ -697,6 +721,25 @@ static void print_functions(const CheckasmFunc *const f) } } +static void run_all_tests(void) +{ + for (const CheckasmTest *test = cfg.tests; test->func; test++) { + if (test->init && test_enabled(test)) { + checkasm_srand(cfg.seed); + test->init(); + } + } + + check_cpu_flag(NULL); + for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) + check_cpu_flag(info); + + for (const CheckasmTest *test = cfg.tests; test->func; test++) { + if (test->uninit && test_enabled(test)) + test->uninit(); + } +} + void checkasm_list_functions(const CheckasmConfig *config) { memset(&state, 0, sizeof(state)); @@ -704,9 +747,7 @@ void checkasm_list_functions(const CheckasmConfig *config) state.skip_tests = 1; cfg = *config; - check_cpu_flag(NULL); - for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) - check_cpu_flag(info); + run_all_tests(); print_functions(current.tree.root); checkasm_func_tree_uninit(¤t.tree); @@ -726,45 +767,79 @@ static void cpu_fprintf(void *priv, const char *fmt, ...) static COLD void print_info(void) { - checkasm_fprintf(stderr, COLOR_YELLOW, "checkasm:\n"); + LOG_COLOR(COLOR_YELLOW, "checkasm:\n"); checkasm_cpu_info(cpu_fprintf, stderr, &cfg); if (cfg.bench) { - fprintf(stderr, " - Timing source: %s\n", checkasm_perf.name); + LOG(" - Timing source: %s\n", checkasm_perf.name); if (cfg.verbose) { const CheckasmVar perf_scale = checkasm_measurement_result(state.perf_scale); const CheckasmVar nop_cycles = checkasm_measurement_result(state.nop_cycles); const CheckasmVar mhz = checkasm_var_div(checkasm_var_const(1e3), perf_scale); - fprintf(stderr, - " - Timing resolution: %.4f +/- %.3f ns/%s (%.0f +/- %.1f " - "MHz) (provisional)\n", - checkasm_mode(perf_scale), checkasm_stddev(perf_scale), - checkasm_perf.unit, checkasm_mode(mhz), checkasm_stddev(mhz)); - - fprintf(stderr, - " - No-op overhead: %.2f +/- %.3f %ss per call (provisional)\n", - checkasm_mode(nop_cycles), checkasm_stddev(nop_cycles), - checkasm_perf.unit); + LOG(" - Timing resolution: %.4f +/- %.3f ns/%s (%.0f +/- %.1f " + "MHz) (provisional)\n", + checkasm_mode(perf_scale), checkasm_stddev(perf_scale), + checkasm_perf.unit, checkasm_mode(mhz), checkasm_stddev(mhz)); + + LOG(" - No-op overhead: %.2f +/- %.3f %ss per call (provisional)\n", + checkasm_mode(nop_cycles), checkasm_stddev(nop_cycles), + checkasm_perf.unit); } - fprintf(stderr, " - Bench duration: %d µs per function (%" PRIu64 " %ss)\n", - cfg.bench_usec, state.target_cycles, checkasm_perf.unit); + LOG(" - Bench duration: %d µs per function (%" PRIu64 " %ss)\n", cfg.bench_usec, + state.target_cycles, checkasm_perf.unit); } - fprintf(stderr, " - Random seed: %u\n", cfg.seed); + LOG(" - Random seed: %u\n", cfg.seed); } -static int print_summary(void) +static void update_statusline(void) { + if (state.skip_tests) + return; + + char status[256]; + int len = 0; + + len += snprintf(status, sizeof(status), "checkasm: iter=%d/%d cpu=%s test=%s", + state.test_iter + 1, cfg.repeat, cpu_suffix(current.cpu), + current.test_name); + + if (current.func && current.func->report_name) { + snprintf(status + len, sizeof(status) - len, " func=%s", + current.func->report_name); + } + + checkasm_statusline(status); +} + +static int print_summary(int interrupted) +{ + checkasm_statusline(NULL); + + LOG("checkasm: "); + if (interrupted) + LOG_COLOR(COLOR_BLUE, "(interrupted) "); + /* Exclude C/ref versions from count reported to user */ const int num_checked_asm = current.num_checked - current.num_funcs; if (current.num_failed) { - fprintf(stderr, "checkasm: %d of %d tests failed\n", current.num_failed, - num_checked_asm); + LOG_COLOR(COLOR_RED, "%d ", current.num_failed); + LOG("of %d tests failed", num_checked_asm); } else if (num_checked_asm) { - fprintf(stderr, "checkasm: all %d tests passed\n", num_checked_asm); + if (!interrupted) + LOG("all "); + LOG("%d tests passed", num_checked_asm); + } else if (interrupted) { + LOG("no tests performed"); } else { - fprintf(stderr, "checkasm: no tests to perform\n"); + /* User did not define any tests */ + LOG_COLOR(COLOR_YELLOW, "no tests to perform"); } + if (cfg.repeat > 1) + LOG(", iteration %d of %d, seed %u\n", state.test_iter + 1, cfg.repeat, cfg.seed); + else + LOG("\n"); + if (current.num_benched && !current.num_failed) print_benchmarks(); @@ -774,8 +849,7 @@ static int print_summary(void) static void handle_interrupt(void) { if (checkasm_interrupted) { - fprintf(stderr, "checkasm: interrupted\n"); - print_summary(); + print_summary(1); exit(128 + checkasm_interrupted); } } @@ -784,7 +858,7 @@ int checkasm_run(const CheckasmConfig *config) { #if !HAVE_HTML_DATA if (cfg.format == CHECKASM_FORMAT_HTML) { - fprintf(stderr, "checkasm: built without HTML support\n"); + LOG("checkasm: built without HTML support\n"); return 1; } #endif @@ -837,17 +911,10 @@ int checkasm_run(const CheckasmConfig *config) print_info(); - for (unsigned i = 0; i < cfg.repeat; i++) { - if (i > 0) { - checkasm_fprintf(stderr, COLOR_YELLOW, "\nTest #%d:\n", i + 1); - fprintf(stderr, " - Random seed: %u\n", cfg.seed); - } - - check_cpu_flag(NULL); - for (const CheckasmCpuInfo *info = cfg.cpu_flags; info->flag; info++) - check_cpu_flag(info); + for (state.test_iter = 0; state.test_iter < cfg.repeat; state.test_iter++) { + run_all_tests(); - int res = print_summary(); + int res = print_summary(0); checkasm_func_tree_uninit(¤t.tree); if (res) return res; @@ -949,6 +1016,7 @@ CheckasmKey checkasm_check_key(const CheckasmKey version, const char *const name current.func_ver = v; current.num_checked++; checkasm_srand(cfg.seed); + update_statusline(); if (cfg.bench) { #if ARCH_X86 @@ -982,8 +1050,8 @@ static int fail_internal(const char *const msg, va_list arg) if (v && v->state == CHECKASM_FUNC_OK) { if (!current.should_fail) { print_cpu_name(); - checkasm_fprintf(stderr, COLOR_RED, "FAILURE:"); - fprintf(stderr, " %s_%s (", current.func->name, ver_suffix(v)); + LOG_COLOR(COLOR_RED, "FAILURE:"); + LOG(" %s_%s (", current.func->name, ver_suffix(v)); vfprintf(stderr, msg, arg); fputs(")\n", stderr); } @@ -1054,28 +1122,27 @@ void checkasm_report(const char *const name, ...) if (current.should_fail) current.num_failed = current.prev_failed + (new_checked - fails); - /* Omit "OK" for non-verbose non-benchmark C function successes */ - const int want_print = current.num_failed != current.prev_failed - || current.should_fail || cfg.verbose || cfg.bench - || current.cpu; + /* Omit 'OK' after the first run, unless failed or verbose */ + int want_print = current.num_failed != current.prev_failed || cfg.verbose; + if (state.test_iter == 0) + want_print |= current.should_fail || current.cpu; if (want_print) { print_cpu_name(); if (name) { - pad_length -= fprintf(stderr, " - %s.%s", current.test_name, report_name); + pad_length -= LOG(" - %s.%s", current.test_name, report_name); } else { - pad_length -= fprintf(stderr, " - %s", current.test_name); + pad_length -= LOG(" - %s", current.test_name); } - fprintf(stderr, "%*c", imax(pad_length, 0) + 2, '['); + LOG("%*c", imax(pad_length, 0) + 2, '['); - if (current.num_failed == current.prev_failed) { - checkasm_fprintf(stderr, COLOR_GREEN, - current.should_fail ? "EXPECTED" : "OK"); - } else if (!current.should_fail) - checkasm_fprintf(stderr, COLOR_RED, "FAILED"); + if (current.num_failed == current.prev_failed) + LOG_COLOR(COLOR_GREEN, current.should_fail ? "EXPECTED" : "OK"); + else if (!current.should_fail) + LOG_COLOR(COLOR_RED, "FAILED"); else - checkasm_fprintf(stderr, COLOR_RED, "%d/%d EXPECTED", fails, new_checked); - fprintf(stderr, "]\n"); + LOG_COLOR(COLOR_RED, "%d/%d EXPECTED", fails, new_checked); + LOG("]\n"); } current.prev_checked = current.num_checked; @@ -1170,13 +1237,13 @@ int checkasm_main(CheckasmConfig *config, int argc, const char *argv[]) #if HAVE_HTML_DATA config->format = CHECKASM_FORMAT_HTML; #else - fprintf(stderr, "checkasm: built without HTML support\n"); + LOG("checkasm: built without HTML support\n"); return 1; #endif } else if (!strncmp(argv[1], "--duration=", 11)) { const char *const s = argv[1] + 11; if (!parseu(&config->bench_usec, s, 10)) { - fprintf(stderr, "checkasm: invalid duration (%s)\n", s); + LOG("checkasm: invalid duration (%s)\n", s); print_usage(argv[0]); return 1; } @@ -1198,14 +1265,14 @@ int checkasm_main(CheckasmConfig *config, int argc, const char *argv[]) const char *const s = argv[1] + 11; config->cpu_affinity_set = 1; if (!parseu(&config->cpu_affinity, s, 16)) { - fprintf(stderr, "checkasm: invalid cpu affinity (%s)\n", s); + LOG("checkasm: invalid cpu affinity (%s)\n", s); print_usage(argv[0]); return 1; } } else if (!strncmp(argv[1], "--repeat=", 9)) { const char *const s = argv[1] + 9; if (!parseu(&config->repeat, s, 10)) { - fprintf(stderr, "checkasm: invalid number of repetitions (%s)\n", s); + LOG("checkasm: invalid number of repetitions (%s)\n", s); print_usage(argv[0]); return 1; } @@ -1214,7 +1281,7 @@ int checkasm_main(CheckasmConfig *config, int argc, const char *argv[]) } else { config->seed_set = 1; if (!parseu(&config->seed, argv[1], 10)) { - fprintf(stderr, "checkasm: unknown option (%s)\n", argv[1]); + LOG("checkasm: unknown option (%s)\n", argv[1]); print_usage(argv[0]); return 1; } diff --git a/src/internal.h b/src/internal.h index dd698cbc84..6192ede221 100644 --- a/src/internal.h +++ b/src/internal.h @@ -109,8 +109,25 @@ NORETURN void checkasm_fail_abort(const char *msg, ...) CHECKASM_PRINTF(1, 2); /* Colored variant of fprintf for terminals that support it */ void checkasm_setup_fprintf(void); -void checkasm_fprintf(FILE *const f, const int color, const char *const fmt, ...) - CHECKASM_PRINTF(3, 4); +int checkasm_vfprintf(FILE *const f, int color, const char *fmt, va_list arg) + CHECKASM_PRINTF(3, 0); + +CHECKASM_PRINTF(3, 4) +static inline int checkasm_fprintf(FILE *const f, int color, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int ret = checkasm_vfprintf(f, color, fmt, ap); + va_end(ap); + return ret; +} + +#define LOG_COLOR(...) checkasm_fprintf(stderr, __VA_ARGS__) +#define LOG(...) LOG_COLOR(COLOR_DEFAULT, __VA_ARGS__) + +/* Update the statusline, reprinted automatically as needed. Pass NULL or an + * empty string ("") to clear it entirely. Maximum 256 characters */ +void checkasm_statusline(const char *status); /* Light-weight helper for printing nested JSON objects */ typedef struct CheckasmJson { diff --git a/src/riscv/callcheck.S b/src/riscv/callcheck.S index 2fa6d3a873..2fad998eec 100644 --- a/src/riscv/callcheck.S +++ b/src/riscv/callcheck.S @@ -312,7 +312,7 @@ function checked_call_ifv, export=1, ext=zve32x .Lclobber_v: # Clobber the vector registers vsetvli t1, zero, e32, m8, ta, ma - li t1, -0xdeadbeef + li t1, 0x0badf00d vmv.v.x v0, t1 vmv.v.x v8, t1 vmv.v.x v16, t1 diff --git a/src/riscv/cpu.c b/src/riscv/cpu.c index 9248babedd..4f3121086c 100644 --- a/src/riscv/cpu.c +++ b/src/riscv/cpu.c @@ -44,6 +44,7 @@ #include <sys/hwprobe.h> #elif HAVE_ASM_HWPROBE_H #include <asm/hwprobe.h> + #include <asm/unistd.h> #include <sys/syscall.h> #include <unistd.h> diff --git a/src/utils.c b/src/utils.c index 768d3028ef..03c2e71186 100644 --- a/src/utils.c +++ b/src/utils.c @@ -119,41 +119,130 @@ unsigned checkasm_seed(void) return (unsigned) gettime_nsec(1); } -// xor128 from Marsaglia, George (July 2003). "Xorshift RNGs". -// Journal of Statistical Software. 8 (14). -// doi:10.18637/jss.v008.i14. -static uint32_t xs_state[4]; +// (parallel) xoshiro128++ from https://prng.di.unimi.it/ +typedef struct CheckasmRand { +#define CHECKASM_PRNG_NUM 4 + uint32_t s0[CHECKASM_PRNG_NUM]; + uint32_t s1[CHECKASM_PRNG_NUM]; + uint32_t s2[CHECKASM_PRNG_NUM]; + uint32_t s3[CHECKASM_PRNG_NUM]; +} CheckasmRand; -void checkasm_srand(unsigned seed) +static CheckasmRand checkasm_prng; + +static ALWAYS_INLINE uint32_t rotl(const uint32_t x, int k) { - xs_state[0] = seed; - xs_state[1] = (seed & 0xffff0000) | (~seed & 0x0000ffff); - xs_state[2] = (~seed & 0xffff0000) | (seed & 0x0000ffff); - xs_state[3] = ~seed; + return (x << k) | (x >> (32 - k)); } -uint32_t checkasm_rand_uint32(void) +/* Single round of a parallel xoshiro128++, generates a full block */ +static ALWAYS_INLINE void xoshiro128pp(CheckasmRand *restrict xs, uint32_t *restrict buf) { - const uint32_t x = xs_state[0]; - const uint32_t t = x ^ (x << 11); + for (int i = 0; i < CHECKASM_PRNG_NUM; i++) { + buf[i] = rotl(xs->s0[i] + xs->s3[i], 7) + xs->s0[i]; - xs_state[0] = xs_state[1]; - xs_state[1] = xs_state[2]; - xs_state[2] = xs_state[3]; - uint32_t w = xs_state[3]; + const uint32_t t = xs->s1[i] << 9; + xs->s2[i] ^= xs->s0[i]; + xs->s3[i] ^= xs->s1[i]; + xs->s1[i] ^= xs->s2[i]; + xs->s0[i] ^= xs->s3[i]; + xs->s2[i] ^= t; + xs->s3[i] = rotl(xs->s3[i], 11); + } +} - return xs_state[3] = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); +static void prng(CheckasmRand *restrict xs, uint8_t *restrict buf, size_t size) +{ + uint32_t tmp[CHECKASM_PRNG_NUM]; + const size_t block_size = sizeof(tmp); + CheckasmRand xs_copy = *xs; + + while (size >= block_size) { + xoshiro128pp(&xs_copy, tmp); + memcpy(buf, tmp, block_size); + buf += block_size; + size -= block_size; + } + + if (size) { + xoshiro128pp(&xs_copy, tmp); + memcpy(buf, tmp, size); + } + + *xs = xs_copy; } -int32_t checkasm_rand_int32(void) +/* Efficient wrapper for generating individual random integers, by caching + * the result of a single call to the underlying generator() */ +static struct { + #define PRNG_CACHE_SIZE 64 + uint8_t buf8 [PRNG_CACHE_SIZE]; + uint16_t buf16[PRNG_CACHE_SIZE >> 1]; + uint32_t buf32[PRNG_CACHE_SIZE >> 2]; + uint64_t buf64[PRNG_CACHE_SIZE >> 3]; + int num8; + int num16; + int num32; + int num64; +} prng_cache; + +static_assert(PRNG_CACHE_SIZE % sizeof(uint32_t[CHECKASM_PRNG_NUM]) == 0, + "PRNG_CACHE_SIZE should be a multiple of uint32_t[CHECKASM_PRNG_NUM]"); + +#define DEF_CHECKASM_RAND(BITS, TYPE, NAME) \ + TYPE checkasm_rand_##NAME(void) \ + { \ + if (!prng_cache.num##BITS) { \ + prng(&checkasm_prng, (uint8_t *) prng_cache.buf##BITS, \ + sizeof(prng_cache.buf##BITS)); \ + prng_cache.num##BITS = ARRAY_SIZE(prng_cache.buf##BITS); \ + } \ + \ + union { \ + TYPE type; \ + uint##BITS##_t raw; \ + } val; \ + val.raw = prng_cache.buf##BITS[--prng_cache.num##BITS]; \ + return val.type; \ + } + +DEF_CHECKASM_RAND(8, int8_t, int8) +DEF_CHECKASM_RAND(8, uint8_t, uint8) +DEF_CHECKASM_RAND(16, int16_t, int16) +DEF_CHECKASM_RAND(16, uint16_t, uint16) +DEF_CHECKASM_RAND(32, int32_t, int32) +DEF_CHECKASM_RAND(32, uint32_t, uint32) +DEF_CHECKASM_RAND(32, float, float32) +DEF_CHECKASM_RAND(64, int64_t, int64) +DEF_CHECKASM_RAND(64, uint64_t, uint64) +DEF_CHECKASM_RAND(64, double, float64) + +static inline uint64_t splitmix64(uint64_t *state) +{ + uint64_t z = (*state += 0x9e3779b97f4a7c15); + + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +void checkasm_srand(unsigned seed) { - union { - uint32_t u; - int32_t i; - } res; + /* Seed using splitmix64() as recommended by xoroshiro128 authors */ + uint64_t s = seed; + + for (int i = 0; i < CHECKASM_PRNG_NUM; i++) { + const uint64_t a = splitmix64(&s); + const uint64_t b = splitmix64(&s); + + checkasm_prng.s0[i] = (uint32_t) a; + checkasm_prng.s1[i] = (uint32_t) b; + checkasm_prng.s2[i] = a >> 32; + checkasm_prng.s3[i] = b >> 32; + } - res.u = checkasm_rand_uint32(); - return res.i; + /* discard cached random bytes */ + prng_cache.num8 = prng_cache.num16 = prng_cache.num32 = prng_cache.num64 = 0; } int checkasm_rand(void) @@ -164,7 +253,7 @@ int checkasm_rand(void) double checkasm_randf(void) { - return checkasm_rand_uint32() / (double) UINT32_MAX; + return checkasm_rand_uint32() / (UINT32_MAX + 1.0); } /* Marsaglia polar method */ @@ -198,35 +287,51 @@ double checkasm_rand_dist(CheckasmDist dist) return dist.mean + dist.stddev * checkasm_rand_norm(); } -void checkasm_randomize(void *bufp, size_t bytes) +void checkasm_randomize(void *buf, size_t bytes) { - uint8_t *buf = bufp; - while (bytes--) - *buf++ = checkasm_rand_uint32() & 0xFF; + prng(&checkasm_prng, buf, bytes); } void checkasm_randomize_mask8(uint8_t *buf, int width, uint8_t mask) { - while (width--) - *buf++ = checkasm_rand_uint32() & mask; + prng(&checkasm_prng, (uint8_t *) buf, width * sizeof(*buf)); + for (int i = 0; i < width; i++) + buf[i] &= mask; } void checkasm_randomize_mask16(uint16_t *buf, int width, uint16_t mask) { - while (width--) - *buf++ = checkasm_rand_uint32() & mask; + prng(&checkasm_prng, (uint8_t *) buf, width * sizeof(*buf)); + for (int i = 0; i < width; i++) + buf[i] &= mask; } void checkasm_randomize_range(double *buf, int width, double range) { + const double scale = range / (UINT32_MAX + 1.0); while (width--) - *buf++ = checkasm_randf() * range; + *buf++ = scale * checkasm_rand_uint32(); } void checkasm_randomize_rangef(float *buf, int width, float range) { + const float scale = (float) (range / (UINT32_MAX + 1.0)); + while (width--) + *buf++ = scale * checkasm_rand_uint32(); +} + +void checkasm_randomize_interval(double *buf, int width, double low, double high) +{ + const double scale = (high - low) / (double) UINT32_MAX; while (width--) - *buf++ = (float) (checkasm_randf() * range); + *buf++ = scale * checkasm_rand_uint32() + low; +} + +void checkasm_randomize_intervalf(float *buf, int width, float low, float high) +{ + const float scale = (high - low) / (float) UINT32_MAX; + while (width--) + *buf++ = scale * checkasm_rand_uint32() + low; } #define RANDOMIZE_DIST(buf, ftype, width, mean, stddev) \ @@ -338,21 +443,21 @@ void checkasm_init(void *buf, size_t bytes) for (int i = 0; i < width; i++, step--) { \ if (!step) { \ step = imax(shift_rand(width), 1); \ - mode = checkasm_rand() & 7; \ + mode = checkasm_rand_uint8() & 7; \ mask = shift_rand(mask_pixel); \ } \ \ - const PIXEL low = checkasm_rand_uint32() & mask; \ + const PIXEL low = checkasm_rand_uint##BITS() & mask; \ const PIXEL high = mask_pixel - low; \ switch (mode) { \ case PAT_ZERO: buf[i] = 0; break; \ case PAT_ONE: buf[i] = mask_pixel; break; \ - case PAT_RAND: buf[i] = checkasm_rand_uint32() & mask_pixel; break; \ + case PAT_RAND: buf[i] = checkasm_rand_uint##BITS() & mask_pixel; break; \ case PAT_LOW: buf[i] = low; break; \ case PAT_HIGH: buf[i] = high; break; \ case PAT_ALTLO: buf[i] = (i & 1) ? high : low; break; \ case PAT_ALTHI: buf[i] = (i & 1) ? low : high; break; \ - case PAT_MIX: buf[i] = (checkasm_rand() & 1) ? low : high; break; \ + case PAT_MIX: buf[i] = (checkasm_rand_uint8() & 1) ? low : high; break; \ } \ } \ } @@ -361,22 +466,57 @@ DEF_CHECKASM_INIT_MASK(8, uint8_t) DEF_CHECKASM_INIT_MASK(16, uint16_t) static int use_printf_color[2]; +static char statusline[256]; +static int statusline_visible; /* Print colored text to stderr if the terminal supports it */ -void checkasm_fprintf(FILE *const f, const int color, const char *const fmt, ...) +int checkasm_vfprintf(FILE *const f, const int color, const char *const fmt, va_list arg) { - va_list arg; - int use_color = use_printf_color[f == stderr]; + size_t fmt_len = strlen(fmt); + int use_color = use_printf_color[f == stderr]; + if (!use_color || !fmt_len) + return vfprintf(f, fmt, arg); + + if (f == stderr && statusline_visible) { + fprintf(f, "\r\033[K"); /* clear line */ + statusline_visible = 0; + } - if (color >= 0 && use_color) + if (color >= 0) fprintf(f, "\x1b[0;%dm", color); - va_start(arg, fmt); - vfprintf(f, fmt, arg); - va_end(arg); + int ret = vfprintf(f, fmt, arg); - if (color >= 0 && use_color) + if (color >= 0) fprintf(f, "\x1b[0m"); + + if (f == stderr && statusline[0] && fmt[fmt_len - 1] == '\n') { + fprintf(f, "%s", statusline); + statusline_visible = 1; + } + + return ret; +} + +void checkasm_statusline(const char *status) +{ + if (!status) + status = ""; + + if (!use_printf_color[1] || !strcmp(statusline, status)) + return; /* don't re-paint unchanged status */ + + snprintf(statusline, sizeof(statusline), "%s", status); + + if (statusline_visible) { + fprintf(stderr, "\r\033[K"); + statusline_visible = 0; + } + + if (statusline[0]) { + fprintf(stderr, "%s", statusline); + statusline_visible = 1; + } } static COLD int should_use_color(FILE *const f) diff --git a/src/x86/cpu.c b/src/x86/cpu.c index 62fa4c55a0..f404204072 100644 --- a/src/x86/cpu.c +++ b/src/x86/cpu.c @@ -167,7 +167,7 @@ static COLD checkasm_simd_warmup_func get_simd_warmup(void) return simd_warmup; checkasm_cpu_cpuid(&r, 7, 0); - if (r.ebx & 0x00000020) /* AVX512F */ + if (r.ebx & 0x00010000) /* AVX512F */ simd_warmup = checkasm_warmup_avx512; return simd_warmup; diff --git a/tests/meson.build b/tests/meson.build index 384e54e6ae..bac991f5fc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -42,6 +42,7 @@ checkasm_asm_sources = [] checkasm_test_sources = files( 'selftest.c', 'generic.c', + 'utils.c', 'arm/32/tests.c', 'arm/64/tests.c', 'riscv/tests.c', diff --git a/tests/selftest.c b/tests/selftest.c index 7ac52d7a1b..744c2a132b 100644 --- a/tests/selftest.c +++ b/tests/selftest.c @@ -1,13 +1,18 @@ +#include <stdio.h> + #include "tests.h" #include <checkasm/checkasm.h> static const CheckasmCpuInfo cpus[] = { { "Bad C", "badc", SELFTEST_CPU_FLAG_BAD_C }, + { "A", "a", SELFTEST_CPU_FLAG_A }, + { "B", "b", SELFTEST_CPU_FLAG_B, .mask = SELFTEST_CPU_FLAG_A }, + { "AB", "ab", SELFTEST_CPU_FLAG_AB }, #if ARCH_X86 { "Generic x86", "x86", SELFTEST_CPU_FLAG_X86 }, { "MMX", "mmx", SELFTEST_CPU_FLAG_MMX }, - { "SSE2", "sse2", SELFTEST_CPU_FLAG_SSE2 }, - { "AVX-2", "avx2", SELFTEST_CPU_FLAG_AVX2 }, + { "SSE", "sse", SELFTEST_CPU_FLAG_SSE }, + { "AVX", "avx", SELFTEST_CPU_FLAG_AVX }, { "AVX-512", "avx512", SELFTEST_CPU_FLAG_AVX512 }, #endif #if ARCH_RISCV @@ -26,8 +31,23 @@ static const CheckasmCpuInfo cpus[] = { {0} }; +static int seen_c, seen_a, seen_b, seen_ab, seen_any; +static void selftest_check_flags(void) +{ + seen_any = 1; + + switch (checkasm_get_cpu_flags() & SELFTEST_CPU_FLAG_AB) { + case 0: seen_c = 1; break; + case SELFTEST_CPU_FLAG_A: seen_a = 1; break; + case SELFTEST_CPU_FLAG_B: seen_b = 1; break; + case SELFTEST_CPU_FLAG_AB: seen_ab = 1; break; + } +} + static const CheckasmTest tests[] = { + { "flags", selftest_check_flags }, { "generic", selftest_check_generic }, + { "utils", selftest_check_utils }, #if ARCH_X86 { "x86", selftest_check_x86 }, #elif ARCH_RISCV @@ -45,7 +65,7 @@ int main(int argc, const char *argv[]) CheckasmConfig cfg = { .cpu_flags = cpus, .tests = tests, - .cpu = SELFTEST_CPU_FLAG_BAD_C, + .cpu = SELFTEST_CPU_FLAG_BAD_C | SELFTEST_CPU_FLAG_AB, }; #if ARCH_X86 @@ -58,5 +78,16 @@ int main(int argc, const char *argv[]) cfg.cpu |= selftest_get_cpu_flags_arm(); #endif - return checkasm_main(&cfg, argc, argv); + int ret = checkasm_main(&cfg, argc, argv); + if (ret) + return ret; + + if (seen_any && (!seen_c || !seen_a || !seen_b || !seen_ab)) { + fprintf(stderr, "checkasm: missing expected CPU flags;\n" + " seen_c=%d, seen_a=%d, seen_b=%d, seen_ab=%d\n", + seen_c, seen_a, seen_b, seen_ab); + return 1; + } + + return 0; } diff --git a/tests/tests.h b/tests/tests.h index f3f9c04635..a7e05dc10f 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -37,23 +37,26 @@ #include <checkasm/utils.h> enum { - SELFTEST_CPU_FLAG_BAD_C = 1 << 0, // dummy flag for "bad" C implementations + SELFTEST_CPU_FLAG_BAD_C = 1 << 0, // dummy flag for "bad" C implementations + SELFTEST_CPU_FLAG_A = 1 << 1, // dummy flag for testing flag masking + SELFTEST_CPU_FLAG_B = 1 << 2, + SELFTEST_CPU_FLAG_AB = SELFTEST_CPU_FLAG_A | SELFTEST_CPU_FLAG_B, #if ARCH_X86 - SELFTEST_CPU_FLAG_X86 = 1 << 1, - SELFTEST_CPU_FLAG_MMX = 1 << 2, - SELFTEST_CPU_FLAG_SSE2 = 1 << 3, - SELFTEST_CPU_FLAG_AVX2 = 1 << 4, - SELFTEST_CPU_FLAG_AVX512 = 1 << 5, + SELFTEST_CPU_FLAG_X86 = 1 << 10, + SELFTEST_CPU_FLAG_MMX = 1 << 11, + SELFTEST_CPU_FLAG_SSE = 1 << 12, + SELFTEST_CPU_FLAG_AVX = 1 << 13, + SELFTEST_CPU_FLAG_AVX512 = 1 << 14, #elif ARCH_RISCV - SELFTEST_CPU_FLAG_RVI = 1 << 1, - SELFTEST_CPU_FLAG_RVF = 1 << 2, - SELFTEST_CPU_FLAG_RVV = 1 << 3, + SELFTEST_CPU_FLAG_RVI = 1 << 10, + SELFTEST_CPU_FLAG_RVF = 1 << 11, + SELFTEST_CPU_FLAG_RVV = 1 << 12, #elif ARCH_AARCH64 - SELFTEST_CPU_FLAG_AARCH64 = 1 << 1, + SELFTEST_CPU_FLAG_AARCH64 = 1 << 10, #elif ARCH_ARM - SELFTEST_CPU_FLAG_ARM = 1 << 1, - SELFTEST_CPU_FLAG_VFP = 1 << 2, - SELFTEST_CPU_FLAG_VFPD32 = 1 << 3, + SELFTEST_CPU_FLAG_ARM = 1 << 10, + SELFTEST_CPU_FLAG_VFP = 1 << 11, + SELFTEST_CPU_FLAG_VFPD32 = 1 << 12, #endif }; @@ -106,6 +109,7 @@ void selftest_test_float(float_func *func, const char *name, float input); /* Platform-specific tests */ void selftest_check_generic(void); +void selftest_check_utils(void); void selftest_check_x86(void); void selftest_check_riscv(void); void selftest_check_aarch64(void); diff --git a/tests/utils.c b/tests/utils.c new file mode 100644 index 0000000000..172f520a90 --- /dev/null +++ b/tests/utils.c @@ -0,0 +1,187 @@ +/* + * Copyright © 2026, Niklas Haas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tests.h" + +#include "src/internal.h" + +static CHECKASM_ALIGN(union { + uint8_t u8[4096]; + uint16_t u16[2048]; + float f32[1024]; + double f64[512]; +}) buf; + +/* We need a wrapper function because checkasm_declare() can't handle void + * parameter lists. This should inline into the benchmark loop. */ +#define WRAP_VOID(RETVAL, FUNC) \ + static ALWAYS_INLINE RETVAL wrap_##FUNC(int unused) \ + { \ + return FUNC(); \ + } + +WRAP_VOID(int, checkasm_rand) +WRAP_VOID(int8_t, checkasm_rand_int8) +WRAP_VOID(uint8_t, checkasm_rand_uint8) +WRAP_VOID(int16_t, checkasm_rand_int16) +WRAP_VOID(uint16_t, checkasm_rand_uint16) +WRAP_VOID(int32_t, checkasm_rand_int32) +WRAP_VOID(uint32_t, checkasm_rand_uint32) +WRAP_VOID(float, checkasm_rand_float32) +WRAP_VOID(int64_t, checkasm_rand_int64) +WRAP_VOID(uint64_t, checkasm_rand_uint64) +WRAP_VOID(double, checkasm_rand_float64) + +static void selftest_test_prng(void) +{ +#define CHECK_RAND(RETVAL, FUNC) \ + if (checkasm_check_func(checkasm_##FUNC, #FUNC)) { \ + checkasm_declare(RETVAL, int); \ + checkasm_bench(wrap_checkasm_##FUNC, 0); \ + } + + CHECK_RAND(int, rand) + CHECK_RAND(int8_t, rand_int8) + CHECK_RAND(uint8_t, rand_uint8) + CHECK_RAND(int16_t, rand_int16) + CHECK_RAND(uint16_t, rand_uint16) + CHECK_RAND(int32_t, rand_int32) + CHECK_RAND(uint32_t, rand_uint32) + CHECK_RAND(float, rand_float32) + CHECK_RAND(int64_t, rand_int64) + CHECK_RAND(uint64_t, rand_uint64) + CHECK_RAND(double, rand_float64) + + checkasm_report("prng"); +} + +static void selftest_test_randomize(void) +{ + if (checkasm_check_func(checkasm_randomize, "randomize")) { + checkasm_declare(void, void *buf, size_t bytes); + checkasm_bench_new(buf.u8, sizeof(buf.u8)); + } + + if (checkasm_check_func(checkasm_randomize_mask8, "randomize_mask8")) { + checkasm_declare(void, uint8_t *buf, int width, uint8_t mask); + checkasm_bench_new(buf.u8, ARRAY_SIZE(buf.u8), 0x7F); + } + + if (checkasm_check_func(checkasm_randomize_mask16, "randomize_mask16")) { + checkasm_declare(void, uint16_t *buf, int width, uint16_t mask); + checkasm_bench_new(buf.u16, ARRAY_SIZE(buf.u16), 0x7FFF); + } + + if (checkasm_check_func(checkasm_randomize_range, "randomize_range")) { + checkasm_declare(void, double *buf, int width, double range); + checkasm_bench_new(buf.f64, ARRAY_SIZE(buf.f64), 100.0); + } + + if (checkasm_check_func(checkasm_randomize_rangef, "randomize_rangef")) { + checkasm_declare(void, float *buf, int width, float range); + checkasm_bench_new(buf.f32, ARRAY_SIZE(buf.f32), 100.0f); + } + + if (checkasm_check_func(checkasm_randomize_interval, "randomize_interval")) { + checkasm_declare(void, double *buf, int width, double low, double high); + checkasm_bench_new(buf.f64, ARRAY_SIZE(buf.f64), -100.0, 100.0); + } + + if (checkasm_check_func(checkasm_randomize_intervalf, "randomize_intervalf")) { + checkasm_declare(void, float *buf, int width, float low, float high); + checkasm_bench_new(buf.f32, ARRAY_SIZE(buf.f32), -100.0f, 100.0f); + } + + if (checkasm_check_func(checkasm_randomize_dist, "randomize_dist")) { + checkasm_declare(void, double *buf, int width, CheckasmDist dist); + checkasm_bench_new(buf.f64, ARRAY_SIZE(buf.f64), checkasm_dist_standard); + } + + if (checkasm_check_func(checkasm_randomize_distf, "randomize_distf")) { + checkasm_declare(void, float *buf, int width, CheckasmDist dist); + checkasm_bench_new(buf.f32, ARRAY_SIZE(buf.f32), checkasm_dist_standard); + } + + if (checkasm_check_func(checkasm_randomize_norm, "randomize_norm")) { + checkasm_declare(void, double *buf, int width); + checkasm_bench_new(buf.f64, ARRAY_SIZE(buf.f64)); + } + + if (checkasm_check_func(checkasm_randomize_normf, "randomize_normf")) { + checkasm_declare(void, float *buf, int width); + checkasm_bench_new(buf.f32, ARRAY_SIZE(buf.f32)); + } + + checkasm_report("randomize"); +} + +static void selftest_test_clear(void) +{ + if (checkasm_check_func(checkasm_clear, "clear")) { + checkasm_declare(void, void *buf, size_t bytes); + checkasm_bench_new(buf.u8, sizeof(buf.u8)); + } + + if (checkasm_check_func(checkasm_clear8, "clear8")) { + checkasm_declare(void, uint8_t *buf, int width, uint8_t val); + checkasm_bench_new(buf.u8, ARRAY_SIZE(buf.u8), 0xAA); + } + + if (checkasm_check_func(checkasm_clear16, "clear16")) { + checkasm_declare(void, uint16_t *buf, int width, uint16_t val); + checkasm_bench_new(buf.u16, ARRAY_SIZE(buf.u16), 0xAAAA); + } + + checkasm_report("clear"); +} + +static void selftest_test_init(void) +{ + if (checkasm_check_func(checkasm_init, "init")) { + checkasm_declare(void, void *buf, size_t bytes); + checkasm_bench_new(buf.u8, sizeof(buf.u8)); + } + + if (checkasm_check_func(checkasm_init_mask8, "init_mask8")) { + checkasm_declare(void, uint8_t *buf, int width, uint8_t mask); + checkasm_bench_new(buf.u8, ARRAY_SIZE(buf.u8), 0x7F); + } + + if (checkasm_check_func(checkasm_init_mask16, "init_mask16")) { + checkasm_declare(void, uint16_t *buf, int width, uint16_t mask); + checkasm_bench_new(buf.u16, ARRAY_SIZE(buf.u16), 0x7FFF); + } + + checkasm_report("init"); +} + +void selftest_check_utils(void) +{ + selftest_test_prng(); + selftest_test_randomize(); + selftest_test_clear(); + selftest_test_init(); +} diff --git a/tests/x86/tests.c b/tests/x86/tests.c index 7c7277d9d5..377ec428a0 100644 --- a/tests/x86/tests.c +++ b/tests/x86/tests.c @@ -20,31 +20,30 @@ uint64_t selftest_get_cpu_flags_x86(void) checkasm_cpu_cpuid(&r, 1, 0); if (r.edx & 0x00800000) /* MMX */ flags |= SELFTEST_CPU_FLAG_MMX; - if (r.edx & 0x02000000) /* SSE2 */ - flags |= SELFTEST_CPU_FLAG_SSE2; + if (r.edx & 0x02000000) /* SSE */ + flags |= SELFTEST_CPU_FLAG_SSE; if (~r.ecx & 0x18000000) /* OSXSAVE/AVX */ return flags; const uint64_t xcr0 = checkasm_cpu_xgetbv(0); - if (max_leaf < 7 || ~xcr0 & 0x6) /* XMM/YMM */ + if (~xcr0 & 0x6) /* XMM/YMM */ return flags; - checkasm_cpu_cpuid(&r, 7, 0); - if (r.ebx & 0x00000020) /* AVX2 */ - flags |= SELFTEST_CPU_FLAG_AVX2; + flags |= SELFTEST_CPU_FLAG_AVX; - if (~xcr0 & 0xe0) /* ZMM/OPMASK */ + if (max_leaf < 7 || ~xcr0 & 0xe0) /* ZMM/OPMASK */ return flags; - if (r.ebx & 0x00000020) /* AVX512F */ + checkasm_cpu_cpuid(&r, 7, 0); + if (r.ebx & 0x00010000) /* AVX512F */ flags |= SELFTEST_CPU_FLAG_AVX512; return flags; } DEF_COPY_FUNC(copy_x86); DEF_COPY_FUNC(copy_mmx); -DEF_COPY_FUNC(copy_sse2); -DEF_COPY_FUNC(copy_avx2); +DEF_COPY_FUNC(copy_sse); +DEF_COPY_FUNC(copy_avx); DEF_COPY_FUNC(copy_avx512); DEF_NOOP_FUNC(clobber_r0); @@ -69,7 +68,7 @@ DEF_NOOP_FUNC(sigill_x86); DEF_NOOP_FUNC(corrupt_stack_x86); DEF_COPY_FUNC(copy_noemms_mmx); -DEF_COPY_FUNC(copy_novzeroupper_avx2); +DEF_COPY_FUNC(copy_novzeroupper_avx); static copy_func *get_copy_x86(void) { @@ -77,10 +76,10 @@ static copy_func *get_copy_x86(void) #if ARCH_X86 if (flags & SELFTEST_CPU_FLAG_AVX512) return selftest_copy_avx512; - if (flags & SELFTEST_CPU_FLAG_AVX2) - return selftest_copy_avx2; - if (flags & SELFTEST_CPU_FLAG_SSE2) - return selftest_copy_sse2; + if (flags & SELFTEST_CPU_FLAG_AVX) + return selftest_copy_avx; + if (flags & SELFTEST_CPU_FLAG_SSE) + return selftest_copy_sse; if (flags & SELFTEST_CPU_FLAG_MMX) return selftest_copy_mmx; if (flags & SELFTEST_CPU_FLAG_X86) @@ -134,7 +133,7 @@ static noop_func *get_clobber(int reg) DEF_NOOP_GETTER(SELFTEST_CPU_FLAG_X86, sigill_x86) DEF_NOOP_GETTER(SELFTEST_CPU_FLAG_X86, corrupt_stack_x86) DEF_COPY_GETTER(SELFTEST_CPU_FLAG_MMX, copy_noemms_mmx) -DEF_COPY_GETTER(SELFTEST_CPU_FLAG_AVX2, copy_novzeroupper_avx2) +DEF_COPY_GETTER(SELFTEST_CPU_FLAG_AVX, copy_novzeroupper_avx) uintptr_t selftest_get_stack_pointer_x86(int unused); @@ -214,7 +213,7 @@ void selftest_check_x86(void) check_clobber(NUM_SAFE, NUM_REGS); if (checkasm_get_check_vzeroupper()) - selftest_test_copy(get_copy_novzeroupper_avx2(), "novzeroupper", 32); + selftest_test_copy(get_copy_novzeroupper_avx(), "novzeroupper", 32); } #endif diff --git a/tests/x86/tests_asm.asm b/tests/x86/tests_asm.asm index 1af9e12dae..a2eeff47ee 100644 --- a/tests/x86/tests_asm.asm +++ b/tests/x86/tests_asm.asm @@ -99,15 +99,15 @@ copy_mm _noemms cglobal noemms, 3, 3, 0, dst, src, size RET -; SSE2 functions -INIT_XMM sse2 +; SSE functions +INIT_XMM sse copy_mm -; AVX2 functions -INIT_YMM avx2 +; AVX functions +INIT_YMM avx copy_mm copy_mm _novzeroupper ; AVX512 functions -INIT_YMM avx512 +INIT_ZMM avx512 copy_mm _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
