Получить член __m128 по индексу?

У меня есть код, первоначально предоставленный мне кем-то, работающим с MSVC, и я пытаюсь заставить его работать на Clang. Здесь функция, с которой у меня возникают проблемы:

float vectorGetByIndex( __m128 V, unsigned int i )
{
    assert( i <= 3 );
    return V.m128_f32[i];
}

Ошибка, которую я получаю, выглядит следующим образом:

Member reference has base type '__m128' is not a structure or union.

Я огляделся и обнаружил, что Clang (и, возможно, GCC) имеет проблему с обработкой __m128 как структуры или объединения. Однако мне не удалось найти прямой ответ о том, как я могу вернуть эти ценности. Я пробовал использовать оператор индекса и не мог этого сделать, и я просмотрел огромный список функций встроенного SSE и еще не нашел подходящего.

Ответ 1

Объединение, вероятно, является самым переносимым способом:

union {
    __m128 v;    // SSE 4 x float vector
    float a[4];  // scalar array of 4 floats
} U;

float vectorGetByIndex(__m128 V, unsigned int i)
{
    U u;

    assert(i <= 3);
    u.v = V;
    return u.a[i];
}

Ответ 2

использование

template<unsigned i>
float vectorGetByIndex( __m128 V) {
    union {
        __m128 v;    
        float a[4];  
    } converter;
    converter.v = V;
    return converter.a[i];
}

который будет работать независимо от доступного набора команд.

Примечание. Даже если SSE4.1 доступен и i pextract постоянной времени компиляции, вы не можете использовать pextract и т.д. Таким образом, поскольку эти инструкции извлекают 32-разрядное целое число, а не число с float:

// broken code starts here
template<unsigned i>
float vectorGetByIndex( __m128 V) {
    return _mm_extract_epi32(V, i);
}
// broken code ends here

Я не удаляю это, потому что это полезное напоминание, как не делать вещи.

Ответ 3

Как модификация решения hirschhornsalz, если i является константой времени компиляции, вы можете полностью исключить путь объединения, используя тасование/хранилище:

template<unsigned i>
float vectorGetByIndex( __m128 V)
{
#ifdef __SSE4_1__
    return _mm_extract_epi32(V, i);
#else
    float ret;
    // shuffle V so that the element that you want is moved to the least-
    // significant element of the vector (V[0])
    V = _mm_shuffle_ps(V, V, _MM_SHUFFLE(i, i, i, i));
    // return the value in V[0]
    return _mm_cvtss_f32(V);
#endif
}

Ответ 4

Я использую

union vec { __m128 sse, float f[4] };

float accessmember(__m128 v, int index)
{
    vec v.sse = v;
    return v.f[index];
}

Кажется, для меня это очень хорошо.