Инструкции x64 ymm / SIMD / vector, где регистр ymm указан в регистре?

Существуют ли какие-либо инструкции регистров SIMD / вектора, где регистр ymm указан в общем регистре (или регистре SIMD), а не в самой инструкции?

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

.text
.align 64
funcname:
    orq         %rcx, %rcx                # is register count <= 0 ???
    jle         funcdone                  # # of registers to save is <= 0

    xorq        %rax, %rax                # rax = 0 == vector save element #

funcloop:
    vmovapd     %ymm(%rsi), (%rdi, %rax)  # save ymm?? to address rdi + rax
    addq        $32, %rax                 # each ymm vector is 32-bytes
    loop        funcloop                  # --rcx; if (rcx > 0) { goto funcloop }

funcdone:
    ret

Инструкция vmovapd - странная инструкция, которая делает то, что я ищу. Я уверен, что никогда не видел инструкции, которая выглядела бы так, но это не значит, что нет какой-то необычной инструкции, которая делает то, что мне нужно.

Или, может быть, инструкция будет выглядеть так:

    vmovapd     %rsi, (%rdi, %rax)
    vmovapd     (%rsi), (%rdi, %rax)

Другой альтернативой могут быть биты с 0 по 15 в %rsi, соответствующие векторным регистрам с ymm00 по ymm15, и регистр, соответствующий самому низкому установленному биту, сохраняется (или сохраняются все регистры «установленный бит» ymm).

Кстати, для того, что мне нужно выполнить, самомодифицирующийся код не вариант.


person honestann    schedule 03.02.2014    source источник
comment
Вы не можете этого сделать. Вы можете использовать таблицу переходов (или просто целую кучу веток) и сделать это, но это будет отстой. Может быть, если вы объясните, для чего это на самом деле (на более высоком уровне, чем сохранение диапазона ymm regs), я / другие могут придумать некоторые другие идеи   -  person harold    schedule 03.02.2014
comment
@harold: Учитывая, для чего это нужно, я уверен, что объяснения самого себя жестоко избили бы меня за то, что я задавал явно неуместные вопросы! Был там, делал это раньше. Некоторые люди здесь слишком щепетильно относятся к правилам, на мой вкус. Но да, при отсутствии какой-нибудь блестящей идеи прыжковый стол будет необходим.   -  person honestann    schedule 03.02.2014
comment
Вы можете выполнить адресную арифметику (и использовать некоторые заполнители, чтобы она работала), чтобы пропустить таблицу, но это не намного лучше (если таковая имеется).   -  person harold    schedule 03.02.2014
comment
@harold: Я думаю, вы просто говорите, измерьте, сколько байтов для реализации каждого сохранения, а затем соответствующим образом измените адрес вызова (вместо того, чтобы иметь массив адресов). Если это так, я подозреваю, что таблица переходов более эффективна. Но я вижу, к чему вы идете ... по сути, сделайте так, чтобы сегменты кода вписывались в своего рода массив кода, причем каждый фрагмент кода имел ту же длину, что и все остальные. Это довольно мерзко, но это должно быть как можно быстрее, так что ... Я тоже займусь этим. PS: для нового 64-битного ABI для поддержки протокола интеллектуальных функций для 32 * 512-битных регистров zmm. Не обращай на это внимания! :-)   -  person honestann    schedule 03.02.2014
comment
@honestann, ты к изменениям идет от ARM? Сборка на ARM (а до этого m68k) всегда знала операции многорегистрового сохранения / загрузки, то есть возможность загружать / сохранять произвольное подмножество универсальных или векторных регистров в одной инструкции. Увы, это не то, что Intel когда-либо реализовывала в своей архитектуре ...   -  person FrankH.    schedule 04.02.2014
comment
@FrankH: Нет, но похоже, что я могу работать с ARM впервые в наступающем году из-за доступности AMD Seattle, которая основана на 64-битной ARM. На самом деле должны быть инструкции, которые принимают 32/64-битное значение битовой маски плюс 64-байтовый выровненный адрес и сохраняют / восстанавливают регистры ymm / zmm, заданные битами, заданными в битовой маске. Это сделало бы то, что я считаю очень практичным. Без чего-то подобного, я подозреваю, что большое улучшение, которое я ожидал от нового ABI для поддержки 32 * 512-битных регистров zmm, может быть очень эффективным, но слишком беспорядочным для принятия.   -  person honestann    schedule 05.02.2014
comment
@honestann относительно 512-битных регистров ... что Intel сделала для VGATHER, так это возможность выполнять маскированную загрузку (подмножество составляющих «слов») одного zmm reg. Т.е. вы можете выразить что-то вроде zmm0, загрузить words[] из адреса aX[], т.е. предоставить массив смещений указателя, чтобы загрузить / сохранить каждую составляющую вектора с / по другому адресу (_mm512_*gather_*() / _mm512_scatter*_*() внутренняя функция). Чего до сих пор не существует, так это возможности загружать несколько регистров с помощью одной инструкции.   -  person FrankH.    schedule 05.02.2014


Ответы (2)


