Compare feature paths

The earlier examples use the original SME2 lookup path: a table in ZT0, packed indices in Z registers, and one or more Z-register results.

Other architectural lookup-table instruction (LUTI) features use a different table source or add specialized forms:

Feature pathTable sourceExecution stateDistinguishing capability
FEAT_SME2Fixed 512-bit ZT0Streaming mode with ZA enabledLUTI2 and LUTI4 can produce one, two, or four Z-register results, subject to the element-width encoding.
FEAT_SME2p1Fixed 512-bit ZT0Streaming mode with ZA enabledExtends the SME2 forms with strided destination pairs and quads.
FEAT_LUT with FEAT_SVE2 or FEAT_SME2One or two scalable Z registersNon-streaming SVE or Streaming SVE, respectivelyAdds Z-register table forms of LUTI2 and LUTI4 which produce one Z-register result without using ZT0.
Note FEAT_LUT and FEAT_SME2p1 aren’t currently implemented on any hardware. The following code excerpts are illustrative. Use the compiler feature macros to guard these paths and prepare your kernel for when hardware support for these features becomes available.

Use Z-register tables with FEAT_LUT

FEAT_LUT expands the functionality of FEAT_SME2, allowing the use of scalable Z registers as the lookup-table source:

    

        
        
table Z register(s) + packed-index Z register
                    |
                 LUTI2/LUTI4
                    |
             one result Z register

    

This provides an alternative to using the ZT0 register. Use this form for vector kernels that don’t need the ZT0 register and need multiple LUTs.

For example, the two-stage decode loop in the previous example reloads ZT0 using svldr_zt() for its LUTI4 and LUTI2 tables.

With FEAT_LUT, both lookup tables are kept in separate Z registers and loaded once before the loop with the appropriate predicate. This removes the need to call svldr_zt() inside the loop.

The following excerpt illustrates one segment of the two-stage decode:

    

        
        
// Load 16-entries LUTI4-Table and 4-entries LUTI2-Table before the loop.
const svuint8_t luti4_table_z = svld1_u8(pg_1, luti4_table_storage);
const svuint8_t luti2_table_z = svld1_u8(pg_2, luti2_table_storage);

for (size_t i_k = 0; i_k < lhs_blocks; ++i_k) {
    svuint8_t packed_indices = svld1_u8(pg8, rhs_indices);
    rhs_indices += vl_b;

    // First stage: Look up the codewords using the LUTI4 table. 
    // No need to call svldr_zt(). 
    svuint8_t codewords = svluti4_lane_u8(
        luti4_table_z, packed_indices, /* segment */ 0);

    // Second stage: Decode signed values using the LUTI2 table. 
    // No need to call svldr_zt(). 
    svint8_t values = svreinterpret_s8_u8(
        svluti2_lane_u8(luti2_table_z, codewords, /* segment */ 0));

    // Consume values, then continue with the next input block.
    ..

    

Each Z-register-table LUTI instruction produces one destination Z register. The table entries are the one or two SVL-sized Z registers. The logical LUT is still the four entries for LUTI2 or sixteen entries for LUTI4.

Place results with FEAT_SME2p1

The SME2 examples use consecutive destination registers. FEAT_SME2p1 extends the ZT0 SME2 forms with strided destination pairs and quads:

    

        
        
Consecutive pair: { z0, z1 }
Strided pair:     { z0, z8 }

Consecutive quad: { z0, z1, z2, z3 }
Strided quad:     { z0, z4, z8, z12 }

    

For example, the following LUTI2 instruction writes a strided destination quad:

    

        
        
luti2 {z0.b, z4.b, z8.b, z12.b}, zt0, z1[0]

    

This form gives the register allocator more placement options. It doesn’t change the index width, table contents, or number of results produced by the instruction.

Guard feature paths with compiler feature macros

Use Arm C Language Extensions (ACLE) macros to guard the currently standardized intrinsic families:

    

        
        
#if defined(__ARM_FEATURE_SME2)
// Original ZT0-based LUTI2 and LUTI4 forms.
#endif

#if defined(__ARM_FEATURE_LUT) && \
    (defined(__ARM_FEATURE_SVE2) || defined(__ARM_FEATURE_SME2))
// Z-register table forms.
#endif

#if defined(__ARM_FEATURE_SME2p1)
// SME2.1 forms, including strided destination groups.
#endif

    

These macros describe the compiler target. Runtime dispatch separately checks that the operating system exposes the required feature on the current processor.

What you’ve learned

You’ve identified the table source, execution state, output shape, and feature macro for the main LUTI variants. Use these distinctions when selecting an instruction form and when adding compile-time and runtime feature checks to production code.

You can now choose between ZT0-based SME2 forms, Z-register table forms with FEAT_LUT, and strided destination groups with FEAT_SME2p1, depending on the register pressure and algorithm structure of your kernel.

Back
Next