Как я могу загрузить литеральное значение в регистр mask (k)? - PullRequest
0 голосов
/ 15 января 2020

Я хочу загрузить регистр AVX512 "k" с указанным c битовым шаблоном.

Лучшее, что я могу придумать, это обнулить 32-битный регистр, добавить константу, а затем переместить это. Для этого должна быть более короткая последовательность инструкций, но я не могу ее найти.

   4:   31 c0                   xor    %eax,%eax                   
   6:   05 aa aa 00 00          add    $0xaaaa,%eax                
   b:   c5 f8 92 f8             kmovw  %eax,%k7

1 Ответ

1 голос
/ 17 января 2020

Обычным способом было бы 2 инструкции по одной операции, 9 байтов.

mov     $0xaaaa, %eax     # 5 bytes
kmovw   %eax, %k7         # 4 bytes

Вы можете выбрать любой регистр нуля; это не должно быть EAX. Но для r8d..r15d потребуется префикс REX для mov, который стоит 1 дополнительный байт размера кода. (3-байтовый префикс VEX на kmovw уже включает биты REX, поэтому он не изменит размер.)

Когда вы не уверены, вы можете и должны взглянуть на то, что C компиляторы делают. Например, скомпилируйте с -O3 return _mm512_maskz_add_ps (0xaaaa, a, b) в функции с __m512 аргументами и посмотрите, как она получает 0xaaaa в регистр.

G CC, clang и я CC не всегда создаю оптимальный код, поэтому, если он выглядит неоптимальным, вы могли бы найти пропущенную оптимизацию в компиляторе. Проверьте https://uops.info/ и руководство по микроарху Agner Fog, чтобы увидеть, есть ли причина для выбора.


add eax, imm32 уже 5 байтов, то же самое длина как mov eax, imm32. (И обратите внимание, что есть 5-байтовая mov r32, imm32 форма без модема для каждого регистра, не только для EAX).

xor-zero + add - это 100% бессмысленно. В 32-битном режиме (где доступен 1-байтовый inc / dec, xor-zero + inc иногда используется при оптимизации по размеру кода по скорости, но даже add r32, imm8 равен 3 байта.

Даже для небольшой (8-битной) константы вы бы сэкономили только 1 байт с xor-zero + 2-байтом mov al, imm8, но по цене 2 мопа для внешнего интерфейса вместо 1 для mov r32, imm32. Производительность - это причина, по которой компиляторы обычно используют mov $1, %eax вместо немного более короткой 2-байтовой последовательности или 3-байтовой push imm8 / pop, которая еще короче, но еще менее эффективна. См. Также CodeGolf Советы по машинному коду .SE x86-64 для компактных способов помещения констант в регистры, например, если у вас уже есть нулевой регистр, вы можете использовать 3-байтовый LEA, чтобы получить любое значение от -128 до +127 в другое Это всего 1 моп, но он не может работать на таком количестве портов, как mov -mmediate.

Обычно размер кода в байтах следует рассматривать только как разрыв ie между последовательностями которые декодируют до как можно меньшего числа мопов. См. https://agner.org/optimize/ и * 10 54 *

Подсчет в инструкциях часто не актуален. Но на Xeon Phi (Knight's Landing) важно избегать многопользовательских инструкций. Декодеры останавливаются на несколько циклов, когда одна инструкция должна декодировать до более чем 1 мегапикселя.

Массивные "большие ядра" x86-процессоры имеют кэш-память uops (Intel начиная с Sandybridge, AMD начиная с Zen), которая в основном удаляет декодирование узкие места для горячих петель. (Настройка для более ранних ISA включала заботу о выравнивании и 16-байтовых блоках выборки, а также о том, будет ли многопользовательская инструкция соответствовать первому декодеру или ждать следующего цикла; см. Разделы Core2 / Nehalem в микроархе Agner Fog PDF.)

Некоторые инструкции по-разному декодируются на разных uarches, поэтому ваш JIT может декодировать между последовательностями на основе текущей цели.


Загрузка константы из памяти: не стоит он

kmovw (mem), %k7 кодируется, но, к сожалению, на Skylake-X / Каскадное озеро / Ледяное озеро он декодируется до 2 моп , как mov загрузка в EAX + a kmov. Таким образом, для порта 5 все еще требуется uop. https://www.uops.info/html-instr/KMOVW_K_M16.html

Он также практически ничего не сохраняет для размера кода: для режима адресации RIP-относительной требуется 4 байта rel32 за пределами кода операции + modrm, то есть за пределами того, что понадобится источнику EAX. Таким образом, kmovw constant(%rip), %k7 будет всего 8 байт, всего на 1 байт короче, чем mov-немедленный + kmov eax, k1.

Кроме того, вам все равно нужно поместить 2 байта данных в пул констант где-нибудь вместе с любые векторные константы.

Но вы, как правило, хотите загружать векторные константы из памяти; они слишком велики, чтобы их можно было эффективно построить из немедленных + тасовок, если только это не повторяющийся шаблон, который вы можете сделать с mov $imm32, %eax / vpbroadcastd %eax, %zmm0. Или повторяющийся шаблон, который вы можете создать на лету из регистра «все единицы» из vpcmpeqd %ymm0, %ymm0, %ymm0 или vpternlogd %zmm0, %zmm0, %zmm0, 0xff.

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

...