如何使用非立即输入进行类似于_mm_extract_epi8的操作?

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

我想要的是使用可变标量索引从向量中提取值。

类似于 _mm_extract_epi8 / _mm256_extract_epi8 但具有非即时输入。

(向量中有一些结果,找到给定索引的结果是真正的结果,其余的被丢弃)

到目前为止,最好的选择似乎是对 SSE

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

不幸的是,这对于 AVX 来说不能很好地扩展。 vpshufb 不会跨车道洗牌。有一个cross lane shuffle _mm256_permutevar8x32_epi32,但是结果看起来很复杂:

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));
}

特别是,如果index在一个GPR中,最简单的方法可能是将val存储到内存中,然后movzx将其存储到另一个GPR中。使用 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 翻译(请注意,堆栈对齐会产生大量开销——如果您没有对齐的临时存储区域,您可以 vmovdqu 而不是 vmovdqa):https://godbolt.org/z/Gj6Eadq9r