x64 ymm/SIMD/векторни инструкции, където регистърът 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 и регистърът, съответстващ на най-ниския зададен бит, се запазва (или се записват всички "set bit" 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 рег. т.е. можете да изразите нещо като в zmm0, заредете words[] от addr aX[], т.е. подадете масив от отмествания на указателя, за да заредите/съхраните всяка съставна част на вектора от/на различен адрес (_mm512_*gather_*() / _mm512_scatter*_*() присъщи). Това, което все още не съществува, е възможността за зареждане на множество правила с една инструкция.   -  person FrankH.    schedule 05.02.2014


Отговори (2)


Инструкциите за запазване на състоянието на x86 (xsave/xrstor) направете вземете маски в edx:eax, за да контролирате какво състояние да запазите/възстановите. Наистина е сложно и ръководството за insn ref просто ви насочва към цял отделен раздел от друго ръководство. IDK обаче, ако можете да избирате на ниво индивидуални векторни регистри. По-вероятно е да има един бит за "ниските 128 от всичките 16 векторни регулации", но ymm0-7 са отделни от останалите, за да се избегне запазването/възстановяването на ymm8-15, когато 32-битовият код не може да ги засегне.

Конкретните запазени компоненти на състоянието съответстват на битовете, зададени в растерната карта с искани характеристики (RFBM), която е логическо И на EDX:EAX и XCR0.

За запазване/възстановяване на няколко ymm regs в пролог/епилог на функция е малко вероятно да е полезно. Не съм го разглеждал. xsavec прави "уплътняване": процесорът проследява кои части от състоянието са действително модифицирани.

Няма други инструкции с допълнително ниво на индиректност за регистрите (регистърът определя кой регистър). Това би било голямо усложнение за внедряването на извънредните машини. Дори инструкциите за многократно зареждане на ARM (вижте другия отговор) имат битовата маска на регистъра, вградена в инструкцията, така че тя е достъпна, докато инструкцията се декодира (вместо да се попълва по-късно).


Вероятно ви е по-добре с очевидното съхраняване/презареждане на всеки векторен регистър, който искате да използвате, но те са запазени за повикване в конвенцията за извикване, която проектирате.

Обърнете внимание, че бъдещите разширения към по-широки вектори означават, че в крайна сметка ще се окаже, че само ниските 256b от избраните от вас векторни правила ще бъдат запазени при повикване, като битовете над това ще бъдат изтрити. (нулирани или не, когато извикваните не ги докосват, вместо запазване/възстановяване).

person Peter Cordes    schedule 27.01.2016

Когато става въпрос за „зареждане / съхраняване на SIMD“, има два възможни различни подхода:

  • извършване на "широко зареждане", попълване на множество векторни регистри от непрекъснати данни на един и същ адрес
  • извършване на "scatter-gather", попълване на векторни съставни части към/от множество адреси

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

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

Intel, с AVX, вместо това създаде възможност за едновременно зареждане от множество адреси, т.е. извършване на „scatter-gather“.

Така че да, 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 документи, "индиректно адресиране") на 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-битова [bitmask или count] стойност непосредствено преди Етикетът на функцията казва кои регистри модифицира извикваният и евентуално също да накара извикващия да постави подобна стойност в r11, преди да извика. Тогава броят на запазванията/възстановяванията винаги е само това, което е необходимо. - person honestann; 06.02.2014
comment
Това може да се направи по няколко начина, но схемата изглежда по-странно, отколкото бих искал предвид набора от инструкции x86-64. Ще потърся по-добър начин известно време, но на този етап изглежда вероятно ще трябва да се задоволим с разширяване на съществуващия/конвенционален подход към допълнителните 16 регистъра. Тези допълнителни 16 векторни регистъра трябва да бъдат обозначени като scratch, но за съжаление има натиск от хора, които не разбират всички проблеми, някои или всички от тях да бъдат запазени. Това изглежда добре на повърхността, но по-внимателен, подробен анализ показва, че това е неразумно. Благодаря. - person honestann; 06.02.2014