Portability has three separate meanings
- The public operation has the same semantics on each backend.
- The scalar implementation lets the source build without a SIMD backend.
- A verified target backend can use the instruction sequences listed by code-generation checks.
The library does not promise a portable vector representation or calling convention. A compiler switch, compiler version, architecture, or enabled instruction set can change the private representation.
Separate scalar results from target mechanisms
| Package | Purpose |
|---|---|
Flyology_SIMD | Public 128-bit types, operations, contracts, and scalar implementation for all ten value families. |
Backends.Native | The full 128-bit primitive contract for the selected scalar, AArch64, or x86-64 body. |
Backends.Scalar | The full 128-bit primitive contract. Each declaration renames the corresponding root scalar subprogram and matches a Backends.Native declaration. |
Flyology_SIMD.Wide | Private 256-bit types, the initial Wide operation profile, and its scalar authority. |
Flyology_SIMD.Wide.Native | Static target selection for Wide operations. Most operations compose selected 128-bit operations across private parts. The optional x86-64 AVX2 backend supplies isolated 256-bit implementations for selected byte operations, floating arithmetic, table lookup, lane movement, and both Permute_Lanes overloads. |
Algorithms.Generic_Bytes | Static composition for Find_First, Find_First_Of, Find_First_Difference, Equal, Count, Count_In_Range, Add_Saturate, and Is_ASCII. |
Algorithms.Generic_Floating | Static composition for binary32 and binary64 Scale, Clamp, AXPY, Sum, Min_Number, Max_Number, and Dot_Product complete-array loops. |
Algorithms.Runtime | One backend choice for one complete floating-array or byte-buffer operation. |
The root scalar body is the semantic reference for the complete 128-bit family. The Wide body is the scalar authority for its initial profile. Differential tests compare every current Wide Native operation group with that authority.
Wide values currently contain two private 128-bit parts. The two-part representation is private and does not define an ABI. The optional x86-64 AVX2 backend has 256-bit instruction-sequence claims for selected U8x32 and I8x32 operations, F32x8 and F64x4 arithmetic, lane movement, and both Permute_Lanes overloads.
Wide.Native uses these mechanisms:
- Bit casts apply the selected 128-bit bit cast to each private part.
- The lane-movement operations and both
Permute_Lanesoverloads use a target-selected permutation mechanism.- The AArch64 backend uses a two-register
tbltable for reverse, slides, and the one-sourcePermute_Lanesoverload. It uses a four-register table for interleave, deinterleave, and the two-sourcePermute_Lanesoverload. - On composed x86-64, reverse and the one-source overload call selected 128-bit two-source
Permute_Lanestwice. Interleave, deinterleave, and the two-source overload call selected 128-bit two-sourcePermute_Lanesfour times and selectedSelect_Valuetwice to choose between the two sources. Slides call selected 128-bit two-sourcePermute_Lanestwice and selectedSelect_Valuetwice againstZero. - The optional AVX2 implementation uses 256-bit byte shuffles, cross-half selection, and a 32-byte index map for each operation.
- The AArch64 backend uses a two-register
- Conversions compose selected 128-bit operations. Widening applies
Widen_LowandWiden_Highto the selected private part. Narrowing converts both private parts of each input. Same-width numeric conversions apply one operation to each private part. - All 80 Wide memory overloads compose selected 128-bit operations. Full, unaligned, and aligned forms call the matching operation for both private parts. Partial loads and stores choose selected full and partial operations from
Count. WhenCountdoes not exceed the private lane count, a partial load uses selectedZerofor the high part. A scalar build uses the same composition through the portable 128-bit implementation. - The composed and AArch64 byte mechanisms apply selected 128-bit operations to both private parts. The AVX2 mechanism uses isolated 256-bit subprograms for the
Add_Wrap,Subtract_Wrap,Multiply_Wrap,Add_Saturate,Subtract_Saturate,Bitwise_And,Bitwise_Or,Bitwise_Xor,Bitwise_Not,Min,Max,Equal,Less_Than,Less_Equal,Greater_Than,Greater_Equal, andSelect_Valuebyte overloads. Each byte comparison andSelect_Valueuses a relation-specific isolated leaf. TheLess_Thanleaf reverses the operands of its greater-than comparison. TheLess_Equalleaf complements that comparison with the original operand order. TheGreater_Equalleaf complements it with reversed operands. - The
F32x8andF64x4Add,Subtract,Multiply,Divide,Min_Number, andMax_Numberoverloads use two selected 128-bit operations on AArch64 and the composed x86-64 backend. The optional AVX2 mechanism uses one isolated 256-bit leaf andvzeroupper. Arithmetic uses one packed instruction. Minimum and maximum use integer classification and bit selection to preserve the public NaN and signed-zero rules. - The 32-entry
Table_Lookupuses a target-selected mechanism. AArch64 runs one two-registertbloperation for each result half. The composed x86-64 backend and a scalar build use one selected 128-bitSplat, four selected 128-bitTable_Lookupoperations, two selected 128-bitSubtract_Wrapoperations, and two selected 128-bitBitwise_Oroperations. An x86-64 build can select one isolated AVX2 256-bit implementation instead. CompressandExpanduse the target-selected compression and expansion mechanism for all ten Wide value types. On AArch64, the mechanism applies selected 128-bitTo_Bit_Maskto both private mask parts. It combines the two compact results and derives one 32-byte index map. It runs one two-registertbloperation for each 128-bit result half. The x86-64 composed and AVX2 mechanisms each derive one two-source lane map for each 128-bit result half. Each mechanism calls selected 128-bitPermute_Lanestwice and selected 128-bitSelect_Valuetwice.Select_ValueselectsZerofor each zero-fill lane.- The
Horizontal_Sumoperation computes the exact byte sum by adding two target-selected 128-bit exact byte-sum results. - The integer
Reduce_Add_Wrap,Reduce_Min, andReduce_Maxoverloads reduce both private parts with selected 128-bit reductions. The implementation splats each scalar result, combines the two vectors with selected 128-bitAdd_Wrap,Min, orMax, and extracts lane 0. This grouping applies only to associative integer reductions. - The floating
Reduce_Add,Reduce_Min_Number, andReduce_Max_Numberoverloads combine lanes in ascending lane order and do not reduce the private parts independently.Reduce_Addstarts from positive zero. The minimum-number and maximum-number reductions start from lane 0. On AArch64, dedicated Advanced SIMD sequences perform scalarfadd,fminnm, orfmaxnmoperations in ascending lane order. The x86-64 composed and AVX2 selections use dedicated SSE2 leaves that process lanes in ascending order. A scalar build uses the portable Wide implementation.
The scalar and Native tests compare these mechanisms with the Wide scalar authority.
Select primitive code at build time
The GPR scenario selects one body for Backends.Native. The choices are scalar, aarch64, and x86_64. Native architecture detection sets this value for normal Alire builds.
FLYOLOGY_SIMD_WIDE_BACKEND selects optional Wide mechanisms. Its default value, composed, preserves the target's composed implementations. The value avx2 selects isolated 256-bit byte, floating-arithmetic, lookup, and permutation implementations. The build accepts this value only with FLYOLOGY_SIMD_ARCH=x86_64 and FLYOLOGY_SIMD_AVX2=enabled.
The static Wide operations perform no runtime feature check. Before a target runs this build, CPUID must report the AVX, AVX2, and OSXSAVE bits, and XCR0 must enable XMM and YMM register state.
Algorithms.Generic_Bytes receives primitive operations as generic formal subprograms. GNAT can inline those operations into the vector loop. There is no runtime feature test in each addition, comparison, or load.
Select optional code once per buffer
Features reports which backends were compiled and which can run on the current system. Algorithms.Runtime chooses one implementation before it starts a complete-array or whole-buffer loop.
The feature-checked Algorithms.AVX2 boundary is separate from the statically selected Wide operations. Selecting the Wide AVX2 backend does not add runtime dispatch.
On x86-64, the AVX2 check requires CPUID to report the AVX, AVX2, and OSXSAVE bits. It also requires XCR0 to enable XMM and YMM register state. The AVX2 implementation is in separate objects.
AArch64 Advanced SIMD, commonly called NEON, is part of the AArch64 architecture baseline. The AArch64 backend does not need a separate NEON feature test.
Keep target code in small Ada leaves
The project first uses a verified compiler intrinsic when GNAT exposes one with the required semantics. If no suitable intrinsic exists, it uses a small Ada System.Machine_Code subprogram.
These subprograms contain only the target mechanism. Ada code implements extent checks, oversized-shift behavior, and the other public semantics. Differential tests check the result. Code-generation scripts check the emitted instruction class.
A primitive operation does not allocate memory, start a task, perform I/O, lock, wait, or read process configuration.
Add a backend
- Implement the existing
Backends.Nativedeclarations in a target source directory. - Keep value and mask representations private.
- Match every edge case in the scalar implementation.
- Run deterministic differential tests for every implemented operation.
- Run exact-tail and protected-page tests.
- Inspect the listed primitive operations and complete loops in generated code.
- Record compile, execution, and continuous-test evidence separately.
Do not add a public operation only because one processor has an instruction. Add it when its semantics are useful on every backend.