This guide covers how to integrate checkasm into existing or new projects.
CPU Flags
CPU flags represent instruction set extensions and features that your optimized implementations depend on (e.g., SSE2, AVX2, NEON). You must define an array of these flags, so checkasm can systematically test each implementation variant.
- Note
- checkasm does not provide CPU detection or runtime dispatch functionality on its own. It is a pure testing framework, and as such, should not be used as a runtime dependency of your project. This means that your project must implement its own CPU feature detection and dispatch mechanisms for production use. checkasm plugs into these existing mechanisms during testing.
Defining CPU Flags
Assuming you have a set of CPU flags defined in your project, e.g.,
enum {
CPU_FLAG_SSE2 = 1 << 0,
CPU_FLAG_SSSE3 = 1 << 1,
CPU_FLAG_SSE41 = 1 << 2,
CPU_FLAG_AVX2 = 1 << 3,
CPU_FLAG_AVX512 = 1 << 4,
};
typedef uint64_t MyCpuFlags;
MyCpuFlags detect_cpu_flags(void);
Then create a CheckasmCpuInfo array describing each flag, terminated by {0}:
{ "SSE2", "sse2", CPU_FLAG_SSE2 },
{ "SSSE3", "ssse3", CPU_FLAG_SSSE3 },
{ "SSE4.1", "sse41", CPU_FLAG_SSE41 },
{ "AVX2", "avx2", CPU_FLAG_AVX2 },
{ "AVX512", "avx512", CPU_FLAG_AVX512 },
{0}
};
Describes a CPU feature flag/capability.
Definition checkasm.h:69
Each entry contains:
- name: Human-readable name displayed in output (e.g., "SSE4.1")
- suffix: Short suffix used in function names and filtering (e.g., "sse41")
- flag: The bitfield value from your CPU flag enum
- Note
- Flags are tested in the order defined in the array. Each test inherits flags from all previous entries, allowing checkasm to test progressively more advanced instruction sets.
Register the CPU flags with checkasm via the CheckasmConfig structure:
{0}
};
int main(int argc, const char *argv[])
{
.cpu_flags = cpu_flags,
};
config.
cpu = detect_cpu_flags();
}
CHECKASM_API int checkasm_main(CheckasmConfig *config, int argc, const char *argv[])
Main entry point for checkasm test programs.
Configuration structure for the checkasm test suite.
Definition checkasm.h:122
CheckasmCpu cpu
Detected CPU flags for the current system.
Definition checkasm.h:150
Extra CPU Flags
You can include additional flags in CheckasmConfig.cpu that aren't in CheckasmConfig.cpu_flags. These are transparently passed through to checkasm_get_cpu_flags() and can be used for modifier flags like CPU_FLAG_FAST_* that don't require separate testing, but should instead always be assumed to be available when matching function implementations.
Selecting Functions
There are two common strategies for selecting the correct function implementation during tests, depending on how your project structures its dispatch mechanism:
- Note
- Choose the strategy that matches your project's existing architecture. If you are developing a new library, we recommend the first approach.
Strategy 1: Mask Callback
If your project has a mask_cpu_flags (or cpu_flags_override) function that updates an internal static bitmask used internally by dispatch table getters, e.g.:
static unsigned cpu_flags = 0;
static unsigned cpu_flags_mask = -1;
unsigned get_cpu_flags(void)
{
return cpu_flags & cpu_flags_mask;
}
void mask_cpu_flags(unsigned flags)
{
cpu_flags_mask = flags;
}
void foo_dsp_init(foo_dsp *dsp)
{
const unsigned cpu_flags = get_cpu_flags();
dsp->add = add_c;
dsp->sub = sub_c;
if (cpu_flags & CPU_FLAG_SSE2) {
dsp->add = add_sse2;
}
if (cpu_flags & CPU_FLAG_AVX2) {
dsp->add = add_avx2;
dsp->sub = sub_avx2;
}
}
Then, in your checkasm main file, you can set that directly as a callback in CheckasmConfig.set_cpu_flags:
{
mask_cpu_flags((unsigned) cpu);
}
.set_cpu_flags = set_cpu_flags,
};
uint64_t CheckasmCpu
Opaque type representing a set of CPU feature flags.
Definition checkasm.h:52
With this approach, checkasm will automatically call your set_cpu_flags() function whenever it changes the active CPU feature set during testing.
- Note
- This will always be a subset of the initially detected CPU flags provided in CheckasmConfig.cpu, so there is no meaningful distinction between a mask_cpu_flags (that masks out real CPU flags) and a cpu_flags_override (that overrides them wholesale). Both can be used as a callback.
Strategy 2: Direct Getters
If your project uses dispatch functions that directly accept a CPU mask parameter (e.g., void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)), you can call them within each test using checkasm_get_cpu_flags():
void foo_dsp_init(foo_dsp *dsp, unsigned cpu_flags)
{
}
Then, in your checkasm test files:
void check_foo_dsp(void)
{
foo_dsp dsp;
}
CHECKASM_API CheckasmCpu checkasm_get_cpu_flags(void)
Get the current active set of CPU flags.
The same applies if your project uses individual function getters like add_func_t get_add_func(unsigned cpu_flags) instead of dispatch tables / dsp structs.
Organizing Multiple Tests
For larger projects, organize tests by module:
void checkasm_check_mc(void);
void checkasm_check_pixel(void);
void checkasm_check_filmgrain(void);
{ "mc", checkasm_check_mc },
{ "pixel", checkasm_check_pixel },
{ "filmgrain", checkasm_check_filmgrain },
{0}
};
Describes a single test function.
Definition checkasm.h:81
Then implement each test in separate files:
#include "mc_dsp.h"
static void test_mc_func1(const mc_dsp *dsp);
static void test_mc_func2(const mc_dsp *dsp);
static void test_mc_func3(const mc_dsp *dsp);
void checkasm_check_mc(void)
{
mc_dsp dsp;
test_func1(&dsp);
test_func2(&dsp);
test_func3(&dsp);
test_func4(&dsp);
test_func5(&dsp);
test_func6(&dsp);
}
Test writing API for checkasm.
CHECKASM_API void checkasm_report(const char *name,...) CHECKASM_PRINTF(1
Report test outcome for a named group of functions.
Next Steps
Now that you know how to integrate checkasm with your project's architecture, dive deeper into best practices and advanced patterns for writing comprehensive tests.
Next: Writing Tests