machine_vectors.h
#include <flint/machine_vectors.h>This header defines short vectors of double, float and ulong values with inline operations that compile to the SIMD instructions of the target: AVX2 and AVX-512 on x86-64, NEON on 64-bit ARM, and portable C elsewhere. On top of these it provides exact modular arithmetic on integers stored in doubles, which is the arithmetic of the small-prime FFT (fft_small), and the matrix multiplication kernels flint_sgemm and flint_dgemm, which some of the matrix multiplication methods of nmod_mat and fmpz_mat use (Floating-point matrix multiplication (gemm)).
The vector operations are internal building blocks. Each backend provides the operations its users in FLINT need, and a missing operation is not emulated, so the available set differs between backends. Code that must build everywhere should use only operations marked as available on all backends; the guide Portable vec4d code lists them, with the few cases where their results differ. Floating-point matrix multiplication describes the matrix multiplication functions in detail.
Guides#
Backends#
The header chooses one backend per translation unit from the target macros of the compiler:
| Backend | Selected when | Representation |
|---|---|---|
| AVX2 | __AVX2__ is defined and ulong has 64 bits | __m128d, __m256d, __m256i, ...; 8-lane types are pairs |
| AVX-512 | in addition __AVX512F__ | the AVX2 types, plus vec8dz, vec16fz etc. in single zmm registers |
| NEON | __ARM_NEON or _M_ARM64, and 64-bit ulong | float64x2_t, uint64x2_t, float32x4_t; wider types are pairs |
| GNU vectors | otherwise, with GCC or clang | __attribute__((vector_size)) types |
| Strict ISO C | otherwise, with other compilers | structs of arrays |
The last two are the generic backends.
- Inline only. Every vector function is a forced-inline static function, so the backend is fixed by the compiler flags of the translation unit that includes the header, which need not be those FLINT was built with.
flint_sgemmandflint_dgemmare compiled into the library and use the backend of FLINT's own build. - Word size. The AVX2 and NEON backends need 64-bit
ulong, because their integer lanes areulongand some intrinsics exist only in 64-bit mode. A 32-bit build uses a generic backend, in whichvec4nhas 32-bit lanes. - Strict ISO C. In this tier, whether an operation becomes a SIMD instruction is up to the compiler's SLP vectoriser, so its performance varies between compilers. It exists so that the header works on compilers without GNU vector extensions; GCC and clang use the GNU vector tier.
If defined before the header is included, select a generic backend even on a target with AVX2 or NEON. FLINT_MACHINE_VECTORS_STRICT_C selects the strict ISO C tier, also with GCC or clang. Both are intended for testing and profiling the portable code paths.
Defined by the header. FLINT_MACHINE_VECTORS_GENERIC is defined when a generic backend is forced or FLINT_BITS is not 64. A 64-bit target without AVX2 or NEON uses a generic backend without defining it, so a generic backend is in use if this macro is defined or if neither __AVX2__ nor NEON is. FLINT_MACHINE_VECTORS_GNU_VECTOR_EXTENSIONS is defined when the GNU vector tier is in use.
Availability#
Each function below states on which backends it is available. In these statements "AVX2" includes builds with AVX-512, and a statement such as "AVX2 and NEON" means that the generic backends lack the operation. The following table summarises them.
| Operations | AVX2 | NEON | Generic |
|---|---|---|---|
Loads, stores, set, zero, add, sub, mul, fmadd, fnmadd on vec1d, vec4d, vec8d, vec1f, vec4f, vec8f, and floor on these types except vec8d | yes | yes | yes |
The same on vec2d | floor only | yes | yes |
The same on vec16f | no | no | yes |
The same on vec8dz and vec16fz | AVX-512 | no | no |
vec4d: one, set_d4, get_index, neg, abs, div, min, max, sqrt, round, fmsub, _fused, comparisons, blendv, masks and bits, unpacklo, unpackhi, permutations, gathers | yes | yes | yes |
vec4n: loads, stores, set_n, zero, add, sub, bitwise and/or, shifts, shift_lanes_up | yes | yes | yes |
Modular arithmetic, addmod, conversions, vec4d_fnmsub, the fused unpack-and-permute operations, vec4n unpacks and permute2, the transpose macros, vec4n_store_unaligned_halves, vec8n, the remaining vec1d and vec8d operations | yes | yes | no |
round_mul, reduce_2n_to_n, vec4d_reduce_0n_to_pmhn, vec4d_addsub, vec4d_half, vec4d_same, vec4d_same_mod, vec8d_same, vec4n_mul, vec4n_horizontal_sum, vec4n_permute_3_2_1_0, vec8n_bit_shift_right, printing | yes | no | no |
vec4n_set_n4 | yes | no | yes |
Other vec2d operations, vec2n, vec1n, vec8d_one, vec8n_add, vec8n_sub | no | yes | no |
vec8d_floor | no | no | yes |
The generic backends implement the operations that flint_sgemm and flint_dgemm use, the vec4d operations that dfloat uses and the vec4n operations that mp_real uses. Everything that fft_small needs requires AVX2 or NEON.
Compiler flags and warnings#
- Compiler flags.
configurechooses-marchfrom the detected CPU.--enable-avx2adds-mavx2 -mfma.--enable-avx512implies--enable-avx2and adds-mavx512f, and-mavx512dqif the compiler accepts it. - FMA. The AVX2 versions of
fmadd,fmsub,fnmaddandfnmsubuse FMA instructions. Without__FMA__(for instance with-mavx2alone) the header still compiles, but code calling these functions does not. The_fusedvariants fall back to the C libraryfmain that case. With MSVC,/arch:AVX2is assumed to include FMA. - Contraction. The generic backends write
fmadd,fnmaddandvec4d_fmsubasa * b + c, which becomes a single FMA instruction only if the compiler contracts floating-point expressions. GCC does not in a strict ISO mode such as-std=c11, which is how FLINT is built, and clang does by default. Performance-critical code should request contraction, asmachine_vectors/gemm.cdoes with#pragma GCC optimize("fp-contract=fast"). The AVX2, AVX-512 and NEON backends use fused instructions. The_fusedvariants round once on every backend. - ABI warnings. On x86-64 without AVX, GCC and clang warn (
-Wpsabi) when a function returns or takes a 32-byte generic vector such asvec4d, because the calling convention for such types changed. The header silences this for its own definitions, but the warning can still appear in the calling code, where it is harmless as long as the functions are inlined. Compile with-Wno-psabior use a pragma, as in the examples on Portable vec4d code.
Conventions#
- Lanes are numbered from 0. A load puts the element at the lowest address in lane 0, and
vec4d_set_d4(a0, a1, a2, a3)putsa0in lane 0. In the descriptions, a_i denotes lane i ofa. - Vectors are passed and returned by value. Since some platforms pass vector types by value incorrectly, helper functions that take vectors should be declared
FLINT_FORCE_INLINE, like those of the header. - In the names of modular functions,
0nstands for [0, n),2nfor [0, 2n),pm1nfor [-n, n],pm1nofor the open interval (-n, n) andpmhnfor [-n/2, n/2]. For examplevec4d_reduce_pm1no_to_0nmaps (-n, n) to [0, n). - The modular functions and
roundassume that the floating-point rounding mode is round to nearest, the default.
Types#
Vectors of 1, 2, 4 and 8 doubles. vec1d is double. On AVX2, vec4d is __m256d and vec8d is a struct of two vec4d named e1 and e2; vec2d is __m128d but only has vec2d_floor. On NEON, vec2d is float64x2_t, vec4d is a struct of two vec2d and vec8d a struct of two vec4d. On the generic backends vec2d and vec4d are GNU vector types or structs with an array member v, and vec8d is a struct of two vec4d. Code that accesses lanes directly is not portable; use vec4d_get_index or a store.
Four table indices, as produced by vec4d_to_index and consumed by vec4d_gather. Available on all backends.
Loads, stores and gathers#
Plain and _aligned loads and stores are the same operation. On AVX2 they require the address to be aligned to the size of the register: 16 bytes for vec4f, 32 bytes for vec4d, vec8f, vec8d and vec4n, and 64 bytes for vec8dz and vec16fz. The _unaligned forms accept any address. NEON and the generic backends never require alignment. Arrays for the aligned forms can be declared with ALIGN_STRUCT or _Alignas.
ALIGN_STRUCT(x)Expands to the attribute that aligns a variable or type to x bytes: __declspec(align(x)) with MSVC and __attribute__((aligned(x))) otherwise.
vec1d vec1d_load(const double * a)vec2d vec2d_load(const double * a)vec4d vec4d_load(const double * a)vec8d vec8d_load(const double * a)vec8dz vec8dz_load(const double * a)vec1d vec1d_load_aligned(const double * a)vec2d vec2d_load_aligned(const double * a)vec4d vec4d_load_aligned(const double * a)vec8d vec8d_load_aligned(const double * a)vec8dz vec8dz_load_aligned(const double * a)vec1d vec1d_load_unaligned(const double * a)vec2d vec2d_load_unaligned(const double * a)vec4d vec4d_load_unaligned(const double * a)vec8d vec8d_load_unaligned(const double * a)vec8dz vec8dz_load_unaligned(const double * a)void vec1d_store(double * z, vec1d a)void vec2d_store(double * z, vec2d a)void vec4d_store(double * z, vec4d a)void vec8d_store(double * z, vec8d a)void vec8dz_store(double * z, vec8dz a)void vec1d_store_aligned(double * z, vec1d a)void vec2d_store_aligned(double * z, vec2d a)void vec4d_store_aligned(double * z, vec4d a)void vec8d_store_aligned(double * z, vec8d a)void vec8dz_store_aligned(double * z, vec8dz a)void vec1d_store_unaligned(double * z, vec1d a)void vec2d_store_unaligned(double * z, vec2d a)void vec4d_store_unaligned(double * z, vec4d a)void vec8d_store_unaligned(double * z, vec8d a)void vec8dz_store_unaligned(double * z, vec8dz a)Stores the lanes of a at consecutive addresses starting at z. Availability as for the loads.
vec1f vec1f_load(const float * a)vec4f vec4f_load(const float * a)vec8f vec8f_load(const float * a)vec16f vec16f_load(const float * a)vec16fz vec16fz_load(const float * a)vec1f vec1f_load_aligned(const float * a)vec4f vec4f_load_aligned(const float * a)vec8f vec8f_load_aligned(const float * a)vec16f vec16f_load_aligned(const float * a)vec16fz vec16fz_load_aligned(const float * a)vec1f vec1f_load_unaligned(const float * a)vec4f vec4f_load_unaligned(const float * a)vec8f vec8f_load_unaligned(const float * a)vec16f vec16f_load_unaligned(const float * a)vec16fz vec16fz_load_unaligned(const float * a)void vec1f_store(float * z, vec1f a)void vec4f_store(float * z, vec4f a)void vec8f_store(float * z, vec8f a)void vec16f_store(float * z, vec16f a)void vec16fz_store(float * z, vec16fz a)void vec1f_store_aligned(float * z, vec1f a)void vec4f_store_aligned(float * z, vec4f a)void vec8f_store_aligned(float * z, vec8f a)void vec16f_store_aligned(float * z, vec16f a)void vec16fz_store_aligned(float * z, vec16fz a)void vec1f_store_unaligned(float * z, vec1f a)void vec4f_store_unaligned(float * z, vec4f a)void vec8f_store_unaligned(float * z, vec8f a)void vec16f_store_unaligned(float * z, vec16f a)void vec16fz_store_unaligned(float * z, vec16fz a)vec4n vec4n_load_unaligned(const ulong * a)vec4n vec4n_load_aligned(const ulong * a)void vec4n_store_unaligned(ulong * z, vec4n a)void vec4n_store_aligned(ulong * z, vec4n a)Loads and stores of four words. All backends.
void vec4n_store_unaligned_halves(ulong * z, vec4n a)Same as vec4n_store_unaligned, but done as two 16-byte stores on AVX2. AVX2 and NEON.
vec8n vec8n_load_unaligned(const ulong * a)Loads eight words. AVX2 and NEON.
vec4d vec4d_gather(const double * t, vec4di i)vec4d_to_index converts lanes holding integers in [0, 2^{31}) to table indices, and vec4d_gather returns \{t[i_0], t[i_1], t[i_2], t[i_3]\}. All backends.
Constructing vectors and reading lanes#
vec1d vec1d_set_d(double a)vec2d vec2d_set_d(double a)vec4d vec4d_set_d(double a)vec8d vec8d_set_d(double a)vec8dz vec8dz_set_d(double a)vec1f vec1f_set_f(float a)vec4f vec4f_set_f(float a)vec8f vec8f_set_f(float a)vec16f vec16f_set_f(float a)vec16fz vec16fz_set_f(float a)Returns a vector with every lane equal to a. Availability by type as for the loads.
vec1d vec1d_zero(void)vec2d vec2d_zero(void)vec4d vec4d_zero(void)vec8d vec8d_zero(void)vec8dz vec8dz_zero(void)vec1f vec1f_zero(void)vec4f vec4f_zero(void)vec8f vec8f_zero(void)vec16f vec16f_zero(void)vec16fz vec16fz_zero(void)Returns a vector of zeros. Availability by type as for the loads.
vec4d vec4d_set_d4(double a0, double a1, double a2, double a3)vec8d vec8d_set_d8(double a0, double a1, double a2, double a3, double a4, double a5, double a6, double a7)Returns the vector with lanes a_0, a_1, \ldots in this order. vec4d_set_d4 is available on all backends, vec8d_set_d8 on AVX2 and NEON.
double vec4d_get_index(vec4d a, const int i)double vec8d_get_index(vec8d a, int i)Returns lane i of a, for 0 \le i < 4 and 0 \le i < 8 respectively. vec4d_get_index is available on all backends, vec8d_get_index on AVX2 and NEON.
void vec4d_print(vec4d a)void vec4n_print(vec4n a)Prints a to standard output. vec4d_print prints the lanes in order as {a0, a1, a2, a3} with %f. vec4n_print prints them in hexadecimal from lane 3 down to lane 0, as [hi a3_a2_a1_a0 lo]. AVX2 only.
Floating-point arithmetic#
Unless stated otherwise the operations act lane by lane and round as the corresponding IEEE 754 operation.
vec8dz vec8dz_add(vec8dz a, vec8dz b)vec8dz vec8dz_sub(vec8dz a, vec8dz b)vec8dz vec8dz_mul(vec8dz a, vec8dz b)Returns a + b, a - b and ab. Availability by type as for the loads.
vec16f vec16f_add(vec16f a, vec16f b)vec16fz vec16fz_add(vec16fz a, vec16fz b)vec16f vec16f_sub(vec16f a, vec16f b)vec16fz vec16fz_sub(vec16fz a, vec16fz b)vec16f vec16f_mul(vec16f a, vec16f b)vec16fz vec16fz_mul(vec16fz a, vec16fz b)Returns a + b, a - b and ab in single precision. Availability by type as for the loads.
Returns a / b. vec4d_div is available on all backends, the others on AVX2 and NEON.
Returns -a and |a|, which flip and clear the sign bit (also of zeros and NaNs). The vec4d versions are available on all backends, the others on AVX2 and NEON.
Returns the smaller and the larger of a and b. When a lane is unordered (a NaN is involved), AVX2 and the generic backends return the lane of b, NEON returns a NaN, and vec1d_min and vec1d_max, which are fmin and fmax, return the operand that is not a NaN. The vec4d versions are available on all backends, the others on AVX2 and NEON.
vec1d vec1d_half(vec1d a)vec4d vec4d_half(vec4d a)Returns a/2, computed as a multiplication by 0.5. vec1d_half is available on AVX2 and NEON, vec4d_half on AVX2 only.
vec1d vec1d_addsub(vec1d a, vec1d b)vec4d vec4d_addsub(vec4d a, vec4d b)Subtracts in even lanes and adds in odd lanes: vec4d_addsub returns \{a_0 - b_0, a_1 + b_1, a_2 - b_2, a_3 + b_3\} and vec1d_addsub returns a - b. vec1d_addsub is available on AVX2 and NEON, vec4d_addsub on AVX2 only.
vec4d vec4d_sqrt(vec4d a)Returns the correctly rounded square root. All backends.
Rounds to an integer in the current rounding mode: to the nearest integer, ties to even, by default. On NEON, vec4d_round and vec8d_round always round to nearest with ties to even. vec4d_round is available on all backends, the others on AVX2 and NEON.
Returns \lfloor a \rfloor. vec8d_floor and vec16f_floor are available on the generic backends only, the z versions with AVX-512 only, and the others on all backends (vec2d_floor also on AVX2).
Fused multiply-add#
vec1d vec1d_fmadd(vec1d a, vec1d b, vec1d c)vec2d vec2d_fmadd(vec2d a, vec2d b, vec2d c)vec4d vec4d_fmadd(vec4d a, vec4d b, vec4d c)vec8d vec8d_fmadd(vec8d a, vec8d b, vec8d c)vec8dz vec8dz_fmadd(vec8dz a, vec8dz b, vec8dz c)vec1d vec1d_fnmadd(vec1d a, vec1d b, vec1d c)vec2d vec2d_fnmadd(vec2d a, vec2d b, vec2d c)vec4d vec4d_fnmadd(vec4d a, vec4d b, vec4d c)vec8d vec8d_fnmadd(vec8d a, vec8d b, vec8d c)vec8dz vec8dz_fnmadd(vec8dz a, vec8dz b, vec8dz c)vec1f vec1f_fmadd(vec1f a, vec1f b, vec1f c)vec4f vec4f_fmadd(vec4f a, vec4f b, vec4f c)vec8f vec8f_fmadd(vec8f a, vec8f b, vec8f c)vec16f vec16f_fmadd(vec16f a, vec16f b, vec16f c)vec16fz vec16fz_fmadd(vec16fz a, vec16fz b, vec16fz c)vec1f vec1f_fnmadd(vec1f a, vec1f b, vec1f c)vec4f vec4f_fnmadd(vec4f a, vec4f b, vec4f c)vec8f vec8f_fnmadd(vec8f a, vec8f b, vec8f c)vec16f vec16f_fnmadd(vec16f a, vec16f b, vec16f c)vec16fz vec16fz_fnmadd(vec16fz a, vec16fz b, vec16fz c)Single-precision versions, with the same rounding behaviour. Availability by type as for the loads.
vec1d vec1d_fmsub(vec1d a, vec1d b, vec1d c)vec4d vec4d_fmsub(vec4d a, vec4d b, vec4d c)vec8d vec8d_fmsub(vec8d a, vec8d b, vec8d c)vec1d vec1d_fnmsub(vec1d a, vec1d b, vec1d c)vec4d vec4d_fnmsub(vec4d a, vec4d b, vec4d c)vec8d vec8d_fnmsub(vec8d a, vec8d b, vec8d c)Returns ab - c and -ab - c, rounded once except for vec4d_fmsub on the generic backends. NEON computes ab - c as -(c - ab), which differs from vec4d_fmsub_fused in the sign of an exact zero. vec4d_fmsub is available on all backends, the others on AVX2 and NEON.
vec4d vec4d_fmadd_fused(vec4d a, vec4d b, vec4d c)vec4d vec4d_fmsub_fused(vec4d a, vec4d b, vec4d c)vec4d vec4d_fnmadd_fused(vec4d a, vec4d b, vec4d c)Returns ab + c, ab - c and -ab + c rounded once on every backend, like fma(a, b, c), fma(a, b, -c) and fma(-a, b, c), including the sign of an exact zero. On AVX2 without FMA instructions they call fma lane by lane. All backends.
Comparisons, masks and bits#
A mask has all bits set in the lanes where a condition holds and all bits clear elsewhere. The vec4d operations in this section behave identically on all backends.
vec4d vec4d_cmp_eq(vec4d a, vec4d b)vec4d vec4d_cmp_ne(vec4d a, vec4d b)vec4d vec4d_cmp_lt(vec4d a, vec4d b)vec4d vec4d_cmp_le(vec4d a, vec4d b)vec4d vec4d_cmp_gt(vec4d a, vec4d b)vec4d vec4d_cmp_ge(vec4d a, vec4d b)vec4d vec4d_cmp_nlt(vec4d a, vec4d b)vec4d vec4d_cmp_nle(vec4d a, vec4d b)vec4d vec4d_cmp_ngt(vec4d a, vec4d b)vec4d vec4d_cmp_nge(vec4d a, vec4d b)Returns the mask of the lanes where a_i = b_i, a_i \ne b_i, a_i < b_i, and so on. The predicates eq, lt, le, gt and ge are false when a lane of a or b is a NaN, and their negations ne, nlt, nle, ngt and nge are true. All backends.
vec1d vec1d_blendv(vec1d a, vec1d b, vec1d c)vec4d vec4d_blendv(vec4d a, vec4d b, vec4d c)vec8d vec8d_blendv(vec8d a, vec8d b, vec8d c)Returns, in each lane, b_i if the sign bit of c_i is set and a_i otherwise, so a mask from a comparison selects b where the comparison holds. vec1d_blendv instead tests c >= 0, which differs for c = -0 (selects a) and for NaNs (selects b). vec4d_blendv is available on all backends, the others on AVX2 and NEON.
int vec4d_movemask(vec4d a)Returns the sign bits of the lanes as an integer, lane i giving bit i. All backends.
int vec1d_same(double a, double b)int vec4d_same(vec4d a, vec4d b)int vec8d_same(vec8d a, vec8d b)Returns whether every lane of a compares equal to the same lane of b with == (so +0 = -0, and a NaN is never equal). vec1d_same is available on AVX2 and NEON, the others on AVX2 only.
vec4d vec4d_bit_and(vec4d a, vec4d b)vec4d vec4d_bit_or(vec4d a, vec4d b)vec4d vec4d_bit_xor(vec4d a, vec4d b)vec4d vec4d_bit_andnot(vec4d a, vec4d b)Bitwise operations on the 64-bit patterns of the lanes; vec4d_bit_andnot computes (~a) & b. All backends.
vec4d vec4d_bits_set_u64(uint64_t a)vec4d vec4d_bits_add(vec4d a, vec4d b)vec4d vec4d_bits_sub(vec4d a, vec4d b)vec4d vec4d_bits_shl(vec4d a, int n)vec4d vec4d_bits_shr(vec4d a, int n)Operations on the lanes viewed as uint64_t bit patterns: a broadcast, addition and subtraction modulo 2^{64}, and logical shifts by 0 \le n < 64. All backends.
Permutations#
vec4d vec4d_unpacklo(vec4d a, vec4d b)vec4d vec4d_unpackhi(vec4d a, vec4d b)Returns \{a_0, b_0, a_2, b_2\} and \{a_1, b_1, a_3, b_3\}. All backends.
vec4d_permute_i_j_k_l returns \{a_i, a_j, a_k, a_l\}. Only these six permutations exist. All backends.
vec4d vec4d_permute2_0_2(vec4d a, vec4d b)vec4d vec4d_permute2_1_3(vec4d a, vec4d b)Viewing a and b as four 128-bit halves a_0a_1, a_2a_3, b_0b_1, b_2b_3 numbered 0 to 3, vec4d_permute2_i_j returns halves i and j: \{a_0, a_1, b_0, b_1\} and \{a_2, a_3, b_2, b_3\}. All backends.
An unpack followed by a permutation, fused where the backend allows: \{u_0, u_2, v_0, v_2\}, \{u_1, u_3, v_1, v_3\}, \{v_2, v_0, u_2, u_0\} and \{v_3, v_1, u_3, u_1\}. AVX2 and NEON.
VEC4D_TRANSPOSE(z0, z1, z2, z3, a0, a1, a2, a3)Sets the vec4d variables z0, ..., z3 to the rows of the transpose of the 4 \times 4 matrix with rows a0, ..., a3. The outputs may be the inputs. AVX2 and NEON.
vec4n vec4n_unpacklo(vec4n a, vec4n b)vec4n vec4n_unpackhi(vec4n a, vec4n b)vec4n vec4n_permute2_0_2(vec4n a, vec4n b)vec4n vec4n_permute2_1_3(vec4n a, vec4n b)Returns \{a_3, a_2, a_1, a_0\}. AVX2 only.
VEC4N_TRANSPOSE(z0, z1, z2, z3, a0, a1, a2, a3)The vec4n version of VEC4D_TRANSPOSE. AVX2 and NEON.
Integer vectors#
Arithmetic on vec4n lanes is modulo 2^{\mathtt{FLINT\_BITS}}.
vec4n vec4n_zero(void)Returns a vector with all lanes equal to a, or to zero. vec8n_set_n is available on AVX2 and NEON, the others on all backends.
Returns \{a_0, a_1, a_2, a_3\}. AVX2 and the generic backends; not NEON.
Returns a + b and a - b lane by lane, modulo 2^{\mathtt{FLINT\_BITS}}. All backends.
vec4n vec4n_bit_and(vec4n a, vec4n b)vec4n vec4n_bit_or(vec4n a, vec4n b)vec8n vec8n_bit_and(vec8n a, vec8n b)Bitwise and and or. vec8n_bit_and is available on AVX2 and NEON, the others on all backends.
vec4n vec4n_bit_shift_left(vec4n a, ulong b)vec4n vec4n_bit_shift_right(vec4n a, ulong b)vec8n vec8n_bit_shift_right(vec8n a, ulong b)Logical shifts of every lane by the same count 0 \le b < \mathtt{FLINT\_BITS}. vec8n_bit_shift_right is available on AVX2 only, the others on all backends.
Logical shift right by 32 bits. These are macros on AVX2 and functions on NEON.
vec4n vec4n_shift_lanes_up(vec4n a, vec4n b)Returns \{b_3, a_0, a_1, a_2\}: the lanes of a moved up by one, with the top lane of b entering at the bottom, as when passing a carry from one vector to the next. All backends.
Returns the 64-bit products of the low 32 bits of each lane, (u_i \bmod 2^{32})(v_i \bmod 2^{32}). AVX2 only.
Returns a_0 + a_1 + a_2 + a_3 modulo 2^{64}. AVX2 only.
vec4n vec4n_addmod(vec4n a, vec4n b, vec4n n)vec8n vec8n_addmod(vec8n a, vec8n b, vec8n n)Returns (a + b) \bmod n in [0, n) for a, b < n, with no condition on n. AVX2 and NEON.
vec4n vec4n_addmod_limited(vec4n a, vec4n b, vec4n n)vec8n vec8n_addmod_limited(vec8n a, vec8n b, vec8n n)The same for n < 2^{63}, which allows a cheaper test. AVX2 and NEON.
Conversions#
Converts between double lanes and ulong lanes holding integers in [0, 2^{52}), which are represented exactly in both. Outside this range the results are unspecified: most of these conversions work on the bit pattern of 2^{52}. AVX2 and NEON.
Modular arithmetic in floating point#
These functions compute modulo a positive integer n on integers stored exactly in doubles, with ninv the double-precision value of 1.0/n. They form the arithmetic of fft_small and are available on AVX2 and NEON only. Modular arithmetic in floating point states and proves the ranges below. None of the conditions are checked, and none of the functions require n to be odd or prime.
Returns an integer r \equiv ab (mulmod) or r \equiv -ab (nmulmod) modulo n, for integers a, b with |ab| < 2^{103} and 1 \le n < 2^{50}, given ninv = 1.0 / n. The result satisfies |r| \le n/2 + \eta|ab| with \eta = (1 + 2^{-53})^3 - 1 < 3.0001 \cdot 2^{-53}. In particular, if n < 2^{49} or fft_small_mulmod_satisfies_bounds(n) holds, then |ab| < 2n^2 gives |r| < n and |ab| < 4n^2 gives |r| < 3n/2. Larger products still give a correct representative, only a larger one. AVX2 and NEON.
Like mulmod and nmulmod, with the quotient rounded by round_mul. Requires |ab| < 2n^2 and n < 2^{50}. The result then satisfies |r| \le n/2 + \eta|ab| as for mulmod, and also |r| \le n/2 + 2.0001 \cdot 2^{-53}|ab| on the magic-constant path, so |r| < n if n < 2^{49} or fft_small_mulmod_satisfies_bounds(n) holds. With the magic constant, a product beyond 2^{51} n in absolute value gives a wrong result, possibly not an integer. With native rounding, and always on NEON, these functions are mulmod and nmulmod. AVX2 and NEON.
vec1d vec1d_round_mul(vec1d a, vec1d b)vec4d vec4d_round_mul(vec4d a, vec4d b)Returns ab rounded to an integer. With the magic constant (see FLINT_FFT_SMALL_ROUND_USE_NATIVE) this is the nearest integer to the exact product, with ties to even, and requires |ab| \le 2^{51}; a larger product gives a wrong value or a non-integer. With native rounding it is round(mul(a, b)), which rounds twice and has no size condition. AVX2 only.
FLINT_FFT_SMALL_ROUND_USE_NATIVE is set in the flint-mparam.h of each target: 0 (magic constant) for most Intel targets, 1 (native rounding) for AMD, ARM and generic targets. The header defaults it to 1. Defining FLINT_ROUND_MUL_USE_ROUND or FLINT_ROUND_MUL_USE_MAGIC forces either choice, for testing.
vec1d vec1d_reduce_to_pm1n(vec1d a, vec1d n, vec1d ninv)vec4d vec4d_reduce_to_pm1n(vec4d a, vec4d n, vec4d ninv)vec8d vec8d_reduce_to_pm1n(vec8d a, vec8d n, vec8d ninv)vec1d vec1d_reduce_to_pm1no(vec1d a, vec1d n, vec1d ninv)vec4d vec4d_reduce_to_pm1no(vec4d a, vec4d n, vec4d ninv)vec8d vec8d_reduce_to_pm1no(vec8d a, vec8d n, vec8d ninv)Returns an integer r = a - qn \equiv a \pmod n, with q an approximation of a/n rounded to an integer, for an integer |a| < 2^{53} and an integer n \ge 4. The two functions are identical. The result satisfies |r| < n/2 + 2, hence |r| < n. On AVX2 the quotient is computed with round_mul, which with the magic constant needs |a| / n \le 2^{51}; this is why n \ge 4 is required. AVX2 and NEON.
vec1d vec1d_reduce_to_0n(vec1d a, vec1d n, vec1d ninv)vec4d vec4d_reduce_to_0n(vec4d a, vec4d n, vec4d ninv)vec8d vec8d_reduce_to_0n(vec8d a, vec8d n, vec8d ninv)int vec1d_same_mod(vec1d a, vec1d b, vec1d n, vec1d ninv)int vec4d_same_mod(vec4d a, vec4d b, vec4d n, vec4d ninv)Returns whether a_i \equiv b_i \pmod n in every lane, by comparing the results of reduce_to_0n, with the same conditions. vec1d_same_mod is available on AVX2 and NEON, vec4d_same_mod on AVX2 only.
vec1d vec1d_reduce_2n_to_n(vec1d a, vec1d n)vec4d vec4d_reduce_2n_to_n(vec4d a, vec4d n)vec8d vec8d_reduce_2n_to_n(vec8d a, vec8d n)Returns a \bmod n in [0, n) for a \in [0, 2n). AVX2 only.
Returns a or a - n, whichever lies in (-n/2, n/2], for a \in [0, n]. vec1d_reduce_0n_to_pmhn is available on AVX2 and NEON, vec4d_reduce_0n_to_pmhn on AVX2 only.
Returns a, a - n or a + n, whichever lies in [-n/2, n/2], for |a| \le 3n/2 (in particular for a \in [-n, n]). AVX2 and NEON.
Matrix multiplication#
These functions compute a matrix product in single or double precision. They are always available: when FLINT is built with BLAS they call it by default, and otherwise FLINT's own kernels are used. They are building blocks for exact linear algebra over \mathbb{Z} and \mathbb{Z}/n\mathbb{Z}, for example in nmod_mat_mul_blas and fmpz_mat_mul_blas.
All of them set C = AB for row-major matrices, where C is m \times n, A is m \times k and B is k \times n, and lda, ldb and ldc are the distances between the starts of consecutive rows. C is overwritten and must not overlap A or B. Floating-point matrix multiplication compares this interface with cblas_dgemm and discusses threading and exactness.
void flint_sgemm(slong m, slong k, slong n, const float * A, slong lda, const float * B, slong ldb, float * C, slong ldc)void flint_dgemm(slong m, slong k, slong n, const double * A, slong lda, const double * B, slong ldb, double * C, slong ldc)Sets C = AB, with the BLAS or the FLINT implementation according to flint_gemm_use_blas.
void flint_sgemm_fallback(slong m, slong k, slong n, const float * A, slong lda, const float * B, slong ldb, float * C, slong ldc)void flint_dgemm_fallback(slong m, slong k, slong n, const double * A, slong lda, const double * B, slong ldb, double * C, slong ldc)Sets C = AB with FLINT's own kernels, which are always available, handle any dimensions and leading dimensions without padding or alignment, and use up to flint_get_num_threads() threads of FLINT's thread pool.
void flint_sgemm_blas(slong m, slong k, slong n, const float * A, slong lda, const float * B, slong ldb, float * C, slong ldc)void flint_dgemm_blas(slong m, slong k, slong n, const double * A, slong lda, const double * B, slong ldb, double * C, slong ldc)Sets C = AB with cblas_sgemm or cblas_dgemm. Throws an exception if FLINT was built without BLAS.
Selects the implementation used by flint_sgemm and flint_dgemm: initialised to 1 if FLINT was built with BLAS and 0 otherwise. It may be changed at run time, for example to compare the two implementations; setting it to 1 without BLAS makes every call throw.
NEON-only operations#
The NEON backend builds vec4d and vec8d from vec2d and exposes the vec2d, vec2n and vec1n operations it uses. They have the semantics of the vec4d, vec4n and vec1d operations of the same names.
vec2d vec2d_set_d2(double a0, double a1)vec4d vec4d_set_vec2d2(vec2d a, vec2d b)vec8d vec8d_set_vec4d2(vec4d a, vec4d b)double vec2d_get_index(vec2d a, int i)int vec2d_same(vec2d a, vec2d b)Constructors and lane access, NEON only. vec4d_set_vec2d2 and vec8d_set_vec4d2 concatenate two halves, a giving the low lanes.
vec2d vec2d_half(vec2d a)vec2d vec2d_sqrt(vec2d a)vec2d vec2d_fmsub(vec2d a, vec2d b, vec2d c)vec2d vec2d_fnmsub(vec2d a, vec2d b, vec2d c)vec2d vec2d_blendv(vec2d a, vec2d b, vec2d c)Arithmetic on two doubles, NEON only. vec2d_fmsub and vec2d_fnmsub are computed as negated fused operations, which the header notes may be slower.
vec2d vec2d_cmp_eq(vec2d a, vec2d b)vec2d vec2d_cmp_ne(vec2d a, vec2d b)vec2d vec2d_cmp_lt(vec2d a, vec2d b)vec2d vec2d_cmp_le(vec2d a, vec2d b)vec2d vec2d_cmp_gt(vec2d a, vec2d b)vec2d vec2d_cmp_ge(vec2d a, vec2d b)vec2d vec2d_cmp_nlt(vec2d a, vec2d b)vec2d vec2d_cmp_nle(vec2d a, vec2d b)vec2d vec2d_cmp_ngt(vec2d a, vec2d b)vec2d vec2d_cmp_nge(vec2d a, vec2d b)vec2d vec2d_bit_and(vec2d a, vec2d b)vec2d vec2d_bit_or(vec2d a, vec2d b)vec2d vec2d_bit_xor(vec2d a, vec2d b)vec2d vec2d_bit_andnot(vec2d a, vec2d b)int vec2d_movemask(vec2d a)vec2d vec2d_bits_set_u64(uint64_t a)vec2d vec2d_bits_add(vec2d a, vec2d b)vec2d vec2d_bits_sub(vec2d a, vec2d b)vec2d vec2d_bits_shl(vec2d a, int n)vec2d vec2d_bits_shr(vec2d a, int n)Comparisons, masks and bit operations on two doubles, with the semantics of the vec4d versions. NEON only.
vec2d vec2d_unpacklo(vec2d a, vec2d b)vec2d vec2d_unpackhi(vec2d a, vec2d b)vec8d vec8d_unpacklo(vec8d a, vec8d b)vec8d vec8d_unpackhi(vec8d a, vec8d b)vec2d_unpacklo returns \{a_0, b_0\} and vec2d_unpackhi returns \{a_1, b_1\}; the vec8d versions act on each vec4d half. NEON only.
vec2d vec2d_reduce_to_pm1n(vec2d a, vec2d n, vec2d ninv)vec2d vec2d_reduce_to_pm1no(vec2d a, vec2d n, vec2d ninv)vec2d vec2d_reduce_to_0n(vec2d a, vec2d n, vec2d ninv)int vec2d_same_mod(vec2d a, vec2d b, vec2d n, vec2d ninv)Modular arithmetic on two doubles, with native rounding and the semantics of the vec4d versions. NEON only.
vec2n vec2n_load_unaligned(const ulong * a)void vec2n_store_unaligned(ulong * z, vec2n a)void vec1n_store_unaligned(ulong * z, vec1n a)vec2n vec2n_bit_and(vec2n a, vec2n b)vec2n vec2n_bit_or(vec2n a, vec2n b)vec2n vec2n_bit_shift_left(vec2n a, ulong b)vec2n vec2n_bit_shift_right(vec2n a, ulong b)vec2n vec2n_unpacklo(vec2n a, vec2n b)vec2n vec2n_unpackhi(vec2n a, vec2n b)vec2n vec2n_addmod(vec2n a, vec2n b, vec2n n)vec2n vec2n_addmod_limited(vec2n a, vec2n b, vec2n n)vec1n vec1n_addmod(vec1n a, vec1n b, vec1n n)Operations on one, two and eight words, with the semantics of the vec4n operations of the same names. NEON only.
The FLINT team, “machine_vectors.h (module overview),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/machine_vectors/index.html (generated 2026-10-20).
@misc{flint-doc-machine-vectors-index,
author = {{The FLINT team}},
title = {{machine\_vectors.h (module overview)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/machine_vectors/index.html},
note = {Generated 2026-10-20}
}