Contributor's Guide
Optimizing arithmetic operations for a variety of enviroments and consumers is a never-ending chase. There hardly will ever be a moment when intops will be 100% ready: there's always an older Nim version, and newer GCC version, or an obscure CPU vendor to target.
Because of that, contributors' help is essential for intops' development. You can help improve intops by improving the code, improving the docs, and reporting issues.
Library Structure
src
β intops.nim <- entrypoint for the public API
β
ββββintops
β consts.nim <- global constants for environment detection
β
ββββimpl <- implementations of primitives
β β inlineasm.nim <- entrypoint for the Inline ASM family of implementations
β β inlinec.nim
β β intrinsics.nim
β β pure.nim
β β ...
β β
β ββββinlineasm
β β arm64.nim <- implementation in ARM64 Assembly
β β x86.nim
β β ...
β β
β ββββ...
β
ββββops <- operation families
add.nim <- addition flavors: carrying, saturating, etc.
mul.nim
sub.nim
...
The entrypoint is the root intops module. It's the library's public API and exposes all the available primitives.
Each operation family has its own submodule in intops/ops. E.g. intops/ops/add is the submodule that contains various addition flavors.
These submodules contain the dispatchers that pick the best implementation of the given operation and for the given CPU, OS, and C compiler. I.e., each operation "knows" its best implementation and "decides" which one to run.
The actual implementations are stored in submodules in intops/impl. For example, intops/impl/intrinsics contains all primitives implemented with C intrinsics.
API Conventions
- intops follows the common Nim convention of calling things in
camelCase. - intops prefers pure functions that return values to the ones that modify mutable arguments.
- The docstrings are mandatory for the dispatchers in
intops/opsmodules because this is the library public API. - Operations the return a wider type that the input type are called widening. For example
wideningMulΒ is multiplication that takes two 64-bit integers and return a single 128-bit integer (althouth represented as a pair of 64-bit integers). - Operations that return a carry or borrow flag are called carrying and borrowing, e.g.
carryingAdd. - Operations that return an overflow flag are called overflowing, e.g.
overflowingSub. - Operations that return maximal or mininal type value when a type border is hit are called saturating, e.g.
saturatingAdd. - Carry, borrow, and overflow flags are booleans.
Tests
The tests for intops are located in a single file tests/tintops.nim.
To run the tests locally, use nimble test command.
With this command, the tests are run:
- without compilation flags in runtime mode
- without compilation flags in compile-time mode
- with each compilation flag separately
- with all compilation flags
When executed on the CI, the tests are run against multiple OS, C compilers, and architectures:
- amd64 + Linux + gcc 13
- amd64 + Linux + gcc 14
- amd64 + Windows + clang 19
- i386 + Linux + gcc 13
- amd64 + macOS + clang 17
- arm46 + macOS + clang 17
This is hardly reproduceable locally, but you can cover at least some of the cases by passing flags to nimble test. For example, to emulate running the tests in a 32-bit CPU, run nimble --cpu:i386 test. On Windows, you also must pass the path to your GCC installation, e.g. nimble --gcc.path:D:\mingw32\bin\ --cpu:i386 test.
To run your tests contunously during development, use monit:
- Install monit with
nimble install monit. - Create a file called .monit.yml in the working directory:
%YAML 1.2
%TAG !n! tag:nimyaml.org,2016:
---
!n!custom:MonitorConfig
sleep: 1
targets:
- name: Run tests
paths: [src, tests]
commands:
- nimble test
- nimble --gcc.path:D:\mingw32\bin\ --cpu:i386 test
extensions: [.nim]
files: []
exclude_extensions: []
exclude_files: []
once: true
- Run
monit run
Docs
The docs consist of two parts:
- the book (this is what you're reading right now)
- the API docs
The book is created using nimibook. Each page is a Nim file that can hold Markdown content and Nim code. The Nim code is executed during the build and its output are included in the book.
The API docs are generated from the source code docstrings.
To build the docs locally, run:
nimble bookΒ to build the booknimble apidocsto build the API docsnimble docsto build both