Ошибки при использовании встроенного ассемблера в C

Я пробую свои силы на ассемблере, чтобы использовать векторные операции, которые я никогда раньше не использовал, и, по общему признанию, у меня есть некоторые проблемы с пониманием синтаксиса.

Соответствующий код ниже.

unit16_t asdf[4];
asdf[0] = 1;
asdf[1] = 2;
asdf[2] = 3;
asdf[3] = 4;
uint16_t other = 3;

__asm__("movq %0, %%mm0"
        :
        : "m" (asdf));
__asm__("pcmpeqw %0, %%mm0"
        :
        : "r" (other));
__asm__("movq %%mm0, %0" : "=m" (asdf));

printf("%u %u %u %u\n", asdf[0], asdf[1], asdf[2], asdf[3]);

В этом простом примере я пытаюсь выполнить 16-битное сравнение «3» с каждым элементом массива. Я надеюсь, что на выходе будет «0 0 65535 0». Но он даже не собирается.

Первая инструкция по сборке выдает следующую ошибку:

ошибка: вход памяти 0 не адресуется напрямую

Вторая инструкция дает мне другую ошибку:

Ошибка: недопустимый суффикс или операнд для `pcmpeqw'

Любая помощь будет оценена по достоинству.


person user1274193    schedule 24.02.2014    source источник
comment
Вы говорите C в заголовке, но тег C++?   -  person crashmstr    schedule 24.02.2014
comment
Исправлено, хотя я не думаю, что различие имеет значение в этом примере.   -  person user1274193    schedule 24.02.2014
comment
Почему бы просто не использовать встроенные функции?   -  person Paul R    schedule 24.02.2014
comment
Какая внутренняя функция могла бы это сделать?   -  person user1274193    schedule 24.02.2014
comment
Он доступен под несколькими именами, такими как _m_pcmpeqw, _mm_cmpeq_pi16 или __builtin_ia32_pcmpeqw. Кроме того, при использовании векторных расширений вы можете просто использовать оператор ==. См. документацию gcc.   -  person Jester    schedule 24.02.2014


Ответы (4)


Из справочного руководства Intel:

PCMPEQW mm, mm/m64        Compare packed words in mm/m64 and mm for equality.
PCMPEQW xmm1, xmm2/m128   Compare packed words in xmm2/m128 and xmm1 for equality.

Ваш pcmpeqw использует неверный регистр "r". Только регистры "мм" и "м64"

Вальтер

person γηράσκω δ' αεί πο    schedule 24.02.2014

Приведенный выше код потерпел неудачу при расширении asm(), он даже не пытался ничего собрать. В этом случае вы пытаетесь использовать нулевой аргумент (%0), но не указали его.

Ознакомьтесь с HOWTO встроенного ассемблера GCC или прочтите соответствующую главу вашей местной документации GCC.

person vonbrand    schedule 24.02.2014

Вы не можете использовать регистры непосредственно в ассемблерных операторах gcc и ожидать, что они совпадут с чем-либо в других ассемблерных операторах — оптимизатор все перемещает. Вместо этого вам нужно объявить переменные соответствующего типа и использовать ограничения, чтобы заставить эти переменные находиться в правильном типе регистра для используемых вами инструкций.

Соответствующие ограничения для MMX/SSE: x для регистров xmm и y для регистров mmx. Для вашего примера вы можете сделать:

#include <stdint.h>
#include <stdio.h>

typedef union xmmreg {
    uint8_t   b[16];
    uint16_t  w[8];
    uint32_t  d[4];
    uint64_t  q[2];
} xmmreg;

int main() {
    xmmreg v1, v2;
    v1.w[0] = 1;
    v1.w[1] = 2;
    v1.w[2] = 3;
    v1.w[3] = 4;
    v2.w[0] = v2.w[1] = v2.w[2] = v2.w[3] = 3;
    asm("pcmpeqw %1,%0" : "+x"(v1) : "x"(v2));
    printf("%u %u %u %u\n", v1.w[0], v1.w[1], v1.w[2], v1.w[3]);
}

Обратите внимание, что вам нужно явно реплицировать 3 во всех соответствующих элементах второго вектора.

person Chris Dodd    schedule 24.02.2014

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

person court    schedule 24.04.2014