Когда __m128 в регистре xmm? - PullRequest
2 голосов
/ 17 октября 2019

Вызов _mm_load_ps возвращает __m128. В справочнике по встроенным функциям Intel говорится: :

Загрузка 128 бит (состоит из 4 упакованных элементов с плавающей запятой одинарной точности (32 бит)) из памяти в dst. mem_addr должен быть выровнен по 16-байтовой границе, иначе может быть сгенерировано исключение общей защиты.

(Примечание редактора: используйте _mm_loadu_ps для возможно невыровненной загрузки)


Значит ли это, что пакет с 4 float находится в регистрах xmm, пока __m128 жив? И будет ли это означать, что при наличии в стеке большего количества __m128, чем доступных регистров xmm, произойдет разлив?

1 Ответ

5 голосов
/ 17 октября 2019

Означает ли это, что пакет с 4 float находится в регистрах xmm, пока __m128 жив?

Нет. Встроенные функции компилируются компилятором, а векторные переменные будут подвергаться распределению регистров, как и любая другая переменная.

Как вы заметили во втором предложении - вы можете написать код с большим количеством __m128 переменных, чем у вас есть регистров, - что вылилось бы в стек.

API встроенных функций разработан, чтобы позволить вам притворятьсявы пишете на ассемблере, но встроенные функции load / store на самом деле просто сообщают компилятору информацию о типе / выравнивании.

(alignof(__m128) = 16, поэтому любой разлив / перезагрузка может быть выполнена с помощью инструкций, необходимых для выравнивания. И перезагрузкаможет даже использовать его в качестве операнда источника памяти вместо загрузки в регистр.)

__m128 переменные также должны быть распределены между вызовами не встроенных функций, особенно в соглашениях о вызовах, в которых нет вызовов. сохраненные XMM регистры. (например, x86-64 System V). Windows x64 имеет несколько сохраняемых вызовов регистров XMM, но некоторые являются энергозависимыми (call-clobbered), поэтому функции имеют несколько регистров XMM для воспроизведения.

Таким образом, гарантируется, что при наличии __m128 больше, чемСуществуют ли регистры, которые могут привести к разливу, и что наличие меньшего количества всегда будет предотвращать разлив?

Компиляторы очень стараются составить расписание для инструкций в порядке, который уменьшает разлив. В абстрактных терминах, например, вы можете написать некоторый код, подобный следующему:

int A = *<foo>;
int B = *<foo+1>;
int C = *<foo+2>;
int D = A + B + C;

Вы можете подумать, что для этого нужно 4 регистра, потому что вы создали и присвоили 4 переменные, но весьма вероятно, что вы что-то получитечто больше похоже на это на уровне машины:

int A = *<foo>;
int B = *<foo+1>;
int D = A + B
int A = *<foo+2>;
int D = D + A

т.е. компилятор переупорядочил этот код, чтобы минимизировать количество необходимых физических регистров.

На самом деле это трудно предсказать. Компиляторы стремятся снизить нагрузку на регистр, потому что разлив стоит дорого, но может намеренно не совсем снизить его до минимально возможного уровня, потому что им также нужно забирать данные заранее, чтобы попытаться скрыть задержку загрузки выборок памяти.

В общем, рекомендуется разбирать высокопроизводительные пути кода, чтобы компилятор делал то, что вы ожидали ...

...