Перемещение плавающего слова из четвертого слова с высоким xmm в четвертое слово с низким xmm - PullRequest
1 голос
/ 19 апреля 2019

MOVHPD извлекает старшее четырехзначное слово регистра xmm в память.

PEXTRQ извлекает старшее четырехсловое слово из регистра xmm и помещает его в регистр целых чисел (только целые числа).

SHUFPD тасует.

VPSLLDQ приводит к обнулению старшего четырехсловного слова.

Существует ли инструкция по перемещению значения с плавающей запятой из старшего четырехслового регистра xmm в младшее четырехсловое слово того же регистра xmm или другого регистра xmm?Или мне всегда приходится проходить через память (добавляя дополнительные циклы)?

ОБНОВЛЕНИЕ: Основываясь на комментариях ниже @fuz и @Peter Cordes, вот что я сделал.Это вызывает функцию округления для нижнего и верхнего четырехслов xmm0 по отдельности;из-за специальных параметров округления функция должна вызываться для каждого qword индивидуально, поэтому она не может быть инструкцией SIMD.Цель состоит в том, чтобы округлить каждое из qwords в xmm0 и поместить результат в xmm11.

movapd xmm2,xmm0 ;preserve both qwords of xmm0
call Round
movsd [scratch_register+0],xmm0 ; write low qword to memory
movhlps xmm0,xmm2
call Round
movsd [scratch_register+8],xmm0 ; write low qword to memory
movupd xmm11,[scratch_register]

ОБНОВЛЕНИЕ №2: @Peter Cordes показал, как это сделать без памяти:

movhlps  xmm2, xmm0   ; extract high qword for later
call Round            ; round the low qword
movaps   xmm3, xmm0   ; save the result
movaps   xmm0, xmm2   ; set up the arg
call Round            ; round the high qword
movlhps  xmm3, xmm0   ; re-combine into xmm3

1 Ответ

5 голосов
/ 19 апреля 2019

См. Руководство по оптимизации ассемблера Агнера Фога , в его главе о SIMD есть таблица инструкций перемешивания различных типов перемещения данных, которая даст вам небольшое количество инструкций для размышления (или поиска в руководствах Intelесли вы точно не помните, что они делают) и посмотрите, что вы хотите.


Самый дешевый способ передать высокое слово регистра на оба элемента - movhlps xmm0,xmm0. (Или для целочисленных данных, если ваш код может работать на Nehalem, используйте punpckhqdq xmm0,xmm0, чтобы избежать задержек обхода FP <-> vec-int.)

Без AVX, movhlps хорошо, потому чтоон немного отличается от случая, когда unpckhpd.

  • movhlps xmm3, xmm4 делает xmm3[0] = xmm4[1];, оставляя xmm3[1] без изменений.
  • unpckhpd xmm3, xmm4 занимаетвысокие слова из xmm3 и xmm4 и помещает их в xmm3 в указанном порядке.Таким образом, в пункте назначения высокое ключевое слово перемещается в низкое, а затем высокое ключевое слово из источника копируется.xmm3[0] = xmm3[1]; xmm3[1] = xmm4[1]

Но unpcklpd бесполезен, он на 1 байт длиннее и делает то же самое, что SSE1 movlhps.(скопируйте младшее слово из src в старшее слово назначения, оставив младшее слово назначения без изменений.) То же самое для movapd, всегда используйте вместо movaps.

Также re: code-size: стоит использовать префикс REX для использования xmm8..15, поэтому выберите распределение регистров, чтобы использовать xmm8..15 как можно меньше инструкций (или тех, которые уже требуют префикс REX, например, для указателя в r8..15),Размер кода, как правило, не имеет большого значения, но все остальное равно меньше, как правило, лучше.Меньшие инструкции обычно лучше упаковываются в кэш uop.


С AVX вы можете использовать vunpckhpd с любым порядком исходных операндов , причем старшее ключевое слово первого src направляется книзкое слово назначения.Для vmovhlps нет никакого преимущества в размере кода (или другого преимущества), они оба могут использовать 2-байтовый префикс VEX для минимального размера инструкции 4 байта.

Например, vunpckhpd xmm0, xmm1, xmm0 похоже на vmovhlps xmm0, xmm0,xmm1.


