checkasm 1.0.0
Assembly testing and benchmarking framework
Loading...
Searching...
No Matches
test.h
Go to the documentation of this file.
1/*
2 * Copyright © 2025, Niklas Haas
3 * Copyright © 2018, VideoLAN and dav1d authors
4 * Copyright © 2018, Two Orioles, LLC
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
36
37#ifndef CHECKASM_TEST_H
38#define CHECKASM_TEST_H
39
40#include <stdint.h>
41
42#include "checkasm/attributes.h"
43#include "checkasm/checkasm.h"
44#include "checkasm/perf.h"
45#include "checkasm/platform.h"
46
76#define checkasm_check_func(func, ...) \
77 (checkasm_key_ref \
78 = checkasm_check_key((checkasm_key_new = (CheckasmKey) (func)), __VA_ARGS__))
79
97CHECKASM_API int checkasm_fail_func(const char *msg, ...) CHECKASM_PRINTF(1, 2);
98
116#define checkasm_fail() checkasm_fail_func("%s:%d", __FILE__, __LINE__)
117
138CHECKASM_API void checkasm_report(const char *name, ...) CHECKASM_PRINTF(1, 2);
139
162#define checkasm_declare(ret, ...) \
163 checkasm_declare_impl(ret, __VA_ARGS__); \
164 typedef ret func_type(__VA_ARGS__); \
165 (void) ((func_type *) NULL)
166
190#ifndef checkasm_declare_emms
191 #define checkasm_declare_emms(cpu_flags, ret, ...) checkasm_declare(ret, __VA_ARGS__)
192#endif
193
209#define checkasm_call(func, ...) \
210 (checkasm_set_signal_handler_state(1), (func) (__VA_ARGS__)); \
211 checkasm_set_signal_handler_state(0)
212
228#ifndef checkasm_call_checked
229 #define checkasm_call_checked(func, ...) \
230 (checkasm_set_signal_handler_state(1), \
231 checkasm_push_stack_guard((uintptr_t[16]) { 0, 0 }), \
232 ((func_type *) (func))(__VA_ARGS__)); \
233 checkasm_pop_stack_guard(); \
234 checkasm_set_signal_handler_state(0)
235#endif
236
264
274
283
293#define checkasm_func_ref ((func_type *) checkasm_key_ref)
294
307#define checkasm_func_new ((func_type *) checkasm_key_new)
308
321#define checkasm_call_ref(...) checkasm_call(checkasm_func_ref, __VA_ARGS__)
322
336#define checkasm_call_new(...) checkasm_call_checked(checkasm_func_new, __VA_ARGS__)
337
357#define checkasm_bench(func, ...) \
358 do { \
359 if (checkasm_bench_func()) { \
360 func_type *const bench_func = (func); \
361 checkasm_set_signal_handler_state(1); \
362 for (int truns; (truns = checkasm_bench_runs());) { \
363 uint64_t time; \
364 CHECKASM_PERF_BENCH(truns, time, __VA_ARGS__); \
365 checkasm_clear_cpu_state(); \
366 checkasm_bench_update(truns, time); \
367 } \
368 checkasm_set_signal_handler_state(0); \
369 checkasm_bench_finish(); \
370 } else { \
371 const int tidx = 0; \
372 (void) tidx; \
373 checkasm_call_checked(func, __VA_ARGS__); \
374 } \
375 } while (0)
376
401#define checkasm_bench_new(...) checkasm_bench(checkasm_func_new, __VA_ARGS__)
402
423#define checkasm_alternate(a, b) ((tidx & 1) ? (b) : (a))
424
433#define fail checkasm_fail
434#define report checkasm_report
435#define check_func checkasm_check_func
436#define func_ref checkasm_func_ref
437#define func_new checkasm_func_new
438#define call_ref checkasm_call_ref
439#define call_new checkasm_call_new
440#define bench_new checkasm_bench_new
441#define alternate checkasm_alternate
442#define declare_func checkasm_declare
443#define declare_func_emms checkasm_declare_emms
445
454
460 CHECKASM_PRINTF(2, 3);
461
467
473CHECKASM_API void checkasm_pop_stack_guard(void);
474
482#ifndef checkasm_clear_cpu_state
483 #define checkasm_clear_cpu_state() \
484 do { \
485 } while (0)
486#endif
487
488typedef struct CheckasmPerf {
494 uint64_t (*start)(void);
495
501 uint64_t (*stop)(uint64_t start_time);
502
504 const char *name;
505
507 const char *unit;
508
509#ifdef CHECKASM_PERF_ASM
511 int asm_usable;
512#endif
514
515#define CHECKASM_PERF_CALL4(...) \
516 do { \
517 int tidx = 0; \
518 bench_func(__VA_ARGS__); \
519 tidx = 1; \
520 bench_func(__VA_ARGS__); \
521 tidx = 2; \
522 bench_func(__VA_ARGS__); \
523 tidx = 3; \
524 bench_func(__VA_ARGS__); \
525 (void) tidx; \
526 } while (0)
527
528#define CHECKASM_PERF_CALL16(...) \
529 do { \
530 CHECKASM_PERF_CALL4(__VA_ARGS__); \
531 CHECKASM_PERF_CALL4(__VA_ARGS__); \
532 CHECKASM_PERF_CALL4(__VA_ARGS__); \
533 CHECKASM_PERF_CALL4(__VA_ARGS__); \
534 } while (0)
535
536/* Naive loop; used when perf.start/stop() is expected to be slow or imprecise, or when
537 * we have no ASM cycle counters, or when the number of iterations is low */
538#define CHECKASM_PERF_BENCH_SIMPLE(count, time, ...) \
539 do { \
540 time = perf.start(); \
541 for (int tidx = 0; tidx < count; tidx++) \
542 bench_func(__VA_ARGS__); \
543 time = perf.stop(time); \
544 } while (0)
545
546/* Unrolled loop with inline outlier rejection; used when we have asm cycle counters */
547#define CHECKASM_PERF_BENCH_ASM(total_count, time, ...) \
548 do { \
549 int tcount_trim = 0; \
550 uint64_t tsum_trim = 0; \
551 for (int titer = 0; titer < total_count; titer += 32) { \
552 uint64_t t = CHECKASM_PERF_ASM(); \
553 CHECKASM_PERF_CALL16(__VA_ARGS__); \
554 CHECKASM_PERF_CALL16(__VA_ARGS__); \
555 t = CHECKASM_PERF_ASM() - t; \
556 if (t * tcount_trim <= tsum_trim * 4 && (titer > 0 || total_count < 1000)) { \
557 tsum_trim += t; \
558 tcount_trim++; \
559 } \
560 } \
561 time = tsum_trim; \
562 total_count = tcount_trim << 5; \
563 } while (0)
564
565/* Select the best benchmarking method at runtime */
566CHECKASM_API const CheckasmPerf *checkasm_get_perf(void);
567
568#ifdef CHECKASM_PERF_ASM
569 #ifndef CHECKASM_PERF_ASM_USABLE
570 #define CHECKASM_PERF_ASM_USABLE perf.asm_usable
571 #endif
572 #define CHECKASM_PERF_BENCH(count, time, ...) \
573 do { \
574 const CheckasmPerf perf = *checkasm_get_perf(); \
575 if (CHECKASM_PERF_ASM_USABLE && count >= 128) { \
576 CHECKASM_PERF_BENCH_ASM(count, time, __VA_ARGS__); \
577 } else { \
578 CHECKASM_PERF_BENCH_SIMPLE(count, time, __VA_ARGS__); \
579 } \
580 } while (0)
581#else /* !CHECKASM_PERF_ASM */
582 #define CHECKASM_PERF_BENCH(count, time, ...) \
583 do { \
584 const CheckasmPerf perf = *checkasm_get_perf(); \
585 CHECKASM_PERF_BENCH_SIMPLE(count, time, __VA_ARGS__); \
586 } while (0)
587#endif
588
594
600
606CHECKASM_API void checkasm_bench_update(int iterations, uint64_t cycles);
607
612
616static inline void checkasm_unused(void)
617{
618 (void) checkasm_key_ref;
619 (void) checkasm_key_new;
620}
621 /* internal */
623
624#endif /* CHECKASM_TEST_H */
Platform and compiler attribute macros.
#define CHECKASM_PRINTF(fmt, attr)
Printf-style format string checking attribute.
Definition attributes.h:63
#define CHECKASM_API
Symbol visibility attribute for public API functions.
Definition attributes.h:88
Main checkasm API for test suite configuration and execution.
uint64_t CheckasmCpu
Opaque type representing a set of CPU feature flags.
Definition checkasm.h:52
uintptr_t CheckasmKey
Opaque type used to identify function implementations.
Definition checkasm.h:60
CHECKASM_API int checkasm_bench_func(void)
Check if current function should be benchmarked.
static void checkasm_unused(void)
Suppress unused variable warnings.
Definition test.h:616
CHECKASM_API void checkasm_bench_finish(void)
Finalize and store benchmark results.
CHECKASM_API void checkasm_bench_update(int iterations, uint64_t cycles)
Update benchmark statistics with timing results.
CHECKASM_API CheckasmKey checkasm_check_key(CheckasmKey version, const char *name,...) CHECKASM_PRINTF(2
Internal implementation of checkasm_check_func().
CHECKASM_API CheckasmKey CHECKASM_API void checkasm_set_signal_handler_state(int enabled)
Enable or disable signal handling.
CHECKASM_API int checkasm_bench_runs(void)
Get number of iterations for current benchmark run.
CHECKASM_API void checkasm_push_stack_guard(uintptr_t guard[2])
Push stack guard values for corruption detection.
Definition test.h:488
uint64_t(* start)(void)
Start timing measurement.
Definition test.h:494
const char * unit
Unit of measurement (e.g., "ns", "cycles").
Definition test.h:507
const char * name
Name of the timing mechanism (e.g., "clock_gettime").
Definition test.h:504
uint64_t(* stop)(uint64_t start_time)
Stop timing measurement.
Definition test.h:501
static CheckasmKey checkasm_key_ref
Key identifying the reference implementation.
Definition test.h:273
static CheckasmKey checkasm_key_new
Key identifying the implementation being tested.
Definition test.h:282
CHECKASM_API void checkasm_report(const char *name,...) CHECKASM_PRINTF(1
Report test outcome for a named group of functions.
CHECKASM_API int checkasm_fail_func(const char *msg,...) CHECKASM_PRINTF(1
Mark the current function as failed with a custom message.
CHECKASM_API int checkasm_should_fail(CheckasmCpu cpu_flags)
Mark a block of tests as expected to fail.