Инструкции x86 по сохранению состояния (_1 _ / _ 2_) действительно принимают маски в edx:eax, чтобы контролировать, какое состояние сохранять / восстанавливать. Это действительно сложно, и руководство insn ref просто указывает вам на целый отдельный раздел другого руководства. IDK, если вы можете выбирать на уровне отдельных векторных регистров. Скорее всего, есть один бит для «low128 из всех 16 векторных регистров», но ymm0-7 отделены от остальных, чтобы избежать сохранения / восстановления ymm8-15, когда 32-битный код не может повлиять на них.

Конкретные сохраненные компоненты состояния соответствуют битам, установленным в битовой карте запрошенных функций (RFBM), которая является логическим И для EDX: EAX и XCR0.

Для сохранения / восстановления нескольких ymm regs в прологе / эпилоге функции это вряд ли будет полезно. Я не разбирался в этом. xsavec выполняет «сжатие»: ЦП отслеживает, какие части состояния фактически изменены.

Других инструкций с дополнительным уровнем косвенности для регистров нет (регистр указывает, какой регистр). Это было бы большой проблемой для вышедшего из строя оборудования. Даже инструкции для многократной загрузки ARM (см. Другой ответ) имеют битовую маску регистра, встроенную в инструкцию, поэтому она доступна, когда инструкция декодируется (а не заполняется позже).


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

Обратите внимание, что будущие расширения для более широких векторов означают, что в конечном итоге вы получите только низкие 256 байт выбранных вами векторных регистров, которые будут сохранены, а биты выше, которые будут затираться. (обнулен или нет, когда вызываемые не трогают их, вместо сохранения / восстановления).

person Peter Cordes    schedule 27.01.2016

Когда дело доходит до «загрузки / сохранения SIMD», возможны два разных подхода:

  • выполнение "широких загрузок", заполнение нескольких векторных регистров из смежных данных по одному и тому же адресу
  • выполнение "разброса-сборка", заполнение векторных компонентов на / от нескольких адресов

ARM и / или m68k всегда делали первое - код операции "переместить несколько" на этих платформах ([v]ldm на ARM, movem на m68k) позволяет указать маску, которая перечисляет регистры, которые должны быть заполнены из (последовательных) данных по заданному адресу.

Intel x86 за всю свою историю никогда не имела этого, за исключением PUSHA / POPA в 32-битном режиме, который безоговорочно / без маскировки сохранял / восстанавливал регистры общего назначения в / из стека, и эта инструкция была удалена для 64-битного режима.

Intel с помощью AVX вместо этого создала возможность одновременной загрузки с нескольких адресов, т. Е. Выполнять "разброс-сборку".

Так что да, замена x86 на что-то на ARM вроде:

VLDM.64 r0, {d1, d3, d5, d7}    ; load odd-numbered vector regs from mem @r0

будет последовательность:

VMOVAPD YMM1, [ RAX ]
VMOVAPD YMM3, [ RAX + 32 ]
VMOVAPD YMM5, [ RAX + 64 ]
VMOVAPD YMM7, [ RAX + 96 ]

С другой стороны, эквивалент ARM (см. ARM docs," косвенная адресация ") инструкции x86 VGATHER, например:

VGATHERDD YMM0, [ RAX + YMM1 ]   ; YMM1[0..8] has 32bit offsets on RAX

требует многократных загрузок отдельных элементов векторного регистра с "объединением" в конце - или загрузками подрегистра; это превратилось бы в последовательность:

VLD1.32 {d0}, [r0]                    ; r0..r3 have the [32bit] addresses
VLD1.32 {d1}, [r1]
VLD1.32 {d2}, [r2]
VLD1.32 {d3}, [r3]
VTBL d0, { d0 - d3 }, d4         ; d4 is [ 0, .., 7, ..., 15, ..., 31, ... ] 
person FrankH.    schedule 05.02.2014
comment
Спасибо. Да, эти инструкции отлично подходят для написания многих подпрограмм обработки векторов. К сожалению для меня, я искал кое-что другое. Некоторые люди хотят сохранить [некоторые из] дополнительных 16 векторных регистров, в то время как другие хотят, чтобы они были царапинами, как текущие 16. Я искал схему, в которой 32/64-битное значение [битовая маска или счетчик] непосредственно перед метка функции сообщает, какие регистры модифицирует вызываемый объект, и, возможно, вызывающий также может указать аналогичное значение в r11 перед вызовом. Тогда количество сохранений / восстановлений всегда только то, что нужно. - person honestann; 06.02.2014
comment
Это можно сделать двумя способами, но схема выглядит более странно, чем я бы хотел, учитывая набор инструкций x86-64. Какое-то время я буду искать лучший способ, но на данный момент кажется вероятным, что нам придется довольствоваться расширением существующего / обычного подхода на дополнительные 16 регистров. Эти дополнительные 16 векторных регистров следует обозначить как временные, но, к сожалению, люди, которые не понимают всех проблем, требуют сохранения некоторых или всех из них. На первый взгляд это выглядит неплохо, но более тщательный и подробный анализ показывает, что это неразумно. Спасибо. - person honestann; 06.02.2014