Check operation semantics

Use this reference after the conceptual guide or when you need exact type coverage, edge semantics, conversions, masks, and missing work.

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 typeValue typesMask type
8-bit integerU8x16, I8x16Mask_8x16
16-bit integerU16x8, I16x8Mask_16x8
32-bit integer or floatU32x4, I32x4, F32x4Mask_32x4
64-bit integer or floatU64x2, I64x2, F64x2Mask_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 typeValue typesMask type
8-bit integerU8x32, I8x32Mask_8x32
16-bit integerU16x16, I16x16Mask_16x16
32-bit integer or floatU32x8, I32x8, F32x8Mask_32x8
64-bit integer or floatU64x4, I64x4, F64x4Mask_64x4
GroupCurrent Wide profile
Construction and accessZero, Splat, From_Lanes, To_Lanes, Extract, and Replace
Comparison and selectionEqual, Less_Than, Less_Equal, Greater_Than, Greater_Equal, floating Unordered, and Select_Value
Lane rearrangementReverse; 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 memoryMask operations; compression and expansion; typed full, aligned, unaligned, and partial memory operations
Bit reinterpretationBit_Cast between signed, unsigned, and floating types that have the same lane width and lane count
Width changesWiden_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 changesConvert_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 lookup32-entry Table_Lookup for U8x32
Exact byte sumHorizontal_Sum for U8x32, with a Natural result from 0 through 8,160
Integer typesWrapping and saturating arithmetic; bitwise operations; shifts; ordered comparisons; minimum; maximum; add, minimum, and maximum reductions
Floating typesArithmetic; 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:

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_Lanes twice.
  • Interleave, deinterleave, and the two-source overload call selected 128-bit two-source Permute_Lanes four times and selected Select_Value twice to choose between the two sources.
  • Slides call selected 128-bit two-source Permute_Lanes twice and selected Select_Value twice against Zero.

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.

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. 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.

OperationResultAvailable conversions
Bit_CastPreserves lane bitsSigned, unsigned, and floating types with the same lane shape
Widen_LowWidens the low source halfAdjacent integer widths with the same signedness; F32x4 to F64x2
Widen_HighWidens the high source halfAdjacent integer widths with the same signedness; F32x4 to F64x2
Narrow_TruncateKeeps the low result-width bitsAdjacent integer widths with the same signedness
Narrow_SaturateClamps to the result rangeSame-signedness and signed-to-unsigned integer narrowing
Narrow_RoundRounds binary64 lanes to binary32Two F64x2 inputs to one F32x4 result
Convert_RoundRounds integer values to floating pointI32x4 or U32x4 to F32x4; I64x2 or U64x2 to F64x2
Convert_Truncate_SaturateTruncates toward zero, then clampsF32x4 to I32x4 or U32x4; F64x2 to I64x2 or U64x2
Convert_SaturateConverts signedness and clampsSigned 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

GroupOperationsTypes
ConstructionZero, Splat, From_Lanes, To_Lanes, Extract, ReplaceAll integer types
ArithmeticAdd_Wrap, Subtract_Wrap, Multiply_Wrap, Add_Saturate, Subtract_SaturateAll integer types
BitsBitwise_And, Bitwise_Or, Bitwise_Xor, Bitwise_NotAll integer types
ShiftsShift_Left_Logical, Shift_Right_LogicalAll integer types
Signed shiftShift_Right_ArithmeticSigned integer types
ComparisonEqual, Less_Than, Less_Equal, Greater_Than, Greater_EqualAll integer types
Selection and limitsSelect_Value, Min, MaxAll integer types
ReductionsReduce_Add_Wrap, Reduce_Min, Reduce_MaxAll integer types
Fixed lane arrangementsReverse_Lanes, Interleave_Low, Interleave_High, Deinterleave_Even, Deinterleave_OddAll integer types
One-source lane mapsMake_Lane_Map, Permute_LanesAll integer types
Two-source lane mapsSelect_Left_Lane, Select_Right_Lane, Make_Two_Source_Lane_Map, Permute_LanesAll integer types
Lane slidesSlide_Lanes_Toward_Low, Slide_Lanes_Toward_HighAll integer types
Mask compression and expansionCompress, ExpandAll integer types
16-entry byte-table lookupTable_LookupU8x16
32-entry byte-table lookupWide.Table_LookupU8x32

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

GroupOperationsTypes
ConstructionZero, Splat, From_Lanes, To_Lanes, Extract, ReplaceF32x4, F64x2
ArithmeticAdd, Subtract, Multiply, DivideF32x4, F64x2
ComparisonEqual, Less_Than, Less_Equal, Greater_Than, Greater_Equal, UnorderedF32x4, F64x2
Selection and limitsSelect_Value, Min_Number, Max_NumberF32x4, F64x2
ReductionsReduce_Add, Reduce_Min_Number, Reduce_Max_NumberF32x4, F64x2
Fixed lane arrangementsReverse_Lanes, Interleave_Low, Interleave_High, Deinterleave_Even, Deinterleave_OddF32x4, F64x2
One-source lane mapsMake_Lane_Map, Permute_LanesF32x4, F64x2
Two-source lane mapsSelect_Left_Lane, Select_Right_Lane, Make_Two_Source_Lane_Map, Permute_LanesF32x4, F64x2
Lane slidesSlide_Lanes_Toward_Low, Slide_Lanes_Toward_HighF32x4, F64x2
Mask compression and expansionCompress, ExpandF32x4, 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.

OperationResult 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:

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.

Coverage statement

“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.