How to make an operation similar to _mm_extract_epi8 with non-immediate input?

Viewed 88

What I want is extracting a value from vector using a variable scalar index.

Like _mm_extract_epi8 / _mm256_extract_epi8 but with non-immediate input.

(There are some results in the vector, the one with the given index is found out to be the true result, the rest are discarded)

2 Answers

Especially, if index is in a GPR, the easiest way is probably to store val to memory and then movzx it into another GPR. Sample implementation using C:

uint8_t extract_epu8var(__m256i val, int index) {
    union {
        __m256i m256;
        uint8_t array[32];
    } tmp;
    tmp.m256 = val;
    return tmp.array[index];
}

Godbolt translation (note that a lot of overhead happens for stack alignment -- if you don't have an aligned temporary storage area, you could just vmovdqu instead of vmovdqa): https://godbolt.org/z/Gj6Eadq9r

So far the best option seem to be using _mm_shuffle_epi8 for SSE

uint8_t extract_epu8var(__m128i val, int index) {
    return (uint8_t)_mm_cvtsi128_si32(
        _mm_shuffle_epi8(val, _mm_cvtsi32_si128(index)));
}

Unfortunately this does not scale well for AVX. vpshufb does not shuffle across lanes. There is a cross lane shuffle _mm256_permutevar8x32_epi32, but the resulting stuff seem to be complicated:

uint8_t extract_epu8var(__m256i val, int index) {
    int index_low = index & 0x3;
    int index_high = (index >> 2);
    return (uint8_t)(_mm256_cvtsi256_si32(_mm256_permutevar8x32_epi32(
      val, _mm256_zextsi128_si256(_mm_cvtsi32_si128(index_high))))
      >> (index_low << 3));
}
Related