Вы используете инструкцию vmovdqa
, для которой требуется выровненный операнд памяти, для невыровненного элемента массива. Вместо этого используйте vmovdqu
для загрузки и хранения. Или, что еще лучше, используйте операнды памяти в реальных инструкциях вычислений (хотя это действительно только в AVX; в устаревших операндах памяти SSE большинство команд должны быть выровнены).
Существуют и другие недостатки и проблемы с блоком ассемблера , Например, как упоминалось в комментариях, вам не хватает клобберов, которые указывают части процессора и состояние памяти, которые могут быть изменены блоком asm. В вашем случае вам не хватает клобберов "memory", "xmm0" и "xmm1". Без них компилятор будет предполагать, что блок asm не влияет на содержимое памяти (в частности, массив aResult
) или регистры xmm
(и, например, использует эти регистры в своих целях в конфликте с вашим блоком asm). ).
Кроме того, вы, кажется, перепутали входной и выходной регистры в инструкциях addps
и divps
, поскольку в некоторых случаях вы перезаписывали или не использовали результаты предыдущих инструкций. В синтаксисе AT & T x86 asm, используемом g cc, последний операнд является выходным операндом. Обычно вы должны использовать версию AVX каждой инструкции при использовании любых инструкций AVX, хотя смешивание 128-битных инструкций AVX с унаследованным SSE не приведет к остановкам перехода SSE / AVX, если верхние половины регистров YMM уже были чистыми (например, vzeroupper). Использовать vaddps
/ vdivps
необязательно, но рекомендуется.
Кроме того, вы неэффективно передаете ссылки на входные и выходные массивы. Вместо того, чтобы передавать указатели на определенные c элементы массивов, более эффективно передавать ссылки на память тем, что позволяет компилятору использовать более сложные аргументы, ссылающиеся на память, чем простой указатель. Это избавляет от необходимости вычислять указатель в отдельной инструкции перед вашим блоком asm. Кроме того, более эффективно передавать константу tree
в регистр xmm
вместо памяти. В идеале вы хотели бы переместить vbroadcastss
из l oop, но это возможно только при поддержке встроенных функций. (Или записать l oop внутри одного оператора asm.)
Исправленный и улучшенный оператор asm будет выглядеть так:
__asm__ __volatile__
(
"vcvtdq2ps %1, %%xmm0;"
"vcvtdq2ps %2, %%xmm1;"
"vaddps %%xmm1, %%xmm0;"
"vcvtdq2ps %3, %%xmm1;"
"vaddps %%xmm1, %%xmm0;"
"vbroadcastss %4, %%xmm1;"
"vdivps %%xmm0, %%xmm1;"
"vcvtps2dq %%xmm1, %0;"
: "=m"(aResult[i])
: "m"(cyclic[i]), "m"(cyclic[j]), "m"(cyclic[k]), "x"(three)
: "memory", "xmm0", "xmm1"
);
(На самом деле это не обязательно volatile
больше, теперь, когда выходные данные памяти являются явными операндами.)
Но еще лучшим решением было бы использование встроенных функций для реализации этого блока asm. Мало того, что это сделает блок asm более безопасным, это сделает его более эффективным, так как это обеспечит дополнительную оптимизацию компилятора. Конечно, это возможно только в том случае, если ваш компилятор поддерживает встроенные функции.