Подрешетки в inline-сборке. C ++ - PullRequest
0 голосов
/ 30 апреля 2020
void new2d(int* aInit, int* aResult)
{
        int cyclic[34] = {0};
        for (int i = 0; i < 32; i++)
        {
                cyclic[i] = aInit[i];
        }
        cyclic[32] = aInit[0];
        cyclic[33] = aInit[1];
        float three = 3.0;
        for (int i = 0; i < 32; i += 4)
        {
                int j = i + 1;
                int k = j + 1;
                __asm__ __volatile__
                (
                "vmovdqa (%0), %%xmm0;"
                "vmovdqa (%1), %%xmm1;"
                "vcvtdq2ps %%xmm0, %%xmm0;"
                "vcvtdq2ps %%xmm1, %%xmm1;"
                "addps %%xmm0, %%xmm1;"
                "vmovdqa (%2), %%xmm1;"
                "vcvtdq2ps %%xmm1, %%xmm1;"
                "addps %%xmm0, %%xmm1;"
                "vbroadcastss (%3), %%xmm1;"
                "divps %%xmm0, %%xmm1;"
                "vcvtps2dq %%xmm0, %%xmm0;"
                "vmovdqa %%xmm0, (%4);"
                :
                : "a"(&(cyclic[i])), "b"(&(cyclic[j])), "c"(&(cyclic[k])), "d"(&three), "S"(&aResult[i])
                );
        }
}

Попытка сложить три подмассива одного исходного массива, найти их средства и сохранить их в массиве результатов. Тем не менее, после запуска .exe-файла, он показывает ошибку сегментации. Как я могу это исправить? Использование GNU 2.9.3, Ubuntu

1 Ответ

1 голос
/ 30 апреля 2020

Вы используете инструкцию 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 более безопасным, это сделает его более эффективным, так как это обеспечит дополнительную оптимизацию компилятора. Конечно, это возможно только в том случае, если ваш компилятор поддерживает встроенные функции.

...