Modules
Optional modules are not part of the default library. They are loaded only when named in VECMAT_MODULES. Public modules live in the vecmat-vendors repository. The recommended layout is a vendors/ checkout under this tree. That checkout may be a git submodule; it is optional. If the repository is checked out elsewhere, pass its path in VECMAT_MODULES_PATH. Private modules are loaded the same way from their own tree.
The loader accepts three kinds: backend, width, and feature. A missing module.cmake, or any other kind, is Unknown Vecmat module.
Kinds
| Kind | What it does | Lists it appends |
|---|---|---|
backend | Registers a complete _ptr table with vm_. A schedule. | VECMAT_MODULE_HOOKS, register includes and calls. |
width | Overrides a slot on a core width such as fp16. Does not fill vm_. | That width's lists (VECMAT_FP16_MODULE_HOOKS, ...). |
feature | Domain helpers. Contributes sources and headers. Does not register. | VECMAT_MODULE_HOOKS (sources), optionally test/bench sources. Not the register lists. |
A backend owns identity, extra HWCAP bits, and the tuned kernels. There is no vm_cpu_part() in the base library: the backend probes, then registers VM_. A width module does not register a backend. The core owns the storage type; the module only overrides a slot. See doc/.
A feature module contributes sources via VECMAT_MODULE_HOOKS (the same hook the float object libraries call) and may append VECMAT_MODULE_TEST_SOURCES / VECMAT_MODULE_BENCH_SOURCES. It must not append to a backend or width register list. No feature module ships in 0.3.x; the kind exists so 0.4.x can land domain helpers without another loader change.
Layout
A module is a directory with module.cmake. Under the recommended layout that is vendors/<vendor>/<name>/. module.cmake is the only file the top-level build includes. It must set VECMAT_MODULE_KIND. Use CMAKE_CURRENT_LIST_DIR for its own sources.
A backend module:
vendors/arm/cortex-a5x ├── doc │ └── cortex-a5x.md ├── include │ └── vecmat_cortex_a5x.h ├── module.cmake └── src ├── a5x.h ├── gemm_a53.c ├── gemm_a55.c ├── probe.c ├── register.c └── sched.c
A width module:
vendors/arm/fp16 ├── doc │ └── fp16.md ├── include │ └── vecmat_arm_fp16.h ├── module.cmake └── src ├── arm_fp16.h ├── neon.c ├── probe.c └── register.c
Build
Empty VECMAT_MODULES is the default. With vendors/ checked out under this tree:
cmake -S . -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DVECMAT_MODULES=arm/fp16 \ -DVECMAT_RUNTIME_DISPATCH=ON cmake --build build
If the modules are elsewhere, add -DVECMAT_MODULES_PATH=../vecmat-vendors. VECMAT_MODULES entries are paths relative to vendors/ (or to each VECMAT_MODULES_PATH root). A private vendors tree is the same.
module.cmake appends, depending on kind:
backend:VECMAT_MODULE_HOOKS(called fromvecmat_add_float_objects, once per float width),VECMAT_MODULE_REGISTER_INCLUDES,VECMAT_MODULE_REGISTER_CALLS, and optionallyVECMAT_MODULE_TEST_HOOKS,VECMAT_MODULE_TEST_SOURCES,VECMAT_MODULE_BENCH_SOURCES.width: the lists for the width it implements (VECMAT_FP16_MODULE_HOOKS,VECMAT_FP16_MODULE_REGISTER_CALLS). Those lists are consumed only by that width's object lib. The module's ownmodule.cmakeerrors if that width's switch is off. A width module must not append to the backend lists; the loader rejects that.
Test and bench sources live in the vendor tree. The mains do not include a module header. VECMAT_MODULE_TEST_HOOKS runs after the test targets exist and may add the public include dir. Do not define a module macro in the test mains.
Backend registration
The base library keeps this surface for backends:
vm_/backend_ register() VM_— a registered table winsCPU_ BACKEND vm_— copy a compiled ISA table and override slots. NEON, SVE, and SVE2 are provided. Returns NULL if that ISA was not compiled.backend_ builtin() vm_/cpu_ set_ note() vm_— one extra banner line, printed only if setcpu_ note() vm_backend_modules_register()— test and benchmark hook, generated, not a library exportVECMAT_FORCE_ISA— override the compiled/runtime pick when no backend is registered
Tests and benchmarks do not include a backend header. They call the generated hook, then vm_:
vm_backend_modules_register(); vm_cpu_init(); if (vm_cpu_note()) printf("%s\n", vm_cpu_note());
With no backends selected, the generated function is empty. A backend that does not match the CPU returns 0 and leaves the builtin table in place. A registered backend wins over VECMAT_FORCE_ISA. The same pointer is idempotent. A table with a missing slot is rejected. VM_ is 8.
The descriptor:
| Field | Role |
|---|---|
name | Stable string. vm_ returns it when this backend is selected. |
features | VM_ if the schedule is not itself an ISA bit. |
priority | Higher wins among registered backends. |
ops | Complete vm_. Must outlive the process. |
Copy the builtin table, then replace the slots this schedule owns. vm_ is NULL when NEON was not compiled.
const vm_backend_ops *builtin = vm_backend_builtin(VM_CPU_NEON); ops = *builtin; ops.vm_gemm_ukernel = my_ukernel; ops.quat_mul_ptr = my_quat_mul; backend.name = "my-backend"; backend.features = VM_CPU_BACKEND; backend.priority = 100; backend.ops = &ops; vm_cpu_set_note("backend my-backend part=..."); return vm_backend_register(&backend);
Float symbols are width-suffixed. Internal functions that both object libs define need the same treatment.
Benchmarks keep the selected/compiled/runtime/precision line. They print vm_ on the next line only when a backend set one.
The
string(APPEND VECMAT_MODULE_REGISTER_INCLUDES "#include <vecmat_cortex_a5x.h>\n") string(APPEND VECMAT_MODULE_REGISTER_CALLS " vm_cortex_a5x_register();\n")
The header rename (vm_cortex_a5x_register to vm_cortex_a5x_register64 on an f64 TU) happens because the generated file includes that header and the test target defines VECMAT_USE_F64.
Adding a module
- Add
vendors/<vendor>/<name>/withmodule.cmake,include/,src/, anddoc/<name>.md. - In
module.cmake, setVECMAT_MODULE_KINDtobackendorwidth. - Append the lists for that kind. Do not add an
#ifdeftotest/test_main.cortest/bench_main.c. - For a backend: probe in the module, not in
src/. Build the ops table fromfeatures/ cpu.c vm_plus overrides. Passbackend_ builtin() VM_unless the schedule is one of the existing ISA bits.CPU_ BACKEND - Document the register call and any
VECMAT_FORCE_*knobs in the module's own doc.