Как заставить gcc использовать все регистры SSE (или AVX)?

я пытаюсь написать некоторый вычислительно интенсивный код для цели Windows x64, с SSE или новыми инструкциями AVX, компилируя в GCC 4.5.2 и 4.6.1, MinGW64 (TDM GCC build и некоторая пользовательская сборка). Мои параметры компилятора -O3 -mavx. (-m64 подразумевается)

короче говоря, я хочу выполнить некоторые длительные вычисления на 4 3D векторах упакованных поплавков. Для этого требуется 4x3=12 XMM или ymm регистров для хранения и 2 или 3 регистра для временных результатов. Это должно соответствовать IMHO плотно в 16 доступных регистрах SSE (или AVX), доступных для 64-битных целей. Однако GCC производит очень неоптимальный код с регистровым разливом, используя только регистры xmm0-xmm10 и перетасовка данных из и в стек. Мой вопрос:

есть ли способ убедить GCC использовать все регистры xmm0-xmm15?

чтобы исправить идеи, рассмотрим следующий код SSE (только для иллюстрации):

void example(vect<__m128> q1, vect<__m128> q2, vect<__m128>& a1, vect<__m128>& a2) {
    for (int i=0; i < 10; i++) {
        vect<__m128> v = q2 - q1;
        a1 += v;
//      a2 -= v;

        q2 *= _mm_set1_ps(2.);
    }
}

здесь vect<__m128> это просто struct of 3 __m128, С естественным сложением и умножением на скалярное. Когда линия a2 -= v закомментировано, т. е. нам нужны только регистры 3x3 для хранения, так как мы игнорируем a2, произведенный код действительно прост без ходов, все выполняется в регистрах xmm0-xmm10. Когда я удаляю комментарий a2 -= v, код довольно ужасен с большим количеством перетасовки между регистрами и стеком. Хотя компилятор может просто использовать registers xmm11-xmm13 или что-то в этом роде.

I на самом деле не видел GCC использовать любой из регистров xmm11-xmm15 везде во всех моих код. Что я делаю не так? Я понимаю, что они являются регистрами, сохраненными вызываемым абонентом, но эти накладные расходы полностью оправданы упрощением кода цикла.

2 ответов


две точки:

  • во-первых, вы делаете слишком много предположений. Регистровый разлив довольно дешев на процессорах x86 (из-за быстрых кэшей L1 и затенения регистра и других трюков), а 64-разрядные только регистры более дороги для доступа (с точки зрения больших инструкций), поэтому может быть, что версия GCC так же быстра или быстрее, чем та, которую вы хотите.
  • во-вторых, GCC, как и любой компилятор, делает лучшее распределение регистра это может. Нет - пожалуйста, сделай!--6-->лучше register allocation", потому что если бы он был, он всегда был бы включен. Компилятор не пытается досадить вам. (Распределение регистров-это NP-полная проблема, насколько я помню, поэтому компилятор никогда не сможет создать идеальное решение. Лучшее, что он может сделать, это приблизиться)

Итак, если вы хотите лучше зарегистрировать распределение, у вас в основном есть два варианта:

  • напишите лучший распределитель регистра и исправьте его в GCC, или
  • обход GCC и переписать функцию в сборке, так что вы можете точно контролировать, какие регистры используются, когда.

на самом деле, то, что вы видите, не разливы, это gcc, работающий на a1 и a2 в памяти, потому что он не может знать, являются ли они псевдонимами. Если вы объявите последние два параметра как vect<__m128>& __restrict__ GCC может и зарегистрирует выделение a1 и a2.