The guide explains the operation groups through numeric and byte examples. This reference lists the wider API and defines each result independently of the selected backend.
Public vector and mask types
Each root-package value vector is 128 bits. The type name gives the scalar type and lane count. For example, I32x4 contains four signed 32-bit lanes.
| Lane type | Value types | Mask type |
|---|---|---|
| 8-bit integer | U8x16, I8x16 | Mask_8x16 |
| 16-bit integer | U16x8, I16x8 | Mask_16x8 |
| 32-bit integer or float | U32x4, I32x4, F32x4 | Mask_32x4 |
| 64-bit integer or float | U64x2, I64x2, F64x2 | Mask_64x2 |
Lane 0 is the first array element that a load reads. A compact mask uses bit 0 for lane 0. Bits above the mask lane count are ignored when you construct a mask.
Check the initial 256-bit profile
The Flyology_SIMD.Wide child package supplies ten private 256-bit value types and four mask types:
| Lane type | Value types | Mask type |
|---|---|---|
| 8-bit integer | U8x32, I8x32 | Mask_8x32 |
| 16-bit integer | U16x16, I16x16 | Mask_16x16 |
| 32-bit integer or float | U32x8, I32x8, F32x8 | Mask_32x8 |
| 64-bit integer or float | U64x4, I64x4, F64x4 | Mask_64x4 |
| Group | Current Wide profile |
|---|---|
| Construction and access | Zero, Splat, From_Lanes, To_Lanes, Extract, and Replace |
| Comparison and selection | Equal, Less_Than, Less_Equal, Greater_Than, Greater_Equal, floating Unordered, and Select_Value |
| Lane rearrangement | Reverse; interleave and deinterleave; one-source maps; Select_Left_Lane, Select_Right_Lane, Make_Two_Source_Lane_Map, and three-argument Permute_Lanes; lane slides |
| Mask and memory | Mask operations; compression and expansion; typed full, aligned, unaligned, and partial memory operations |
| Bit reinterpretation | Bit_Cast between signed, unsigned, and floating types that have the same lane width and lane count |
| Width changes | Widen_Low and Widen_High between adjacent integer types with the same signedness and from F32x8 to F64x4; Narrow_Truncate between adjacent integer types with the same signedness; Narrow_Saturate for same-signedness and signed-to-unsigned integer narrowing; Narrow_Round from two F64x4 inputs to F32x8 |
| Numeric-domain changes | Convert_Round from I32x8 or U32x8 to F32x8 and from I64x4 or U64x4 to F64x4; Convert_Truncate_Saturate in the reverse directions; Convert_Saturate between signed and unsigned integer types at all four lane widths |
| Byte-table lookup | 32-entry Table_Lookup for U8x32 |
| Exact byte sum | Horizontal_Sum for U8x32, with a Natural result from 0 through 8,160 |
| Integer types | Wrapping and saturating arithmetic; bitwise operations; shifts; ordered comparisons; minimum; maximum; add, minimum, and maximum reductions |
| Floating types | Arithmetic; ordered and unordered comparisons; number minimum and maximum; add, number-minimum, and number-maximum reductions |
An operation with the same name as a 128-bit operation has the same lane semantics. Wide two-source maps can select and repeat lanes from either input. Wide bit casts preserve every lane bit and position between signed, unsigned, and floating types that have the same lane width and lane count. Wide conversions use the same rounding, saturation, exceptional-input, and lane-order rules as the corresponding 128-bit operations.
A full Wide load or store transfers one complete 256-bit vector. An aligned Wide load or store requires 32-byte alignment. Wide partial operations retain the root exact-extent and zero-count rules. Flyology_SIMD.Wide.Native composes selected 128-bit memory operations across private parts. The parts are not a public representation or ABI.
The composed Wide backend is the default. It applies selected 128-bit operations to both private parts. On x86-64, FLYOLOGY_SIMD_WIDE_BACKEND=avx2 statically selects isolated 256-bit implementations for these Wide.Native byte overloads:
Add_Wrap,Subtract_Wrap, andMultiply_WrapforU8x32andI8x32;Add_SaturateandSubtract_Saturatefor both byte types;Bitwise_And,Bitwise_Or,Bitwise_Xor, andBitwise_Notfor both byte types;MinandMaxfor both byte types;Equal,Less_Than,Less_Equal,Greater_Than, andGreater_Equalfor both byte types;Select_Valuefor both byte types; and- the 32-entry
Table_LookupforU8x32.
The same AVX2 selection supplies isolated 256-bit Add, Subtract, Multiply, Divide, Min_Number, and Max_Number implementations for F32x8 and F64x4. Arithmetic uses one packed AVX operation. Minimum and maximum use integer classification and bit selection to preserve the documented NaN and signed-zero results. Each leaf ends with vzeroupper. The composed x86-64 and AArch64 backends retain two selected 128-bit operations.
Wide Table_Lookup uses a target-selected mechanism. On AArch64, one two-register tbl operation produces each result half. The composed x86-64 backend and a scalar build use one selected 128-bit Splat to construct a vector whose lanes contain 16. They use four selected 128-bit Table_Lookup operations, two selected Subtract_Wrap operations, and two selected Bitwise_Or operations. The low-table lookups accept indexes 0 through 15. Subtracting 16 makes the high-table lookups accept indexes 16 through 31. Both lookups return zero outside their ranges, so every original index above 31 produces zero. The optional AVX2 backend uses one isolated 256-bit implementation.
Wide Compress and Expand use the target-selected compression and expansion mechanism. On AArch64, the mechanism applies selected 128-bit To_Bit_Mask to both private mask parts. It combines the compact results and derives one 32-byte index map. It runs one two-register tbl operation 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-bit Permute_Lanes twice and selected 128-bit Select_Value twice. Select_Value selects Zero for each zero-fill lane.
The seven lane-movement operations are Reverse_Lanes, both slide operations, both interleave operations, and both deinterleave operations. These operations and both Permute_Lanes overloads use the target-selected permutation mechanism. All three backends support all ten value types.
On AArch64, reverse, both slide operations, and the one-source Permute_Lanes overload use one tbl operation with a two-register table for each result half. Both interleave operations, both deinterleave operations, and the two-source Permute_Lanes overload use one tbl operation with a four-register table for each result half.
Composed x86-64 uses selected 128-bit operations:
- 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-source
Permute_Lanesfour times and selectedSelect_Valuetwice to choose between the two sources. - Slides call selected 128-bit two-source
Permute_Lanestwice and selectedSelect_Valuetwice againstZero.
The optional AVX2 implementation uses two byte shuffles and one cross-half selection for each one-source operation. It uses four byte shuffles and two cross-half selections for each two-source operation. The AArch64 and optional AVX2 implementations derive a 32-byte index map.
The byte operations and the six floating operations described above, Table_Lookup, compression and expansion, and lane movement use their documented mechanisms. Other Wide operations compose selected 128-bit operations or use fixed-width Ada code.
In the AVX2 byte mechanism, each comparison and Select_Value uses a relation-specific isolated 256-bit leaf. The Less_Than leaf reverses the operands of its greater-than comparison. The Less_Equal leaf complements that comparison with the original operand order. The Greater_Equal leaf complements it with reversed operands. AVX2 has no packed byte multiplication instruction. The wrapping multiply separates byte lanes in 16-bit words, uses word multiplication, and retains the low eight bits of each result.
Before a target runs this build, CPUID must report the AVX, AVX2, and OSXSAVE bits, and XCR0 must enable XMM and YMM register state. The Algorithms.AVX2 package checks these conditions before each public complete-array or whole-buffer entry. Algorithms.Runtime selects one safe algorithm for each complete operation.
The build rejects other configurations that select the Wide AVX2 backend. If every deployment target meets the CPU and OS requirement, build the x86-64 selection with:
alr build --release -- -XFLYOLOGY_SIMD_ARCH=x86_64 \
-XFLYOLOGY_SIMD_AVX2=enabled \
-XFLYOLOGY_SIMD_WIDE_BACKEND=avx2
Chapter 2 demonstrates one-source permutation and two-source permutation. Selecting AVX2 with the three build variables above changes the lowering, not the Ada source or its results.
Convert bits and lane widths explicitly
Bit_Cast preserves each lane's bits and position. Source and result types must have the same lane width and count.
For example, the F32x4-to-U32x4 overload returns four IEEE binary32 encodings. It does not perform numeric conversion.
Integer widening preserves each numeric lane value. It zero-extends unsigned lanes and sign-extends signed lanes. With the platform's default gradual-underflow environment, floating widening converts each finite F32 value exactly to F64. It preserves signed zero and infinity. A NaN input produces a NaN result, but payload and signaling state are unspecified. The operation can update floating-point exception-status flags.
| Operation | Result | Available conversions |
|---|---|---|
Bit_Cast | Preserves lane bits | Signed, unsigned, and floating types with the same lane shape |
Widen_Low | Widens the low source half | Adjacent integer widths with the same signedness; F32x4 to F64x2 |
Widen_High | Widens the high source half | Adjacent integer widths with the same signedness; F32x4 to F64x2 |
Narrow_Truncate | Keeps the low result-width bits | Adjacent integer widths with the same signedness |
Narrow_Saturate | Clamps to the result range | Same-signedness and signed-to-unsigned integer narrowing |
Narrow_Round | Rounds binary64 lanes to binary32 | Two F64x2 inputs to one F32x4 result |
Convert_Round | Rounds integer values to floating point | I32x4 or U32x4 to F32x4; I64x2 or U64x2 to F64x2 |
Convert_Truncate_Saturate | Truncates toward zero, then clamps | F32x4 to I32x4 or U32x4; F64x2 to I64x2 or U64x2 |
Convert_Saturate | Converts signedness and clamps | Signed to unsigned and unsigned to signed for 8-, 16-, 32-, and 64-bit lanes |
Each narrowing operation takes two source vectors. Low supplies the low result half, and High supplies the high result half.
The 128-bit and Wide packages provide the same conversion shapes at their respective vector widths. The conversion chapter provides worked examples and explains the Wide lane mapping. The semantic compatibility document defines rounding environments, infinity, NaN, signed-zero, and saturation results.
Integer operation coverage
Reverse_Bytes is the 128-bit U8x16 compatibility name for Reverse_Lanes. Wide types use Wide.Reverse_Lanes; the absence of a Wide Reverse_Bytes alias is not a missing primitive.
The root Horizontal_Sum overload returns the exact sum of 16 unsigned byte lanes. The Wide overload returns the exact sum of 32 unsigned byte lanes. Wide.Reduce_Add_Wrap returns the sum modulo 256 as U8 instead:
package Wide renames Flyology_SIMD.Wide;
Bytes : constant Wide.U8x32 := Wide.Splat (255);
Exact : constant Natural := Wide.Horizontal_Sum (Bytes); -- 8_160
Wrapped : constant Flyology_SIMD.U8 :=
Wide.Reduce_Add_Wrap (Bytes); -- 224
The exact result cannot overflow its documented range. The wrapping result is useful only when modulo-256 arithmetic is intended.
The Wide Native integer reductions reduce each private 128-bit part with the selected 128-bit reduction. The implementation splats each scalar result, combines the two vectors with selected 128-bit Add_Wrap, Min, or Max, and extracts lane 0. This grouping preserves the scalar result because these integer operations are associative.
A logical shift returns zero in each lane when its count is equal to or greater than the lane width. An arithmetic right shift returns the sign fill for those counts.
For each result lane, the U8x16 overload reads the unsigned value from the index lane at the same position. A value from 0 through 15 selects the table lane whose lane index equals that value. A larger value produces zero. The operation does not reduce the index modulo 16.
Hex_Digits : constant U8x16 := From_Lanes
([Character'Pos ('0'), Character'Pos ('1'), Character'Pos ('2'),
Character'Pos ('3'), Character'Pos ('4'), Character'Pos ('5'),
Character'Pos ('6'), Character'Pos ('7'), Character'Pos ('8'),
Character'Pos ('9'), Character'Pos ('A'), Character'Pos ('B'),
Character'Pos ('C'), Character'Pos ('D'), Character'Pos ('E'),
Character'Pos ('F')]);
Indices : constant U8x16 :=
From_Lanes ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255]);
Encoded : constant U8x16 := Table_Lookup (Hex_Digits, Indices);
-- Encoded lanes 0 .. 14 contain ASCII 0 through E.
-- Encoded lane 15 is zero because index 255 is out of range.
The maintained table_lookup.adb example maps all 16 hexadecimal digits and then checks an out-of-range index.
Use a 32-entry table for five-bit values
The Wide overload uses a 32-byte table. Indexes from 0 through 31 select table lanes, and larger indexes produce zero. Each result lane uses the index at the same lane position.
The maintained wide_table_lookup.adb example maps 32 five-bit digit values through a Base32 alphabet. The first 12 index lanes select FLYOLOGYSIMD. Lanes 12 through 31 select A through T. A second lookup replaces index lane 31 with 32, so result lane 31 is zero. The example demonstrates the lookup step only; it is not a complete Base32 encoder.
Base32 digits: FLYOLOGYSIMDABCDEFGHIJKLMNOPQRST
out-of-range lane: 0
Floating-point operation coverage
| Group | Operations | Types |
|---|---|---|
| Construction | Zero, Splat, From_Lanes, To_Lanes, Extract, Replace | F32x4, F64x2 |
| Arithmetic | Add, Subtract, Multiply, Divide | F32x4, F64x2 |
| Comparison | Equal, Less_Than, Less_Equal, Greater_Than, Greater_Equal, Unordered | F32x4, F64x2 |
| Selection and limits | Select_Value, Min_Number, Max_Number | F32x4, F64x2 |
| Reductions | Reduce_Add, Reduce_Min_Number, Reduce_Max_Number | F32x4, F64x2 |
| Fixed lane arrangements | Reverse_Lanes, Interleave_Low, Interleave_High, Deinterleave_Even, Deinterleave_Odd | F32x4, F64x2 |
| One-source lane maps | Make_Lane_Map, Permute_Lanes | F32x4, F64x2 |
| Two-source lane maps | Select_Left_Lane, Select_Right_Lane, Make_Two_Source_Lane_Map, Permute_Lanes | F32x4, F64x2 |
| Lane slides | Slide_Lanes_Toward_Low, Slide_Lanes_Toward_High | F32x4, F64x2 |
| Mask compression and expansion | Compress, Expand | F32x4, F64x2 |
The build does not enable -ffast-math. Ordered comparisons are false when either lane is NaN. Unordered is true for that case.
Min_Number and Max_Number return the numeric input when only one quiet NaN is present. They select negative zero for a minimum and positive zero for a maximum. The API reference and semantic document give the full NaN rules.
The following example shows ordered reductions on 128-bit vectors:
Samples : constant F32x4 := From_Lanes ([4.5, -2.0, 7.25, 1.0]);
Smallest : constant F32 := Reduce_Min_Number (Samples); -- -2.0
Largest : constant F32 := Reduce_Max_Number (Samples); -- 7.25
These input values make the 128-bit results easy to check. See the semantic compatibility document for NaN and signed-zero results.
Wide floating reductions use the same lane order. They do not use the integer grouping or reduce the private parts independently. Wide.Native.Reduce_Add, Reduce_Min_Number, and Reduce_Max_Number combine lanes in ascending lane order. Reduce_Add starts from positive zero. The minimum-number and maximum-number reductions start from lane 0. Each step applies the documented NaN and signed-zero rules.
On AArch64, dedicated Advanced SIMD sequences perform scalar fadd, fminnm, or fmaxnm operations in ascending lane order for the six F32x8 and F64x4 overloads. 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.
For a complete Wide example, see the Chapter 2 weighted sum and order-sensitive reduction.
Check lane movement semantics
Use fixed lane arrangements
The Reverse_Lanes, Interleave_Low, Interleave_High, Deinterleave_Even, and Deinterleave_Odd families use fixed lane arrangements. The following table illustrates the rules for two eight-lane inputs. The same rules apply to every supported lane count.
| Operation | Result for Left = [L0 .. L7] and Right = [R0 .. R7] |
|---|---|
Reverse_Lanes (Left) | [L7, L6, L5, L4, L3, L2, L1, L0] |
Interleave_Low (Left, Right) | [L0, R0, L1, R1, L2, R2, L3, R3] |
Interleave_High (Left, Right) | [L4, R4, L5, R5, L6, R6, L7, R7] |
Deinterleave_Even (Left, Right) | [L0, L2, L4, L6, R0, R2, R4, R6] |
Deinterleave_Odd (Left, Right) | [L1, L3, L5, L7, R1, R3, R5, R7] |
Each operation preserves the complete bits of every moved lane.
Reuse one-source lane maps
Make_Lane_Map constructs a strongly typed map from a selector array. The array contains one source-lane index for each result lane. Permute_Lanes then sets result lane n to the source lane selected at position n. A repeated selector copies one source lane to several result lanes. Each selected lane keeps its complete bit encoding.
The four private map types are Lane_Map_8x16, Lane_Map_16x8, Lane_Map_32x4, and Lane_Map_64x2. Signed, unsigned, and floating vectors with the same lane shape use the same map type. Permute_Lanes supports all ten 128-bit value types.
Chapter 2 uses a Wide 32x8 lane map to rotate four planar points. This Permute_Lanes overload reads one source vector.
Select lanes from two source vectors
Select_Left_Lane and Select_Right_Lane construct a typed selector that records one source choice and one lane index. Make_Two_Source_Lane_Map stores one selector for each result lane. The three-argument Permute_Lanes overload sets result lane n to the selected lane of Left or Right.
The four private map types are Two_Source_Lane_Map_8x16, Two_Source_Lane_Map_16x8, Two_Source_Lane_Map_32x4, and Two_Source_Lane_Map_64x2. Signed, unsigned, and floating vectors with the same lane shape use the same map type. The overload supports all ten 128-bit value types.
Selectors can repeat lanes from either source vector. Each selected lane keeps its complete bit encoding. A default-initialized two-source map selects left lane 0 for every result lane. Chapter 2 forms a cross-block successor window and calculates first differences.
Slide neighboring lanes
The count is in lanes. Slide_Lanes_Toward_Low maps source lane n + Count to result lane n. It discards values beyond the low edge and fills vacated high-index lanes with zero. Slide_Lanes_Toward_High maps source lane n - Count to result lane n when n >= Count. It discards values beyond the high edge and fills vacated low-index lanes with zero.
A zero count returns the input. A count equal to or greater than the lane count returns Zero. Retained lanes preserve their complete bit encoding. Vacated floating lanes contain positive zero.
The operations are available for all ten 128-bit value types. Chapter 2 builds a three-point floating stencil and links the concrete native overloads.
Combine and inspect masks
Compress visits source lanes in ascending order. It packs true-mask lanes consecutively from result lane 0 and zero-fills the remainder. Expand consumes consecutive input lanes from lane 0 into true result positions and zero-fills false positions. Both operations preserve the complete bits of moved lanes. Floating fill lanes contain positive zero. They are available for all ten value types. Chapter 3 uses them to compress selected measurements and restore their positions.
A mask is a Boolean value for each lane. It is not an integer vector. Use these operations instead of depending on its private representation:
Mask_And,Mask_Or,Mask_Xor, andMask_Notcombine lane truths.Testreads one lane truth.Any_True,All_True, andNone_Truereduce the mask to one Boolean.Population_Countcounts true lanes.First_TrueandLast_Truereturn the lowest or highest true lane.Mask_From_Bit_Maskconstructs a mask from the low lane-count bits of a compact integer.To_Bit_Maskreturns a compact mask. Bit n represents lane n.
A mask-position result is a valid lane index when it is less than Lane_Count_8x16'Last. The last value, 16 for this mask, means that no lane is true. The same rule applies to the other mask widths.
Input : constant Byte_Array (1 .. 16) :=
[97, 98, 99, 44, 100, 101, 102, 44,
103, 104, 105, 106, 107, 108, 109, 110];
Bytes : constant U8x16 := Load_Unaligned (Input, Input'First);
Commas : constant Mask_8x16 := Equal (Bytes, Splat (44));
First_Comma : constant Lane_Count_8x16 := First_True (Commas);
Last_Comma : constant Lane_Count_8x16 := Last_True (Commas);
if First_Comma /= Lane_Count_8x16'Last then
-- First_Comma is 3 and Last_Comma is 7.
-- Both values can now be converted to Lane_Index_8x16.
end if;
The Byte_Array input is abc,def,ghijklmn. Load_Unaligned reads its 16 bytes. Lane indexes start at zero, so the comma positions are 3 and 7. The sentinel check comes before any conversion to Lane_Index_8x16.
Use typed arrays for memory operations
Each family has Load, Store, aligned variants, unaligned variants, and safe partial variants. Full operations require one complete vector of valid elements.
Has_Extent is a root-package Byte_Array contract helper. It reports whether Count byte elements fit from Start. When Count is zero, Start does not have to identify an element. This helper is not a missing Wide vector primitive. Is_Aligned_16 reports whether that byte element address is 16-byte aligned.
A partial load reads exactly Count elements and fills the remaining lanes with zero. A partial store writes exactly Count elements. A count of zero touches no element.
See the memory guide for a complete tail loop.
Current profile limits
The complete 128-bit family and initial Wide profile do not imply that every planned operation or lowering exists. Dedicated 256-bit implementations currently cover the optional x86-64 byte operations and lookup listed in the Wide section. They also cover the Wide lane-movement operations and both Permute_Lanes overloads for all ten value types.
“Complete 128-bit family” means that all ten 128-bit value types have the documented root-package profile. “Initial Wide profile” means that all ten 256-bit value types have only the groups listed in the Wide section.