Перемещение одного поплавка в регистр xmm - PullRequest
4 голосов
/ 27 ноября 2011

Я хочу умножить данные, хранящиеся в одном регистре xmm, на одно значение с плавающей точкой и сохранить результат в регистре xmm.Я сделал небольшую графику, чтобы объяснить это немного лучше.

enter image description here

Как вы видите, я получил регистр xmm0 с моими данными в нем.Например, он содержит:

xmm0 = | 4.0 | 2.5 | 3.5 | 2.0 |

Каждая с плавающей запятой хранится в 4 байтах.Мой регистр xmm0 имеет длину 128 битов и длину 16 байтов.

Это работает довольно хорошо.Теперь я хочу сохранить 0.5 в другом регистре xmm, например xmm1, и умножить этот регистр на регистр xmm0, чтобы каждое значение, хранящееся в xmm0, умножалось на 0.5.

Я абсолютно не знаю, как хранить 0.5 врегистр XMM.Любые предложения?

Кстати: это встроенный ассемблер в C ++.

void filter(image* src_image, image* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movaps (%%esi), %%xmm0\n"      
        // Multiply %xmm0 with a float, e.g. 0.5
        "movaps %%xmm0, (%%edi)\n" 

        :
        : "S"(src), "D"(dst) :  
    );
}

Это тихая простая версия того, что я хочу сделать.Я получил некоторые данные изображения, хранящиеся в массиве с плавающей точкой.Указатель на эти массивы передается в сборку.movaps принимает первые 4 значения с плавающей точкой массива и сохраняет эти 16 байтов в регистре xmm0.После этого xmm0 следует умножить, например, на 0,5.Чем «новые» значения должны храниться в массиве из edi.

Ответы [ 5 ]

8 голосов
/ 27 ноября 2011

Как отмечали в комментариях люди, для такого рода очень простой операции всегда лучше использовать встроенные функции:

void filter(image* src_image, image* dst_image)
{
    const __m128 data = _mm_load_ps(src_image->data);
    const __m128 scaled = _mm_mul_ps(data, _mm_set1_ps(0.5f));
    _mm_store_ps(dst_image->data, scaled);
}

Вы должны прибегать к встроенному ASM, только если компилятор генерирует плохой код (и только после сообщения об ошибке поставщику компилятора.

Если вы действительно хотите остаться в сборке, есть много способов выполнить эту задачу.Вы можете определить масштабный вектор вне блока ASM:

    const __m128 half = _mm_set1_ps(0.5f);

, а затем использовать его внутри ASM так же, как вы используете другие операнды.

Вы можете сделать это без каких-либо нагрузок, еслиВы действительно хотите:

    "mov    $0x3f000000, %%eax\n"  // encoding of 0.5
    "movd   %%eax,       %%xmm1\n" // move to xmm1
    "shufps $0, %%xmm1,  %%xmm1\n" // splat across all lanes of xmm1

Это всего лишь два подхода.Есть много других способов.Вы можете потратить некоторое время на справочную информацию по набору инструкций Intel.

4 голосов
/ 27 ноября 2011

Если вы используете встроенные функции: __m128 halfx4 = _mm_set1_ps(0.5f);

Edit:

Вам гораздо лучше использовать встроенные функции:

__m128 x = _mm_mul_ps(_mm_load_ps(src), halfx4);
_mm_store_ps(dst, x);

Если данные с плавающей запятой src и dst не выровнены по 16 байтам, вам необходимо: _mm_loadu_ps и _mm_storeu_ps - которые медленнее.

2 голосов
/ 27 ноября 2011

Вы ищете инструкцию MOVSS (которая загружает число с плавающей запятой одинарной точности из памяти в младшие 4 байта регистра SSE), за которым следует случайное перемешивание, чтобы заполнить остальные 3 числа с этим значением:

movss  (whatever), %%xmm1
shufps %%xmm1, %%xmm1, $0

Это также, как, вероятно, встроенный _mm_set1_ps может сделать это. Затем вы можете просто умножить эти значения SSE или сделать что хотите:

mulps %%xmm1, %%xmm0
0 голосов
/ 04 июля 2017

Вот один из способов сделать это:

#include <stdio.h>
#include <stdlib.h>

typedef struct img {
    float *data;
} image_t;

image_t *src_image;
image_t *dst_image;
void filter(image_t*, image_t*);

int main()
{
    image_t src, dst;
    src.data = malloc(64);
    dst.data = malloc(64);
    src_image=&src;
    dst_image=&dst;

    *src.data = 42.0;
    filter(src_image, dst_image);

    printf("%f\n", *dst.data);
    free(src.data);
    free(dst.data);
    return 0;
}

void filter(image_t* src_image, image_t* dst_image)
{
    float* src = src_image->data;
    float* dst = dst_image->data;

    __asm__ __volatile__ (              
        "movd   %%esi, %%xmm0;"
        "movd   %%xmm0, %%edi;"
        : "=D" (*dst)
        : "S" (*src)
    );
}
0 голосов
/ 29 ноября 2011

Если вы используете c ++ с gcc и используете EasySSE, ваш код может выглядеть следующим образом

void filter(float* src_image, float* dst_image){
    *(PackedFloat128*)dst_image =  Packefloat128(0.5) * (src_image+0);
}

Предполагается, что указанные указатели выровнены по 16 байтам. Вы можете проверить код сборки, чтобы убедиться, что переменные правильно сопоставлены с векторными регистрами.

...