Добавление двух 4-векторов с sse с помощью указателей

Этот кусок кода (удваивающий 4-вектор) работает:

__declspec(align(16)) struct vec4 { float a[4]; };

int main()
{
    vec4 c;
    c.a[0]=2;
    c.a[1]=0;
    c.a[2]=0;
    c.a[3]=0;

    __asm {
        movaps xmm1, c

        addps xmm1, xmm1
        movaps c, xmm1
    }
}

Но эта часть (делает то же самое, но теперь с указателем на выровненные данные):

__declspec(align(16)) struct vec4 { float a[4]; };

int main()
{
    vec4* c = new vec4;
    c->a[0]=2;
    c->a[1]=0;
    c->a[2]=0;
    c->a[3]=0;

    __asm {
        movaps xmm1, c

        addps xmm1, xmm1
        movaps c, xmm1
    }
}

Зачем?

Мне нужно, чтобы он работал с указателями, потому что я не могу использовать сами выровненные данные в качестве аргумента функции.

2 ответа

Решение

Указатель в ASM должен обрабатываться в соответствии с определенными правилами, которые вы можете узнать в значительной степени, узнав, как работает "MOV".

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

vec4 *d = ...;
__asm {
    mov eax, d
    movaps xmm1, [eax]

    addps xmm1, xmm1
    movaps [eax], xmm1
}

Проблема в том, что объекты, созданные распределителем кучи (например, new и malloc), не следуют указанному выравниванию. Вы получаете выравнивание только с выделенным стеком объектом (ваш первый пример).

C++ 11 поддерживает явное выравнивание объектов, выделенных в куче с помощью alignas, но это пока не реализовано в VC++. Это будет работать с некоторыми компиляторами, а не с другими.

У вас есть несколько вариантов.

Самый простой: создайте выделенный объект кучи, как вы это сделали, и скопируйте его в выделенный объект стека, прежде чем использовать его:

vec4* c = new vec4;
c->a[0]=2;
c->a[1]=0;
c->a[2]=0;
c->a[3]=0;

vec4 d = *c;
// process with d

Другой вариант заключается в том, чтобы ваша структура vec4 включала достаточно дополнительной памяти, чтобы вы гарантированно имели 16 байтов при 16-байтовом выравнивании. Я считаю, что новый гарантирует как минимум 4 байта выравнивания, поэтому 28 байтов сделали бы это. Затем вам нужно будет вручную проверить указатель, чтобы увидеть, где вы хотите хранить данные, которые будут использоваться с sse.

Другие вопросы по тегам