Obsah
1. Problematika blokových přenosů dat
2. Blokový přenos po jednotlivých bajtech
3. Blokový přenos po 64bitových slovech
4. Přenos po dvojici 64bitových slov – instrukce LDP a STP
6. Problematika provedení instrukcí při splnění určité podmínky
7. Nové instrukce s podmínkami
8. Instrukce CSET – Conditional Set
9. Instrukce CSETM – Conditional Set Mask
10. Instrukce CSEL – Conditional Select
11. Instrukce CSINV – Conditional Select Invert
12. Instrukce CSINC – Conditional Select Increment
13. Instrukce CSNEG – Conditional Select Negate
14. Instrukce CINC – Conditional Increment
15. Instrukce CINV – Conditional Invert
16. Instrukce CNEG – Conditional Negate
17. Instrukce TBZ a TBNZ (Test and Branch…)
18. Repositář s demonstračními příklady
19. Předchozí články o architektuře AArch64
1. Problematika blokových přenosů dat
V závěrečné části předchozího článku jsme si ukázali, jakým způsobem je možné přenášet blok dat z jedné oblasti operační paměti do oblasti jiné. Připomeňme si, že jsme si pro tento účel vytvořili pomocné makro, které tuto operaci provede. Toto makro přenáší data po jednotlivých bajtech a lze ho použít vícekrát, protože návěští (label) loop je doplněno automaticky generovaným pořadovým číslem:
.macro moveBlockByBytes from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x4, #\length // počet bajtu loop\@: ldrb w3, [x1], 1 // čtení bajtu strb w3, [x2], 1 // zápis bajtu sub x4, x4, #1 // zmenšení počitadla cbnz x4, loop\@ // pokud jsme se nedostali k nule, skok na zacatek smycky .endm
Ze zápisu tohoto makra je patrné, že smyčka, v níž k přenosu dochází, obsahuje čtveřici instrukcí. První dvě instrukce načtou bajt, který následně uloží (písmeno b na konci jména instrukce určuje, že se pracuje jen s osmi bity registru w3). Současně jsou zvýšeny i hodnoty registrů x1 a x2 obsahujících adresu dalšího čteného resp. zapisovaného bajtu. Třetí instrukce snižuje hodnotu počitadla (počet zbývajících bajtů, které se mají přenést) a poslední instrukcí je podmíněný skok – v případě, že je hodnota registru x4 (tedy počitadla) nenulová, provede se další iterace.
2. Blokový po jednotlivých bajtech
Přenos dat je proveden po jednotlivých bajtech, což i přes (relativní) krátkost programové smyčky znamená, že se ani zdaleka nevyužijí možnosti nabízené 64bitovým mikroprocesorem, který je mnohdy vybaven plnohodnotnou 64bitovou externí datovou sběrnicí. Bylo by tedy dobré zjistit rychlost blokových přenosů. K tomuto účelu si vytvoříme jednoduchý benchmark, který bude přenášet větší blok (ideálně o délce dělitelné šestnácti), a to tolikrát, aby bylo možné dobu běhu změřit standardním nástrojem time. Na konkrétním počítači, na němž byl benchmark odladěn, je přenos řízen touto počítanou programovou smyčkou:
mov x10, #50000 // počet opakování blokového přesunu lsl x10, x10, #8 // ještě zvětšíme počet opakování loop: moveBlockByBytes hello_lbl, buffer, rep_count sub x10, x10, #1 // snížení hodnoty počitadla cbnz x10, loop // pokud se nedosáhlo nuly, opakovat
Úplný zdrojový kód prvního benchmarku vypadá následovně:
# asmsyntax=as # Přesun bloku dat po jednotlivých bajtech. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 sys_write=64 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h # počet bajtu pro blokové přesuny rep_count = 448 # Deklarace makra pro ukončení aplikace .macro exit mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro vytištění zprávy na standardní výstup .macro writeMessage message, messageLength mov x8, #sys_write // číslo sycallu pro funkci "write" mov x0, #1 // standardní výstup ldr x1, =\message // adresa řetězce, který se má vytisknout mov x2, #\messageLength // počet znaků, které se mají vytisknout svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro přesun bloku po bajtech .macro moveBlockByBytes from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x4, #\length // počet bajtu loop\@: ldrb w3, [x1], 1 // čtení bajtu strb w3, [x2], 1 // zápis bajtu sub x4, x4, #1 // zmenšení počitadla cbnz x4, loop\@ // pokud jsme se nedostali k nule, skok na začátek smyčky .endm .balign 8 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum<pad>" #----------------------------------------------------------------------------- .section .bss .lcomm buffer, rep_count // rezervace bufferu pro výstup #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má být dostupný i linkeru _start: writeMessage buffer, rep_count mov x10, #50000 // počet opakování blokového přesunu lsl x10, x10, #8 // ještě zvětšíme počet opakování loop: moveBlockByBytes hello_lbl, buffer, rep_count sub x10, x10, #1 // snížení hodnoty počitadla cbnz x10, loop // pokud se nedosáhlo nuly, opakovat writeMessage buffer, rep_count exit
3. Blokový přenos po 64bitových slovech
Plného využití možností 64bitových mikroprocesorů s 64bitovou externí sběrnicí dosáhneme pouze tehdy, pokud budeme blokové přenosy provádět po 64bitových slovech a nikoli po jednotlivých bajtech. Nejprve je vhodné zajistit zarovnání dat assemblerem:
.balign 8
Následně můžeme makro pro blokový přenos upravit takovým způsobem, aby se data přenášela po celých slovech, konkrétně přes registr x3. Povšimněte si, že adresy v registrech x1 a x2 zvyšujeme po osmi a počitadlo smyčky naopak snižujeme o konstantu 8. Jedinou další úpravou je náhrada instrukcí LDRP a STRP za „plnohodnotné“ 64bitové instrukce LDR a STR:
# Deklarace makra pro přesun bloku po osmi bajtech .macro moveBlockByWords from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x4, #\length // počet bajtů, které se mají přenést loop\@: ldr x3, [x1], 8 // čtení osmi bajtů str x3, [x2], 8 // zápis osmi bajtů sub x4, x4, #8 // zmenšení počitadla cbnz x4, loop\@ // pokud jsme se nedostali k nule, skok na začátek smyčky .endm
Další změny v benchmarku již není nutné provádět, ale stejně si pro jistotu ukažme jeho plnou verzi:
# asmsyntax=as # Presun bloku dat po osmi bajtech. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 sys_write=64 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h # počet bajtů pro blokové přesuny rep_count = 448 # Deklarace makra pro ukončení aplikace .macro exit mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro vytištění zprávy na standardní výstup .macro writeMessage message, messageLength mov x8, #sys_write // číslo sycallu pro funkci "write" mov x0, #1 // standardní výstup ldr x1, =\message // adresa řetězce, který se má vytisknout mov x2, #\messageLength // počet znaků, které se mají vytisknout svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro presun bloku po osmi bajtech .macro moveBlockByWords from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x4, #\length // počet bajtu loop\@: ldr x3, [x1], 8 // čtení osmi bajtů str x3, [x2], 8 // zápis osmi bajtů sub x4, x4, #8 // zmenšení počitadla cbnz x4, loop\@ // pokud jsme se nedostali k nule, skok na začátek smyčky .endm .balign 8 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum<pad>" #----------------------------------------------------------------------------- .section .bss .lcomm buffer, rep_count // rezervace bufferu pro výstup #----------------------------------------------------------------------------- .section .text .global _start // tento symbol má byt dostupný i linkeru _start: writeMessage buffer, rep_count mov x10, #50000 // počet opakování blokového přesunu lsl x10, x10, #8 // ještě zvětšíme počet opakování loop: moveBlockByWords hello_lbl, buffer, rep_count sub x10, x10, #1 // snížení hodnoty počitadla cbnz x10, loop // pokud se nedosáhlo nuly, opakovat writeMessage buffer, rep_count exit
4. Přenos po dvojici 64bitových slov – instrukce LDP a STP
Mikroprocesory s architekturou AArch64 obsahují i dvojici instrukcí určených pro načtení dvou 32bitových nebo 64bitových slov do dvojice zvolených pracovních registrů a taktéž pro uložení dvojice 32bitových a 64bitových registrů do paměti. Tyto instrukce mají mnemotechnické zkratky LDP (Load Pair) a STP (Store Pair). Jedná se o instrukce, které je možné využít k mnoha účelům – typicky při ukládání operandů do paměti před voláním funkce, v přerušovacích rutinách atd. Důležité přitom je, že volba, o jakou dvojici registrů se jedná, je zcela libovolná – může se tedy například jednat o registry, jejichž indexy nejsou sousední atd.
Podívejme se nyní na to, jakým způsobem jsou vlastně instrukce LDP a STP zakódovány. Tyto instrukce musí v instrukčním slovu obsahovat indexy trojice pracovních registrů – jeden z těchto registrů je použit při adresování, další dva registry pak budou obsahovat načtená slova (instrukce LDP) či naopak poslouží jako zdroj dat (konkrétně dvou slov), která se budou ukládat do operační paměti (instrukce STP). Navíc je v instrukčním slovu uložena i relativně krátká (sedmibitová) konstanta. Existují tři různé varianty těchto instrukcí, které se liší podle toho, jakým způsobem je tato konstanta využita:
- Konstanta je použita pro zvýšení obsahu adresovacího registru po provedení operace (post-inkrement, post-dekrement)
- Konstanta je použita pro zvýšení obsahu adresovacího registru před provedením operace (pre-inkrement, pre-dekrement)
- Konstanta je použita ve funkci offsetu, který je přičten k registru použitému pro adresování
Kódování instrukcí s post-inkrementem a post-dekrementem:
31 23 22 21 15 14 10 9 5 4 0 +---------+---+-------+-----+-----+-----+ |101010001| L |iiiiiii| R2 | Rn | R1 | +---------+---+-------+-----+-----+-----+
Bit L slouží k volbě instrukce 0-STP a 1-LDP. Samotná konstanta je sedmibitová a je uložena v bitovém poli iiiiiii.
Kódování instrukcí s pre-inkrementem a pre-dekrementem:
31 23 22 21 15 14 10 9 5 4 0 +---------+---+-------+-----+-----+-----+ |101010011| L |iiiiiii| R2 | Rn | R1 | +---------+---+-------+-----+-----+-----+
Kódování instrukcí s offsetem:
31 23 22 21 15 14 10 9 5 4 0 +---------+---+-------+-----+-----+-----+ |101010010| L |iiiiiii| R2 | Rn | R1 | +---------+---+-------+-----+-----+-----+
Se znalostí instrukcí LDP a STP můžeme upravit makro pro blokové přenosy dat takovým způsobem, aby se v každé iteraci smyčky přenesla dvojice 64bitových slov, tedy dohromady šestnáct bajtů. Samozřejmě se musí příslušným způsobem pracovat i s počitadlem smyčky, změnou adresy atd:
# Deklarace makra pro presun bloku po dvou slovech .macro moveBlockByTwoWords from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x5, #\length // počet bajtu loop\@: ldp x3, x4, [x1], 16 // čtení 2x osmi bajtů stp x3, x4, [x2], 16 // zápis 2x osmi bajtů sub x5, x5, #16 // zmenšení počitadla cbnz x5, loop\@ // pokud jsme se nedostali k nule, skok na začátek smyčky .endm
V případě, že je zaručeno zarovnání bloků na hranice šestnácti bajtů, lze náš demonstrační příklad převést do podoby:
# asmsyntax=as # Presun bloku dat po 2x osmi bajtech. # # Autor: Pavel Tišnovský # Linux kernel system call table sys_exit=93 sys_write=64 # List of syscalls for AArch64: # https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h # počet bajtu pro blokove presuny rep_count = 448 # Deklarace makra pro ukončení aplikace .macro exit mov x8, #sys_exit // číslo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro vytištění zprávy na standardní výstup .macro writeMessage message, messageLength mov x8, #sys_write // číslo sycallu pro funkci "write" mov x0, #1 // standardní výstup ldr x1, =\message // adresa řetězce, který se má vytisknout mov x2, #\messageLength // počet znaků, které se mají vytisknout svc 0 // volání Linuxového kernelu .endm # Deklarace makra pro presun bloku po dvou slovech .macro moveBlockByTwoWords from, to, length ldr x1, =\from // adresa bloku pro čtení ldr x2, =\to // adresa bloku pro zápis mov x5, #\length // počet bajtu loop\@: ldp x3, x4, [x1], 16 // čtení 2x osmi bajtů stp x3, x4, [x2], 16 // zápis 2x osmi bajtů sub x5, x5, #16 // zmenšení počitadla cbnz x5, loop\@ // pokud jsme se nedostali k nule, skok na začátek smyčky .endm .balign 16 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum<pad>" #----------------------------------------------------------------------------- .section .bss .lcomm buffer, rep_count // rezervace bufferu pro výstup #----------------------------------------------------------------------------- .section .text .global _start // tento symbol ma byt dostupny i linkeru _start: writeMessage buffer, rep_count mov x10, #50000 // počet opakování blokového přesunu lsl x10, x10, #8 // ještě zvětšíme počet opakování loop: moveBlockByTwoWords hello_lbl, buffer, rep_count sub x10, x10, #1 // snížení hodnoty počitadla cbnz x10, loop // pokud se nedosáhlo nuly, opakovat writeMessage buffer, rep_count exit
5. Výsledky benchmarků
Všechny tři demonstrační příklady, které jsme si prozatím popsali, provádí opakovaný přenos stejného bloku dat, přičemž počet opakování přenosu (a tím pádem i celkový počet přenesených bajtů) je stále stejný. Můžeme tudíž porovnat časy běhu všech příkladů a zjistit, do jaké míry je přenos po slovech nebo „dvojslovech“ rychlejší, než přenos po jednotlivých bajtech.
Využití makra pro blokový přenos po bajtech:
$ time ./move5 real 0m3.909s user 0m3.908s sys 0m0.000s
Využití makra pro blokový přenos po 64bitových slovech:
$ time ./move6 real 0m0.498s user 0m0.498s sys 0m0.000s
Využití makra pro blokový přenos po dvojici 64bitových slov:
$ time ./move7 real 0m0.258s user 0m0.257s sys 0m0.000s
6. Problematika provedení instrukcí při splnění určité podmínky
Ve druhé části dnešního článku se zaměříme na prozatím relativně novou oblast. Jedná se o speciální instrukce implementované na moderní mikroprocesorové architektuře AArch64, které provádí specifikovanou činnost (nastavení registru na určitou hodnotu, inverze obsahu registru atd.), ale pouze za předpokladu, že je splněna nějaká zadaná podmínka (kód podmínky je přitom součástí instrukčního slova). Připomeňme si, že na rozdíl od původní 32bitové architektury ARM (dnes označované ARM32 nebo též jen A32) nemají všechny instrukce na AArch64 rezervovány čtyři nejvyšší bity pro specifikaci podmínky. Tato vlastnost byla na základě analýzy existujících programových strojových kódů, které za dobu existence platformy ARM vznikly, zrušena a podmínky je tak možné použít pouze u vybrané množiny instrukcí. Do této množiny byly zahrnuty i zcela nové instrukce, které na původní platformě ARM32 nenalezneme, což je opět důsledek analýzy stávajících strojových kódů, ale i studia funkce překladačů.
Připomeňme si, že na procesorové architektuře AArch64 je použito celkem čtrnáct různých podmínek, k nimž se někdy přidává i pseudopodmínka AL neboli Any/Always. V tomto případě se samozřejmě o žádnou skutečnou podmínku nejedná, neboť je instrukce provedena v každém případě, tedy nezávisle na aktuálním stavu příznakových bitů.
Prvních šest podmínek testuje hodnotu pouze jediného bitového příznaku, a to N (negative), Z (zero) či V (overflow):
Kód | Přípona | Význam | Testovaná podmínka |
---|---|---|---|
0000 | EQ | Z = 1 | rovnost po porovnání (či nulový výsledek) |
0001 | NE | Z = 0 | nerovnost po porovnání (či nenulový výsledek) |
0100 | MI | N = 1 | výsledek je záporný |
0101 | PL | N = 0 | výsledek je kladný či nulový |
0110 | VS | V = 1 | nastalo přetečení (overflow) |
0111 | VC | V = 0 | nenastalo přetečení (overflow) |
1110 | AL | Any/Always | většinou se nezapisuje, implicitní podmínka |
Další čtyři podmínkové kódy se většinou používají při porovnávání dvou hodnot bez znaménka (unsigned). V těchto případech se testují stavy příznakových bitů C (carry) a Z (zero), přesněji řečeno kombinace těchto bitů:
Kód | Přípona | Význam | Testovaná podmínka |
---|---|---|---|
0010 | CS/HS | C = 1 | ≥ |
0011 | CC/LO | C = 0 | < |
1000 | HI | C = 1 & Z = 0 | > |
1001 | LS | C = 0 | Z = 1 | ≤ |
Poslední čtyři podmínkové kódy se používají pro porovnávání hodnot se znaménkem (signed). V těchto případech se namísto příznakových bitů © carry a (Z) zero testují kombinace bitů (N) negative, (V) overflow a (Z) zero:
Kód | Přípona | Význam | Testovaná podmínka |
---|---|---|---|
1010 | GE | N == V | ≥ |
1011 | LT | N ≠ V | < |
1100 | GT | Z = 0, N = V | > |
1101 | LE | Z = 1, N ≠ V | ≤ |
Důležité přitom je, že všechny dále popsané instrukce podporují všechny výše zmíněné podmínky. Výjimku tvoří minule popsané instrukce CBZ a CBNZ, které pouze testovaly nulovost či naopak nenulovost vybraného pracovního registru.
Instrukce | Stručný popis instrukce |
---|---|
B.podmínka návěští | podmíněný skok po splnění podmínky na zadané návěští (kódy podmínek byly uvedeny výše) |
CBZ Wn, návěští | pokud platí Wn=0, skok na zadané návěští |
CBZ Xn, návěští | pokud platí Xn=0, skok na zadané návěští |
CBNZ Wn, návěští | pokud platí Wn≠0, skok na zadané návěští |
CBNZ Xn, návěští | pokud platí Xn≠0, skok na zadané návěští |
7. Nové instrukce s podmínkami
Mezi nové instrukce s podmínkou, které lze použít na architektuře AArch64, patří především:
Instrukce | Význam mnemotechnického kódu instrukce | Kapitola |
---|---|---|
CSET | Conditional Set | 8 |
CSETM | Conditional Set Mask | 9 |
CSEL | Conditional Select | 10 |
CSINV | Conditional Select Invert | 11 |
CSINC | Conditional Select Increment | 12 |
CSNEG | Conditional Select Negate | 13 |
CINC | Conditional Increment | 14 |
CINV | Conditional Invert | 15 |
CNEG | Conditional Negate | 16 |
TBZ | Test and Branch if Zero | 17 |
TBNZ | Test and Branch if not zero | 17 |
Kromě instrukcí TBZ a TBNZ mají ostatní instrukce prakticky shodné instrukční slovo, které je rozděleno do několika bitových polí:
31 30 29 28 21 20 16 15 12 9 5 4 0 +--+--+-+--------+-----+-----+---+-----+-----+ |sf|op|S|11010100| Rm |cond |op2| Rn | Rd | +--+--+-+--------+-----+-----+---+-----+-----+
Povšimněte si, že v instrukčním slovu je určena trojice pracovních registrů, z nichž dva obsahují zdrojové operandy a třetí pak výsledek činnosti instrukce.
Podle bitů v bitových polích sf, op, S a op2 se rozlišuje, o jakou instrukci se jedná a taktéž, zda se jedná o 32bitovou variantu (s registry Wx) nebo o variantu 64bitovou (s registry Xx):
sf | op | S | op2 | Instrukce | Varianta |
---|---|---|---|---|---|
0 | 0 | 0 | 00 | CSEL | 32 bitů |
0 | 0 | 0 | 01 | CSINC | 32 bitů |
0 | 1 | 0 | 00 | CSINV | 32 bitů |
0 | 1 | 0 | 01 | CSNEG | 32 bitů |
1 | 0 | 0 | 00 | CSEL | 64 bitů |
1 | 0 | 0 | 01 | CSINC | 64 bitů |
1 | 1 | 0 | 00 | CSINV | 64 bitů |
1 | 1 | 0 | 01 | CSNEG | 64 bitů |
Instrukční slova instrukcí TBZ a TBNZ mají odlišný formát, s nímž se seznámíme v sedmnácté kapitole.
8. Instrukce CSET – Conditional Set
První novou instrukcí s podmínkou, s níž se v dnešním článku seznámíme, je instrukce nazvaná CSET, což je mnemotechnická zkratka celého názvu Conditional Set. Tato instrukce vlastně přímo odpovídá požadavkům kladeným na datový typ boolean v mnoha programovacích jazycích, v nichž je hodnota true interně reprezentována jedničkou a hodnota false nulou. Tato instrukce existuje ve dvou variantách, přičemž první varianta pracuje s 32bitovým a druhá varianta s 64bitovým operandem. Zápis této instrukce v assembleru (včetně GNU Assembleru) může vypadat následovně:
CSET Wd, podmínka CSET Xd, podmínka
Například (ukázka pro různé podmínky):
CSET W3, EQ CSET W4, MI CSET X5, HI
Tato instrukce pracuje následujícím způsobem – v případě, že je podmínka zapsaná ve druhém operandu cond splněna (tedy pokud mají podmínkové/příznakové bity očekávané hodnoty), uloží se do cílového 32bitového registru Wd či do 64bitového registru Xd hodnota 1. Pokud podmínka naopak splněna není, uloží se do registru Wd či Xd hodnota 0:
cíl = condition ? 1 : 0;
Ve skutečnosti se v případě instrukce CSET jedná o alias pro instrukci CSINC popsanou dále (podmínka ovšem musí být v tomto případě negována):
CSINC Wd, WZR, WZR, invert(podmínka) CSINC Xd, XZR, XZR, invert(podmínka)
neboli:
cíl = invert(condition) ? 0 : 0+1;
9. Instrukce CSETM – Conditional Set Mask
V některých případech je nutné ukládat pravdivostní hodnoty true a false odlišným způsobem – pravdivostní hodnota true bude reprezentována konstantou, v níž jsou všechny bity nastaveny na jedničku (v případě celých čísel se znaménkem to odpovídá hodnotě –1), hodnota false naopak konstantou, v níž jsou všechny bity nulové. V tomto případě je možné pro nastavení pravdivostní hodnoty na základě podmínky použít instrukci s mnemotechnickou zkratkou CSETM neboli Conditional Set Mask:
CSETM Wd, podmínka CSETM Xd, podmínka
Ve vyšším programovacím jazyce by bylo možné napsat:
cíl = condition ? -1 : 0;
V případě instrukce CSETM se opět jedná o aliasy, tentokrát ovšem na instrukci CSINV a nikoli CSINC:
CSINV Wd, WZR, WZR, invert(podmínka) CSINV Xd, XZR, XZR, invert(podmínka)
10. Instrukce CSEL – Conditional Select
Další v praxi užitečnou instrukcí s podmínkou je instrukce zapisovaná mnemotechnickým kódem CSEL neboli plným jménem Conditional Select. I tato instrukce existuje ve dvou variantách – 32bitové a 64bitové:
CSEL Wd, Wn, Wm, podmínka CSEL Xd, Xn, Xm, podmínka
Instrukce CSEL pracuje následovně: v případě, že je podmínka splněna, uloží se do cílového registru Wd či Xd hodnota z prvního zdrojového registru Wn nebo Xn. Pokud podmínka splněna není, je do cílového registru Wd/Xd uložena hodnota z druhého zdrojového registru Wm/Xm.
Instrukce CSEL tedy nahrazuje programovou konstrukci typu:
cíl = podmínka ? zdroj1 : zdroj2;
if: B.neg_podmínka else MOV Wd, Wn B endif else: MOV Wd, Wm endif:
11. Instrukce CSINV – Conditional Select Invert
Alternativní formou instrukce CSEL popsané v předchozí kapitole je instrukce s mnemotechnickou zkratkou CSINV neboli Conditional Select Invert:
CSINV Wd, Wn, Wm, podmínka CSINV Xd, Xn, Xm, podmínka
Tato instrukce pracuje následovně: v případě, že je zapsaná podmínka splněna, uloží se do cílového registru Wd či Xd hodnota z prvního zdrojového registru Wn nebo Xn. Pokud podmínka splněna není, je do cílového registru Wd/Xd uložena negovaná hodnota přečtená z druhého zdrojového registru Wm/Xm. Opět by pochopitelně bylo možné tuto instrukci nahradit složitějším kódem, v němž by byl použit nepodmíněný skok B i skok podmíněný B.podmínka.
Instrukce CSINV tedy nahrazuje programovou konstrukci typu:
cíl = podmínka ? zdroj1 : ~zdroj2;
12. Instrukce CSINC – Conditional Select Increment
Zajímavá je instrukce CSINC, která kombinuje možnosti instrukce CINC a CSEL. Zápis této instrukce v assembleru vypadá následovně:
CSINC Wd, Wn, Wm, podmínka CSINC Xd, Xn, Xm, podmínka
Tato instrukce provádí následující činnost:
Wd = podmínka ? Wn : Wm+1; Xd = podmínka ? Xn : Xm+1;
Jak jsme si již řekli v osmé kapitole, je touto instrukcí realizována i pseudoinstrukce CSET, a to tehdy, pokud jsou oba zdrojové registry nulové (WZR a XZR). V tomto případě se totiž do cílového registru dosadí buď hodnota 0 nebo hodnota 0+1=1:
CSINC Wd, WZR, WZR, invert(podmínka) CSINC Xd, XZR, XZR, invert(podmínka)
13. Instrukce CSNEG – Conditional Select Negate
Instrukce nazvaná CSNEG se do jisté míry podobá již popsané instrukci CSINV, ovšem s tím rozdílem, že se namísto jedničkového doplňku (negace) používá při nesplnění podmínky dvojkový doplněk. Opět je možné použít 32bitovou či 64bitovou variantu této instrukce:
CSNEG Wd, Wn, Wm, podmínka CSNEG Xd, Xn, Xm, podmínka
Tato instrukce pracuje následovně: pokud je podmínka splněna, uloží se do cílového registru Wd či Xd hodnota z prvního zdrojového registru Wn nebo Xn. Pokud podmínka naopak splněna není, je do cílového registru Wd/Xd uložena hodnota přečtená z druhého zdrojového registru Wm/Xm, u které se nejdříve změní znaménko (tedy vypočte se onen zmíněný dvojkový doplněk).
Tato instrukce tedy nahrazuje programovou konstrukci typu:
cíl = podmínka ? zdroj1 : -zdroj2;
14. Instrukce CINC – Conditional Increment
Instrukce CINC je jmenným aliasem pro instrukci CSINC, ovšem s převrácenou podmínkou a shodnými zdrojovými registry. Zápis této instrukce v assembleru:
CINC Wd, Wn, podmínka CINC Xd, Xn, podmínka
Tato instrukce provádí následující činnost (povšimněte si, že je zde pouze jediný zdrojový registr):
Wd = podmínka ? Wn+1 : Wn; Xd = podmínka ? Xn+1 : Xn;
Použití této instrukce je různé. Může se například použít pro realizaci příkazu continue v programovacím jazyku C.
15. Instrukce CINV – Conditional Invert
Podobná instrukce taktéž s jedním zdrojovým registrem se jmenuje CINV a v assembleru se zapisuje následovně:
CINV Wd, Wn, podmínka CINV Xd, Xn, podmínka
Prováděná činnost je následující (připomeňme si, že tilda znamená negaci bit po bitu):
Wd = podmínka ? ~Wn : Wn; Xd = podmínka ? ~Xn : Xn;
Ve skutečnosti se opět jedná o instrukční alias rozpoznávaný assemblery (resp. většinou z nich). V tomto případě lze instrukci CINV nahradit instrukcí CSINV s oběma zdrojovými registry totožnými:
CSINV Wd, Wn, Wn, invert(podmínka) CSINV Xd, Xn, Xn, invert(podmínka)
16. Instrukce CNEG – Conditional Negate
Poslední instrukce ze skupiny přenosových instrukcí s podmínkou, přesněji řečeno (opět) instrukční alias se jmenuje CNEG:
CNEG Wd, Wn, podmínka CNEG Xd, Xn, podmínka
Činnost prováděná touto instrukcí je následující:
Wd = podmínka ? -Wn : Wn; Xd = podmínka ? -Xn : Xn;
Tento alias lze nahradit za CSNEG s totožnými zdrojovými registry a opačně zapsanou podmínkou:
CSNEG Wd, Wn, Wn, invert(podmínka) CSNEG Xd, Xn, Xn, invert(podmínka)
17. Instrukce TBZ a TBNZ (Test and Branch…)
Poslední dvě instrukce, které si dnes popíšeme, spadají do kategorie podmíněných skoků. Tyto instrukce se jmenují TBZ (Test and Branch if Zero) a TBNZ (Test and Branch if Not Zero). Způsob zápisu těchto instrukcí v assembleru je následující:
TBZ Xn, #konstanta, návěští TBZ Wn, #konstanta, návěští TBNZ Xn, #konstanta, návěští TBNZ Wn, #konstanta, návěští
Konstanta má v případě těchto instrukcí šířku pouze šesti bitů, protože je v ní uložen index bitu pracovního registru, který se testuje na nulu či jedničku (u registrů Wn by stačilo jen pět bitů). V případě instrukce TBZ – pokud je n-tý bit registru Xn/Wn nastavený na nulu, provede se skok, v opačném případě se řízení přenese na další instrukci. V případě instrukce TBNZ je bit testován na nulu. Vzhledem k tomu, že v instrukčním slovu je nutné kromě adresy cíle (návěští) specifikovat i číslo pracovního registru a index bitu, je tento typ skoku omezen na rozsah ±32kB, což by ovšem v praxi mělo být více než dostačující (v opačném případě lze TBZ/TBNZ zkombinovat s absolutním skokem B):
31 30 25 24 23 19 18 5 4 0 +--+------+--+------+--------------+-----+ |b5|011011|op|b4..b0| immediate 14 | Rt | +--+------+--+------+--------------+-----+
18. Repositář s demonstračními příklady
Všechny minule i dnes popisované demonstrační příklady byly společně s podpůrným souborem Makefile určeným pro jejich překlad či naopak pro disassembling, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny příklady jsou určeny pro standardní GNU Assembler a používají výchozí syntaxi procesorů AArch64. Následuje tabulka s odkazy na zdrojové kódy příkladů i na již zmíněné podpůrné skripty:
19. Předchozí články o architektuře AArch64
S architekturou AArch64 jsme se již na stránkách Roota setkali, a to konkrétně v následujících článcích, z nichž dnes vycházíme:
- 64bitové mikroprocesory s architekturou AArch64
https://www.root.cz/clanky/64bitove-mikroprocesory-s-architekturou-aarch64/ - Instrukční sada AArch64
https://www.root.cz/clanky/instrukcni-sada-aarch64/ - Instrukční sada AArch64 (2.část)
https://www.root.cz/clanky/instrukcni-sada-aarch64–2-cast/ - Tvorba a ladění programů v assembleru mikroprocesorů AArch64
https://www.root.cz/clanky/tvorba-a-ladeni-programu-v-assembleru-mikroprocesoru-aarch64/ - Instrukční sada AArch64: technologie NEON
https://www.root.cz/clanky/instrukcni-sada-aarch64-technologie-neon/ - Specifické vlastnosti procesorů AArch64: základní instrukce
https://www.root.cz/clanky/specificke-vlastnosti-procesoru-aarch64-zakladni-instrukce/ - Specifické vlastnosti procesorů AArch64: podmíněné a nepodmíněné skoky, adresování dat
https://www.root.cz/clanky/specificke-vlastnosti-procesoru-aarch64-podminene-a-nepodminene-skoky-adresovani-dat/
20. Odkazy na Internetu
- Arm Architecture Reference Manual for A-profile architecture
https://developer.arm.com/documentation/ddi0487/latest - The GNU Assembler – macros
http://tigcc.ticalc.org/doc/gnuasm.html#SEC109 - GNU Binutils
https://sourceware.org/binutils/ - Documentation for binutils 2.38
https://sourceware.org/binutils/docs-2.38/ - AArch64 Instruction Set Architecture
https://developer.arm.com/architectures/learn-the-architecture/aarch64-instruction-set-architecture/instruction-sets-in-the-arm-architecture - Arm Armv8-A A32/T32 Instruction Set Architecture
https://developer.arm.com/documentation/ddi0597/2021–12/?lang=en - Comparison of ARMv8-A cores
https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores - Cortex-A32 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a32-processor.php - Cortex-A35 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a35-processor.php - Cortex-A53 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a53-processor.php - Cortex-A57 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a57-processor.php - Cortex-A72 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php - Cortex-A73 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a73-processor.php - Apple A7 (SoC založen na CPU Cyclone)
https://en.wikipedia.org/wiki/Apple_A7 - System cally pro AArch64 na Linuxu
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - Architectures/AArch64 (FedoraProject.org)
https://fedoraproject.org/wiki/Architectures/AArch64 - SIG pro AArch64 (CentOS)
https://wiki.centos.org/SpecialInterestGroup/AltArch/AArch64 - The ARMv8 instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - A64 Instruction Set
https://developer.arm.com/products/architecture/instruction-sets/a64-instruction-set - Switching between the instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - The A64 instruction set
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - Introduction to ARMv8 64-bit Architecture
https://quequero.org/2014/04/introduction-to-arm-architecture/ - MCU market turns to 32-bits and ARM
http://www.eetimes.com/document.asp?doc_id=1280803 - Cortex-M0 Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0.php - Cortex-M0+ Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0plus.php - ARM Processors in a Mixed Signal World
http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world - ARM Architecture (Wikipedia)
https://en.wikipedia.org/wiki/ARM_architecture - DSP for Cortex-M
https://developer.arm.com/technologies/dsp/dsp-for-cortex-m - Cortex-M processors in DSP applications? Why not?!
https://community.arm.com/processors/b/blog/posts/cortex-m-processors-in-dsp-applications-why-not - White Paper – DSP capabilities of Cortex-M4 and Cortex-M7
https://community.arm.com/processors/b/blog/posts/white-paper-dsp-capabilities-of-cortex-m4-and-cortex-m7 - Q (number format)
https://en.wikipedia.org/wiki/Q_%28number_format%29 - TriCore Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/32-bit-tricore-tm-microcontroller/tricore-tm-architecture-and-core/channel.html?channel=ff80808112ab681d0112ab6b73d40837 - TriCoreTM V1.6 Instruction Set: 32-bit Unified Processor Core
http://www.infineon.com/dgdl/tc_v131_instructionset_v138.pdf?fileId=db3a304412b407950112b409b6dd0352 - TriCore v2.2 C Compiler, Assembler, Linker Reference Manual
http://tasking.com/support/tricore/tc_reference_guide_v2.2.pdf - Infineon TriCore (Wikipedia)
https://en.wikipedia.org/wiki/Infineon_TriCore - C166®S V2 Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/16-bit-c166-microcontroller/c166-s-v2-architecture-and-core/channel.html?channel=db3a304312bef5660112c3011c7d01ae - Comparing four 32-bit soft processor cores
http://www.eetimes.com/author.asp?section_id=14&doc_id=1286116 - RISC-V Instruction Set
http://riscv.org/download.html#spec_compressed_isa - RISC-V Spike (ISA Simulator)
http://riscv.org/download.html#isa-sim - RISC-V (Wikipedia)
https://en.wikipedia.org/wiki/RISC-V - David Patterson (Wikipedia)
https://en.wikipedia.org/wiki/David_Patterson_(computer_scientist) - OpenRISC (oficiální stránky projektu)
http://openrisc.io/ - OpenRISC architecture
http://openrisc.io/architecture.html - Emulátor OpenRISC CPU v JavaScriptu
http://s-macke.github.io/jor1k/demos/main.html - OpenRISC (Wikipedia)
https://en.wikipedia.org/wiki/OpenRISC - OpenRISC – instrukce
http://sourceware.org/cgen/gen-doc/openrisc-insn.html - OpenRISC – slajdy z přednášky o projektu
https://iis.ee.ethz.ch/~gmichi/asocd/lecturenotes/Lecture6.pdf - Berkeley RISC
http://en.wikipedia.org/wiki/Berkeley_RISC - Great moments in microprocessor history
http://www.ibm.com/developerworks/library/pa-microhist.html - Microprogram-Based Processors
http://research.microsoft.com/en-us/um/people/gbell/Computer_Structures_Principles_and_Examples/csp0167.htm - Great Microprocessors of the Past and Present
http://www.cpushack.com/CPU/cpu1.html - A Brief History of Microprogramming
http://www.cs.clemson.edu/~mark/uprog.html - What is RISC?
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/whatis/ - RISC vs. CISC
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/risccisc/ - RISC and CISC definitions:
http://www.cpushack.com/CPU/cpuAppendA.html - FPGA
https://cs.wikipedia.org/wiki/Programovateln%C3%A9_hradlov%C3%A9_pole - The Evolution of RISC
http://www.ibm.com/developerworks/library/pa-microhist.html#sidebar1 - disasm.pro
https://disasm.pro/ - Exploring AArch64 assembler – Chapter 5
https://thinkingeek.com/2016/11/13/exploring-aarch64-assembler-chapter-5/