Проблемы встроенной сборки SSE4 в C ++ - PullRequest
2 голосов
/ 07 января 2011

Привет всем Я хочу выполнить встроенную инструкцию по сборке, которая имеет следующий вид

BLENDPD xmm1,xmm2/m128, imm8

Я новичок во встроенной сборке, поэтому у меня возникли некоторые трудности. мой код:

#include<iostream>
using namespace std;
int main()
{
  long long y;
  __asm("blendpd %0,$0xabcd000000001111abcd000000001111,$0x1":
    "=r" (y):
    );
  cout<<y;
  return 0;
}

Моей первой ошибкой было получение 128-битного операнда, поэтому я использовал длинное шестнадцатеричное число, но все же мне нужно, чтобы вывод был 128-битным, поскольку я хочу, чтобы 2 мог печатать y на экране. и больше всего я знаю, что мой синтаксис __asm ​​неверен, но не могу понять, плюс я не уверен, что компиляция с синтаксисом Intel или AT & T изменится при использовании __asm.

Любая помощь приветствуется. Ура! =)

Редактировать: У меня есть эта версия, и я получаю неопределенную ошибку функции.

  #include<iostream>
  #include<emmintrin.h>
  using namespace std;

int main()
{
const int mask=5;
__m128d v2 = _mm_set_pd(1.0, 2.0);
__m128d v1;
v1=_mm_blend_pd(v1, v2, mask);
return 0;
}

Ответы [ 2 ]

5 голосов
/ 07 января 2011

Во-первых, для такого рода вещей вам очень редко нужно использовать встроенную сборку.GCC обычно предоставляет «встроенные» функции компилятора, которые позволяют вам вызывать заданную специальную инструкцию, используя синтаксис функции C, а не синтаксис сборки.

В этом случае вам нужна встроенная функция _mm_blend_pd (), и она имеетсигнатура функции

#include <smmintrin.h>
__m128d _mm_blend_pd(__m128d v1, __m128d v2, const int mask);

Компилятор заменит ее одной инструкцией blendpd;на самом деле это не вызов функции.

Тип данных __m128d - это вектор, содержащий два значения с плавающей запятой двойной точности;вы можете создать его из массива значений типа double следующим образом:

__m128d v = _mm_set_pd(1.0, 2.0);

Чтобы извлечь значения из вектора и напечатать их, вы можете сохранить вектор в массиве чисел с плавающей запятой двойной точности:

double a[2];
_mm_store_pd(a, v);

Все это основано на руководстве Intel Intrinsics по адресу http://www.info.univ -angers.fr / ~ richer / ens / l3info / ao / intel_intrinsics.pdf ;хотя это относится к компилятору Intel C ++, GCC поддерживает тот же синтаксис.

Редактировать: Заменить ошибочный emmintrin.h на правильный smmintrin.h.Также обратите внимание, что значение mask должно быть 2-битным (один бит на значение в векторе);значения, отличные от 0, 1, 2 или 3, приводят к ошибке.И, конечно, вам нужно скомпилировать это с опцией -msse4 GCC.

3 голосов
/ 08 января 2011

Как альтернативный ответ на мой другой ответ, вот как это сделать с помощью встроенной сборки, а не встроенной. (Как Томас Порнин отмечает в моем другом ответе, встроенные функции, как правило, лучше, потому что они более переносимы, но иногда вы тоже хотите что-то подобное.)

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

    movsd   -64(%rbp), %xmm0
    movhpd  -56(%rbp), %xmm0
    movsd   -48(%rbp), %xmm1
    movhpd  -40(%rbp), %xmm1
    blendpd $3, %xmm1, %xmm0
    movlpd  %xmm0, -64(%rbp)
    movhpd  %xmm0, -56(%rbp)

Здесь вы можете увидеть несколько вещей, отличных от вашего исходного кода. Во-первых, обратите внимание, что два 128-битных аргумента являются , а не немедленными - это регистры xmm0 и xmm1. Кроме того, у вас есть операнды в неправильном порядке - маска идет первой, а регистр, содержащий выходные данные, идет последним. Исправьте это, и код скомпилируется.

Вторая проблема заключается в том, что вы сохраняете результат из общего регистра в y, а инструкция blendpd не затрагивает общие регистры, так что это просто хранение мусора. Вам нужен регистр xmm0, который вы получаете вместе с =Yz (см. Документацию GCC здесь ). И вы не можете сохранить это в long long, который составляет 64 бита; вам нужна 128-битная векторная переменная. Решая все эти проблемы, исправляем код:

#include<iostream>
#include<smmintrin.h>
using namespace std;
int main()
{
  __m128d y;
  __asm("blendpd $0x3,%%xmm1,%%xmm0":
    "=Yz" (y):
    );
  // cout<<y;
  return 0;
}

Вы заметите, что мне пришлось закомментировать строку cout, так как в ней не предусмотрено обработки векторов SSE; вам нужно будет использовать встроенную _mm_store_pd, чтобы сначала получить отдельные двойные значения из y. (Или вы можете добавить больше встроенной сборки для вызова инструкций movhpd и movhld, чтобы получить двойные значения непосредственно из регистра, вместо использования ограничения для присвоения им y.)

И вот, он у вас есть - он компилируется и работает нормально. Конечно, входные значения не определены (что бы то ни было в этих регистрах случайным образом), поэтому в любом случае выходные данные являются мусором - вам нужно сначала что-то добавить для загрузки значений в регистры, если вы хотите получить значимый результат.

...