Вы можете использовать shufpd или vpshufd для решения проблемы, которую вы пытаетесь решить.Это пустая трата размера кода, потому что он требует немедленного, но, видимо, вы не понимали, что вы можете использовать shufpd xmm0, xmm0, 0b11, чтобы взять (в следующем порядке):

  • низкое слово из xmm0[1](первый операнд src, младший бит непосредственного)
  • старшее слово из xmm0[1] (второй операнд src, старший бит немедленного).

Элемент управления перемешивания может читатьодин и тот же элемент ввода несколько раз.


Интересно, что компилятор NASM будет компилировать VUNPCKHPD только с двумя операндами

NASM позволяет писать такие инструкции, как vaddps xmm0, xmm0, xmm1как vaddps xmm0, xmm1, опуская отдельный целевой операнд, когда он совпадает с первым источником.

Я озадачен, потому что эти значения имеют двойную точность, а не одинарную, но это работает.

Все это просто биты / байты, которые нужно скопировать вокруг .Если вы не используете инструкцию вычисления FP (например, как addpd / addps), «тип» не имеет значения.(По наличию или отсутствию раздела «Исключения с плавающей точкой SIMD» в ручном вводе можно узнать, заботится ли он о значении битов как битовой комбинации FP или нет. Например, addps: https://www.felixcloutier.com/x86/addps#simd-floating-point-exceptions. (Но никаких сюрпризов не бывает. Единственные инструкции, которые действительно заботятся об этом, делают по очень очевидным причинам, например, выполняют вычисления FP или преобразование типов, а не просто копируют данные вокруг.)

Никакие реальные процессоры не заботятся о PSСравнение инструкций по производительности с PD, но некоторые заботятся о vec-int и vec-FP, поэтому, к сожалению, не всегда выгодно использовать pshufd для копирования и перетасовки данных FP или использовать shufps как 2-сточ. целое число в случайном порядке.

К сожалению, до AVX512 не было "целочисленных" случайных чисел общего назначения с 2 источниками, только инструкции palignr и punpck.И до AVX не было инструкций копирования и перемешивания FP.(И по иронии судьбы, vpermilps с немедленным является избыточным по сравнению с vshufps dst, same,same, imm8 за исключением загрузки источника памяти + случайного перемешивания, и его следует избегать по соображениям размера кода. В чем смысл инструкции VPERMILPS (_mm_permute_ps)? )


  movapd xmm2,xmm0 ;preserve both qwords of xmm0
  call Round
     movsd [scratch_register+0],xmm0 ; write low qword to memory
  movhlps xmm0,xmm2
  call Round

Это эффективная перестановка, но, к сожалению, создает ложную зависимость между выводом первого раунда и вводом во второй .Таким образом, два вызова не могут работать параллельно.Вместо этого тасуйте при копировании перед первым вызовом, предпочтительно в регистр, который, как вы знаете, некоторое время был «мертвым» или был частью цепочки зависимостей для значения в xmm0, поэтому должен быть готов перед ним.

  movhlps  xmm2, xmm0   ; extract high qword for later
  call Round                ; round the low qword
  movaps   xmm3, xmm0   ; save the result
  movaps   xmm0, xmm2   ; set up the arg
  call Round                ; round the high qword
  movlhps  xmm3, xmm0    ; re-combine into xmm3

Если у вас не хватает регистров, которых не касается ваша рукописная функция Round, вам не особенно нужна память и она не более эффективна.

В качестве бонуса, все эти *Инструкции 1111 * и movhlps имеют длину всего 3 байта, и их столько же, сколько инструкций в вашей версии.

Еще один вариант (особенно если ваш ввод был в другом регистре для начала)будет сначала Round верхняя половина, затем вы можете положить верхнюю половину обратно в xmm0 с помощью movlhps.

И, кстати, если у вас есть SSE4.1, roundpd может округлить до ближайшего целого числас Ближайшим, в направлении + -Inf (ceil / floor) или в направлении 0 (усечение).


movsd [scratch_register+8],xmm0 ; write low qword to memory
movupd xmm11,[scratch_register]

Никогда не делайте этого, узкий магазин + широкая перезагрузка - гарантированная пересылка в магазинстанцияLL.(~ 10 циклов дополнительной задержки).

Используйте 16-байтовое выровненное хранилище (например, в стеке в [rsp+8] или что-то), и
unpckhpd xmm0, [scratch_register] для загрузки+ shuffle .

К сожалению, Intel разработала инструкции unpck источника памяти плохо, поэтому им требуется 16-байтовый источник памяти, а не только 8 байтов, которые они фактически загружают / используют.Есть несколько случаев, когда

...