Funkce vestavěné v GCC pro nalezení délky sekvence nulových bitů

28. 8. 2025
Doba čtení: 31 minut

Sdílet

Autor: Depositphotos
Dnes si ukážeme další funkce vestavěné do GCC, jež slouží pro realizaci nízkoúrovňových operací. Zaměříme se na funkce umožnující ve vstupní hodnotě nalézt délku sekvence nulových bitů.

Obsah

1. Funkce vestavěné v GCC pro provádění nízkoúrovňových bitových operací a rotací (2. část)

2. Vyhledání prvního nebo posledního bitu se zadanou hodnotou vs. hledání sekvence stejných bitů

3. Důležitá odbočka: instrukce pro hledání bitů dostupné na architektuře x86–64

4. Základní použití instrukcí BSF a BSR

5. Realizace výpočtů pozice nejvyššího a nejnižšího bitu s jedničkovou hodnotou pro vstupy 0 až 16

6. Chování instrukcí BSF a BSR v případě, že je vstup nulový

7. Upravené příklady pro výpočty pozice nejvyššího a nejnižšího bitu s jedničkovou hodnotou pro vstupy 0 až 16

8. Vypočtené výsledky

9. Zjištění počtu nulových bitů na začátku nebo konci testované hodnoty

10. Příklad použití instrukce LZCNT

11. Příklad použití instrukce TZCNT

12. LZCNT == REP BSR

13. TZCNT == REP BSF

14. Volání vestavěné funkce __builtin_stdc_first_trailing_one

15. Překlad funkce __builtin_stdc_first_trailing_one do strojového kódu

16. Analýza přeloženého kódu

17. Překlad pro platformu AArch64

18. Příloha: soubor Makefile pro překlad příkladů napsaných v assembleru NASM

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Funkce vestavěné v GCC pro provádění nízkoúrovňových bitových operací a rotací (2. část)

Na předchozí článek o podpoře bitových rotací a výpočtu parity dnes navážeme. Ukážeme si některé další funkce vestavěné do GCC, které slouží pro realizaci nízkoúrovňových operací. Zaměříme se na funkce, které dokážou ve vstupní hodnotě nalézt délku sekvence nulových bitů, přičemž se tato sekvence hledá od nejvyššího bitu nebo naopak od bitu nejnižšího. Tyto operace se označují zkratkami clz (count leading zeros) a ctz (count trailing zeros). Existují ještě podobné operace ffs (find first set) a fls (find last set), které hledají index prvního nebo posledního bitu nastaveného na jedničku.

Ukážeme si, jak jsou tyto operace realizovány na architektuře x86–64, která k tomuto účelu nabízí hned čtyři instrukce.

Jen pro úplnost si vypišme všechny vestavěné funkce nabízené překladačem GCC, které jsou určeny pro provádění nízkoúrovňových operací. Velká část těchto funkcí realizuje operace popsané v úvodním odstavci:

int __builtin_ffs(int x)
int __builtin_clz(unsigned int x)
int __builtin_ctz(unsigned int x)
int __builtin_clrsb(int x)
int __builtin_popcount(unsigned int x)
int __builtin_ffsl(long)
int __builtin_clzl(unsigned long)
int __builtin_ctzl(unsigned long)
int __builtin_clrsbl(long)
int __builtin_popcountl(unsigned long)
int __builtin_ffsll(long long)
int __builtin_clzll(unsigned long long)
int __builtin_ctzll(unsigned long long)
int __builtin_clrsbll(long long)
int __builtin_popcountll(unsigned long long)
int __builtin_ffsg(...)
int __builtin_clzg(...)
int __builtin_ctzg(...)
int __builtin_clrsbg(...)
int __builtin_popcountg(...)
type __builtin_stdc_bit_ceil(type arg)
type __builtin_stdc_bit_floor(type arg)
unsigned int __builtin_stdc_bit_width(type arg)
unsigned int __builtin_stdc_count_ones(type arg)
unsigned int __builtin_stdc_count_zeros(type arg)
unsigned int __builtin_stdc_first_leading_one(type arg)
unsigned int __builtin_stdc_first_leading_zero(type arg)
unsigned int __builtin_stdc_first_trailing_one(type arg)
unsigned int __builtin_stdc_first_trailing_zero(type arg)
unsigned int __builtin_stdc_has_single_bit(type arg)
unsigned int __builtin_stdc_leading_ones(type arg)
unsigned int __builtin_stdc_leading_zeros(type arg)
unsigned int __builtin_stdc_trailing_ones(type arg)
unsigned int __builtin_stdc_trailing_zeros(type arg)

2. Vyhledání prvního nebo posledního bitu se zadanou hodnotou vs. hledání sekvence stejných bitů

První čtveřice vestavěných funkcí slouží pro nalezení prvního bitu se zadanou hodnotou (nula či jedna). Vyhledávání prvního bitu může začínat od nejnižšího či naopak od nejvyššího bitu a korektně se reaguje i na situace, kdy žádný z bitů nenabývá hledané hodnoty (tj. hledáme jedničkový bit v hodnotě 0×0000 či nulový bit v hodnotě 0×ffff):

unsigned int __builtin_stdc_first_leading_one(type arg)
unsigned int __builtin_stdc_first_leading_zero(type arg)
unsigned int __builtin_stdc_first_trailing_one(type arg)
unsigned int __builtin_stdc_first_trailing_zero(type arg)
Poznámka: nejnižší bit má v tomto případě index roven jedné! Hodnota nula je rezervována pro situace, ve kterých nebyl bit nalezen.

Další funkce hledají sekvenci bitů se stejnou hodnotou, a to opět od nejnižšího nebo od nejvyššího bitu. Opět se pochopitelně jedná o čtveřici funkcí:

unsigned int __builtin_stdc_leading_ones(type arg)
unsigned int __builtin_stdc_leading_zeros(type arg)
unsigned int __builtin_stdc_trailing_ones(type arg)
unsigned int __builtin_stdc_trailing_zeros(type arg)
Poznámka: v případě, že se vrátí 0, znamená to, že vstupní hodnota nezačíná očekávanou sekvencí, resp. že je tato sekvence nulová.

Existují ještě starší funkce s podobným významem, které ovšem nemají definovanou hodnotu v případě, že nebyl bit nalezen. A navíc se bity indexují od nuly a nikoli od jedničky:

int __builtin_ffs(int x)
int __builtin_ffsl(long)
int __builtin_ffsll(long long)
 
int __builtin_clz(unsigned int x)
int __builtin_clzl(unsigned long)
int __builtin_clzll(unsigned long long)
 
int __builtin_ctz(unsigned int x)
int __builtin_ctzl(unsigned long)
int __builtin_ctzll(unsigned long long)

Modernější varianty těchto funkcí jsou generické (první operand je jakéhokoli celočíselného typu) a jako druhý operand akceptují hodnotu vrácenou v případě, že bit s hledanou hodnotou není nalezen (můžeme například předat –1 atd.):

int __builtin_ffsg(...)
int __builtin_clzg(...)
int __builtin_ctzg(...)

3. Důležitá odbočka: instrukce pro hledání bitů dostupné na architektuře x86–64

Ještě předtím, než si ukážeme způsob použití a překladu vestavěných funkcí určených pro nalezení nulových či naopak nenulových bitů, je užitečné, poučné a zajímavé se podívat, jaké instrukce, které tuto činnost provádí, jsou vlastně k dispozici a jak se chovají. Zaměřme se na platformu x86–64, která vychází z platformy x86. Zde máme k dispozici hned čtyři instrukce, které mají odlišnou historii vzniku a taktéž (značně) odlišné chování:

Instrukce Operandy Význam zkratky Chování Chování pro nulový vstup
BSF 16bit, 32bit, 64bit Bit Scan Forward index prvního bitu nastaveného na jedničku (hledání od nejnižšího bitu) ZF=1
BSR 16bit, 32bit, 64bit Bit Scan Reverse index prvního bitu nastaveného na jedničku (hledání od nejvyššího bitu) ZF=1
LZCNT 16bit, 32bit, 64bit Count Leading Zeros délka sekvence nulových bitů (hledání od nejvyššího bitu) CF=1
TZCNT 16bit, 32bit, 64bit Count Trailing Zeros délka sekvence nulových bitů (hledání od nejnižšího bitu) CF=1

Délka sekvence nulových bitů ovšem neodpovídá indexu prvního jedničkového bitu, a to ani při hledání od nejnižšího bitu.

Poznámka: u všech těchto instrukcí platí důležitá vlastnost – doba výpočtu není závislá na struktuře vstupních dat, tj. interně se NEprovádí postupné procházení jednotlivými bity (s testy na jejich hodnotu), ale využívají se komplikovanější a sofistikovanější mechanismy.

4. Základní použití instrukcí BSF a BSR

Vyzkoušejme si nyní, jaké výsledky získáme, pokud instrukcím BSF a BSR předáme bitovou hodnotu 0b01000010. Nezávisle na bitové šířce operandu (16 bitů, 32 bitů či 64 bitů) by měla instrukce BSF vrátit hodnotu 1, protože první jedničkový bit hledaný zprava (od nejnižšího bitu) má index roven jedné. Instrukce BSR vrátí hodnotu 6, protože první jedničkový bit hledaný zleva (od nejvyššího bitu) má index roven právě šesti.

Použití instrukce BSF:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        bsf     edx, eax             ; zjisteni indexu nejvyssiho nenuloveho bitu
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Výsledek:

00000001

Použití instrukce BSR:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        bsr     edx, eax             ; zjisteni indexu nejvyssiho nenuloveho bitu
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Výsledek:

00000006

5. Realizace výpočtů pozice nejvyššího a nejnižšího bitu s jedničkovou hodnotou pro vstupy 0 až 16

Předchozí demonstrační příklady si nepatrně upravíme, a to tak, že si necháme vypsat indexy bitů získaných instrukcemi BSF a BSR pro vstupní hodnoty 0 až 16, tj. binárně 0×00000000 až 0×00010000. Pro realizaci počítané smyčky použijeme pracovní registr ECX, který bude fungovat jako počitadlo a současně i vstup do instrukce BSF nebo BSR.

První z příkladů vypíše indexy nejnižšího jedničkového bitu:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
 
        xor     ecx, ecx             ; pocitadlo
 
repeat:
        bsf     edx, ecx             ; zjisteni indexu prvniho nenuloveho bitu
 
        push    ecx
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
        pop     ecx
        inc     ecx                  ; zvysit hodnotu pocitadla
        cmp     ecx, 17              ; test na konec pocitane smycky
        jne     repeat               ; for-loop
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Druhý z příkladů vypíše indexy nejvyššího jedničkového bitu:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
 
        xor     ecx, ecx             ; pocitadlo
 
repeat:
        bsr     edx, ecx             ; zjisteni indexu nejvyssiho nenuloveho bitu
 
        push    ecx
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
        pop     ecx
        inc     ecx                  ; zvysit hodnotu pocitadla
        cmp     ecx, 17              ; test na konec pocitane smycky
        jne     repeat               ; for-loop
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Výsledky obou příkladů (bez úprav):

00000000
00000000
00000001
00000000
00000002
00000000
00000001
00000000
00000003
00000000
00000001
00000000
00000002
00000000
00000001
00000000
00000004
00000000
00000000
00000001
00000001
00000002
00000002
00000002
00000002
00000003
00000003
00000003
00000003
00000003
00000003
00000003
00000003
00000004

Vysvětlení výsledků je patrné z následující tabulky:

Dec Bin BSF BSR
0 00000 0 0
1 00001 0 0
2 00010 1 1
3 00011 0 1
4 00100 2 2
5 00101 0 2
6 00110 1 2
7 00111 0 2
8 01000 3 3
9 01001 0 3
10 01010 1 3
11 01011 0 3
12 01100 2 3
13 01101 0 3
14 01110 1 3
15 01111 0 3
16 10000 4 4

6. Chování instrukcí BSF a BSR v případě, že je vstup nulový

V předchozím textu jsme si řekli, že instrukce BSF a BSR hledají ve vstupním bajtu či vícebajtovém slovu první bit, který je nastavený na jedničku. Ovšem v tomto případě je nutné myslet i na mezní případ, kdy vstupní data obsahují jen nulové bity. Mikroprocesor tento stav pochopitelně kontroluje a pokud je vstup nulový, nastaví se příznak ZF (Zero Flag) a hodnota uložená (nebo neuložená) do výsledného registru není definována. To tedy znamená, že v praxi je prakticky vždy nutné testovat i hodnotu příznakového bitu ZF, a to například následujícím způsobem:

        bsf     edx, ecx             ; zjisteni indexu prvniho nenuloveho bitu
 
        jnz     zf_reset             ; vse v poradku, byl nalezen index bitu
 
zf_set:             ; ZF je nastaven - vstupni operand byl nulovy
        ...
        ...
        ...
 
zf_reset:
        ...
        ...
        ...
Poznámka: díky existenci příznakových bitů tedy není nutné pracovat s „magickými chybovými konstantami“ typu –1 atd.

7. Upravené příklady pro výpočty pozice nejvyššího a nejnižšího bitu s jedničkovou hodnotou pro vstupy 0 až 16

Oba demonstrační příklady z páté kapitoly upravíme takovým způsobem, že kromě výsledků získaných instrukcemi BSF a BSR vypíšeme i stav příznakového bitu ZF, což se provede s využitím podmíněného skoku.

Varianta demonstračního příkladu s instrukcí BSF vypadá následovně:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
zf_set_message:
         db "ZF set   "
         zf_set_message_length equ $ - zf_set_message
 
zf_reset_message:
         db "ZF reset "
         zf_reset_message_length equ $ - zf_reset_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
 
        xor     ecx, ecx             ; pocitadlo
 
repeat:
        bsf     edx, ecx             ; zjisteni indexu prvniho nenuloveho bitu
        push    ecx
        push    edx
 
        jnz     zf_reset
 
zf_set:
        print_string   zf_set_message, zf_set_message_length
        jmp     continue
 
zf_reset:
        print_string   zf_reset_message, zf_reset_message_length
 
continue:
        pop     edx
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
        pop     ecx
        inc     ecx                  ; zvysit hodnotu pocitadla
        cmp     ecx, 17              ; test na konec pocitane smycky
        jne     repeat               ; for-loop
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Varianta demonstračního příkladu s instrukcí BSR vypadá následovně:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
zf_set_message:
         db "ZF set   "
         zf_set_message_length equ $ - zf_set_message
 
zf_reset_message:
         db "ZF reset "
         zf_reset_message_length equ $ - zf_reset_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
 
        xor     ecx, ecx             ; pocitadlo
 
repeat:
        bsr     edx, ecx             ; zjisteni indexu nejvyssiho nenuloveho bitu
        push    ecx
        push    edx
 
        jnz     zf_reset
 
zf_set:
        print_string   zf_set_message, zf_set_message_length
        jmp     continue
 
zf_reset:
        print_string   zf_reset_message, zf_reset_message_length
 
continue:
        pop     edx
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
        pop     ecx
        inc     ecx                  ; zvysit hodnotu pocitadla
        cmp     ecx, 17              ; test na konec pocitane smycky
        jne     repeat               ; for-loop
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

8. Vypočtené výsledky

Podívejme se nyní na vypočtené výsledky, které prozradí, za jakých okolností se nastavuje příznak ZF. První tabulka ukazuje výsledky získané instrukcí BSF. Podle očekávání je příznak ZF nastaven pouze v případě, že vstupem je nulová hodnota, přesněji řečeno hodnota, která má všechny své bity nulové a instrukce tedy nenašla žádný jedničkový bit:

ZF set   00000000
ZF reset 00000000
ZF reset 00000001
ZF reset 00000000
ZF reset 00000002
ZF reset 00000000
ZF reset 00000001
ZF reset 00000000
ZF reset 00000003
ZF reset 00000000
ZF reset 00000001
ZF reset 00000000
ZF reset 00000002
ZF reset 00000000
ZF reset 00000001
ZF reset 00000000
ZF reset 00000004

Instrukce BSR sice pro některé vstupy vrací odlišné indexy bitů (což je očekávané chování), ovšem příznak ZF nastavuje naprosto stejným způsobem, jako je tomu u instrukce BSF. To je ostatně patrné z následující tabulky:

ZF set   00000000
ZF reset 00000000
ZF reset 00000001
ZF reset 00000001
ZF reset 00000002
ZF reset 00000002
ZF reset 00000002
ZF reset 00000002
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000003
ZF reset 00000004

9. Zjištění počtu nulových bitů na začátku nebo konci testované hodnoty

Instrukce BSF a BSR jsou podporovány (z pohledu moderního IT) celou věčnost, protože byly přidány již do instrukční sady mikroprocesorů Intel 80386 (1985). V moderních mikroprocesorech s architekturou x86–64 jsou tyto instrukce stále podporovány, ovšem navíc mají vývojáři k dispozici dvě další instrukce nazvané LZCNT a TZCNT. Instrukce LZCNT vrací délku sekvence nulových bitů, přičemž tato sekvence musí začínat nejvyšším bitem. Instrukce TZCNT taktéž vrací délku sekvence nulových bitů, ovšem nyní tato sekvence začíná nejnižším bitem.

Do jistém míry tedy tedy instrukce nahrazují BSF a BSR, ovšem je nutné myslet na odlišné chování:

  • Jsou vráceny délky sekvencí nulových bitů a nikoli indexy nenulových bitů – tj.  hodnota bude shodná, ovšem jen pokud začínáme od nejnižšího bitu. A pokud naopak začínáme od bitu nejvyššího, je hodnota zcela odlišná (závisí na bitové šířce operandu).
  • Pokud je délka nalezené sekvence nulová (tj. první nalezený bit má hodnotu 1), nastavuje se příznak ZF.
  • Pokud odpovídá délka sekvence bitové šířce vstupu, nastavuje se příznakCF.
Poznámka: to vlastně znamená, že hodnota příznaku CF sémanticky nahradila příznak ZF použitý u instrukcí BSF a BSR.

10. Příklad použití instrukce LZCNT

Základní chování instrukce LZCNT si ověříme na demonstračním příkladu, ve kterém této instrukci opět předáme hodnotu 0×42, kterou je možné v binární podobě vyjádřit jako 0b01000010:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        lzcnt   edx, eax
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Po překladu a spuštění by se měla vypsat hodnota:

00000019

Proč tomu tak je? Ve skutečnosti pracujeme s 32bitovým registrem a vypsaná hodnota 0×19 decimálně odpovídá hodnotě 25. A pokud se podíváme na binární vyjádření vstupu, tak zjistíme, že plná 32bitová hodnota 0b00000000000000000000000001000010 má na začátku dvacet pět nul.

11. Příklad použití instrukce TZCNT

V dalším demonstračním příkladu naprogramovaném v assembleru zavoláme instrukci TZCNT:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        tzcnt   edx, eax
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Nyní se vypíše:

00000001

To taktéž odpovídá očekávání, protože (počítáno od nejnižšího bitu) končí analyzovaná 32bitová hodnota přesně jedním nulovým bitem.

12. LZCNT == REP BSR

Instrukce LZCNT do jisté míry odpovídá chování instrukce BSR, i když vrácené hodnoty a nastavované příznaky jsou odlišné. Při zavádění této (relativně nové) instrukce do instrukční sady byl proto zvolen malý trik – instrukční kód LZCNT je totožný s instrukčním kódem instrukce BSR, ovšem s přidaným prefixem REP, jenž u BSR vlastně nemá žádný význam (teoreticky by měl znamenat opakování instrukce se změnou ukazatelů/adres, což je ovšem definováno pouze pro instrukce MOVS, STOS, CMPS, LODS a SCAS). Instrukci tak bylo možné přidat, aniž by zabírala další (drahou) pozici v sadě dostupných operačních kódů.

Ostatně můžeme si otestovat, zda je to pravda, úpravou existujícího demonstračního příkladu (viz podtrženou část kódu):

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        rep bsr   edx, eax
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Výsledkem bude:

00000019

Totožné kódování je patrné i při pohledu na kontrolní výpisy generované assemblerem NASM při použití přepínače -l:

  0FBDD0       bsr     edx, eax         ; BSR bez prefixu
F30FBDD0       lzcnt   edx, eax         ; LZCNT má totožné kódování s přidaným prefixem REP
F30FBDD0       rep bsr   edx, eax       ; naprosto stejný instrukční kód

13. TZCNT == REP BSF

Prakticky stejnou vlastností „trpí“ i instrukce TZCNT, která je přeložena do stejné sekvence bajtů, jako instrukce REP BSF. Taktéž si to ověříme na upraveném demonstračním příkladu:

[bits 32]
 
%include "linux_macros.asm"
 
;-----------------------------------------------------------------------------
section .data
 
hex_message:
         times 8 db '?'
         db 0x0a
         hex_message_length equ $ - hex_message
 
;-----------------------------------------------------------------------------
section .bss
 
;-----------------------------------------------------------------------------
section .text
        global _start                ; tento symbol ma byt dostupny i linkeru
 
_start:
        mov     eax, 0x42            ; == 0b01000010
        rep bsf   edx, eax
 
        mov     ebx, hex_message     ; buffer, ktery se zaplni hexa cislicemi
        call    hex2string           ; zavolani prislusne subrutiny
        print_string   hex_message, hex_message_length    ; tisk hexadecimalni hodnoty
 
        exit                         ; ukonceni procesu
 
 
%include "hex2string.asm"

Po překladu a spuštění takto upraveného příkladu se korektně vypíše hodnota 1:

00000001

Porovnání operačních kódů instrukcí:

  0FBCD0       bsf     edx, eax         ; BSF bez prefixu
F30FBCD0       tzcnt   edx, eax         ; TZCNT má totožné kódování s přidaným prefixem REP
F30FBCD0       rep bsf   edx, eax       ; naprosto stejný instrukční kód

14. Volání vestavěné funkce __builtin_stdc_first_trailing_one

Nyní od assembleru přejdeme zpět k jazyku C, konkrétně k překladači GCC. Ukážeme si použití vestavěné funkce __builtin_stdc_first_trailing_one, která nalezne index prvního jedničkového bitu. Indexy jsou počítány od jedničky (nikoli od nuly) a nulová výsledná hodnota je vyhrazena pro ty případy, v nichž není jedničkový bit vůbec nalezen, tj. pro nulový vstup. Příklad navíc vytiskne i binární hodnotu, ve které jedničkový bit hledáme, aby se dala ověřit korektní funkcionalita:

#include <stdio.h>
#include <stdint.h>
 
uint8_t bsf_8bit(uint8_t x) {
    uint8_t z;
    z = __builtin_stdc_first_trailing_one(x);
    return z;
}
 
void print_bin(uint8_t x) {
    int i;
 
    for (i = 7; i >= 0; i--) {
        printf("%d", (x >> i) & 1);
    }
}
 
void test_bsf(uint8_t x) {
    printf("%u \t", x);
    print_bin(x);
    printf("\t%d\n", bsf_8bit(x));
}
 
int main(void) {
    uint8_t i;
 
    for (i=0; i<=17; i++) {
        test_bsf(i);
    }
 
    test_bsf(0xff);
}

Z vypsaných výsledků je patrné, že indexy skutečně začínají jedničkou a nula je vrácena pouze pro vstup 0×00:

0       00000000        0
1       00000001        1
2       00000010        2
3       00000011        1
4       00000100        3
5       00000101        1
6       00000110        2
7       00000111        1
8       00001000        4
9       00001001        1
10      00001010        2
11      00001011        1
12      00001100        3
13      00001101        1
14      00001110        2
15      00001111        1
16      00010000        5
17      00010001        1
255     11111111        1

15. Překlad funkce __builtin_stdc_first_trailing_one do strojového kódu

Dále nás bude zajímat, jakým způsobem se vlastně vestavěná funkce __builtin_stdc_first_trailing_one přeloží do strojového kódu mikroprocesorů s architekturou x86–84. Vyzkoušíme si chování pro různé typy argumentů (8, 16, 32 a 64 bitů):

#include <stdint.h>
 
uint8_t bsf_8bit(uint8_t x) {
    uint8_t z;
    z = __builtin_stdc_first_trailing_one(x);
    return z;
}
 
uint16_t bsf_16bit(uint16_t x) {
    uint16_t z;
    z = __builtin_stdc_first_trailing_one(x);
    return z;
}
 
uint32_t bsf_32bit(uint32_t x) {
    uint32_t z;
    z = __builtin_stdc_first_trailing_one(x);
    return z;
}
 
uint64_t bsf_64bit(uint64_t x) {
    uint64_t z;
    z = __builtin_stdc_first_trailing_one(x);
    return z;
}

16. Analýza přeloženého kódu

Podívejme se nyní na způsob překladu demonstračního příkladu uvedeného v předchozí kapitole do strojového kódu. Je patrné, že se ve všech čtyřech variantách využívá instrukce TZCNT, která získá délku sekvence nulových bitů (po které následuje bit jedničkový). Z tohoto důvodu je výsledek vypočtený instrukcí zvýšen o jedničku. Navíc se testuje situace, kdy je vstup nulový (tedy neexistují v něm jedničkové bity). V takovém případě se vrací nulová hodnota přečtená z registru EDX, který je vynulován operací xor (příznaky nelze použít, protože jsou ovlivněny instrukcí ADD).

Varianta s osmibitovým vstupem musí provést převod vstupu na 16 nebo 32 bitů, protože instrukce TZCNT nepodporuje osmibitový vstup:

bsf_8bit:
        movzx   edi, dil
        xor     eax, eax
        xor     edx, edx
        rep bsf eax, edi
        add     eax, 1
        test    edi, edi
        cmove   eax, edx
        ret

Šestnáctibitová varianta s převodem vstupu na 32 bitů:

bsf_16bit:
        movzx   edi, di
        xor     eax, eax
        xor     edx, edx
        rep bsf eax, edi
        add     eax, 1
        test    edi, edi
        cmove   eax, edx
        ret

Nejjednodušší je 32bitová varianta:

bsf_32bit:
        xor     eax, eax
        xor     edx, edx
        rep bsf eax, edi
        add     eax, 1
        test    edi, edi
        cmove   eax, edx
        ret

64bitová varianta musí opět provádět konverzi, tentokrát výsledku:

bsf_64bit:
        xor     eax, eax
        rep bsf rax, rdi
        add     eax, 1
        test    rdi, rdi
        cdqe
        cmove   rax, rdi
        ret

17. Překlad pro platformu AArch64

Na závěr se alespoň ve stručnosti podíváme na způsob překladu téhož demonstračního příkladu, nyní ovšem pro platformu AArch64, která má zcela odlišnou instrukční sadu, než x86–64. I postup je do značné míry odlišný, protože je k dispozici jen instrukce CLZ určená pro získání sekvence nulových bitů, přičemž se začíná od nejvyššího bitu. Před voláním této instrukce je tedy nutné obsah vstupu otočit (nejvyšší bit zaměnit s nejnižším bitem atd.). A pochopitelně se musí pracovat i se stavem, kdy je vstup nulový a musí se tedy vracet nula. Ukážeme si ho na příkladu 16bitového vstupu:

ands    w0, w0, 65535        ; maskování vstupu na 16 bitů
rbit    w1, w0               ; otočení všech bitů v registru
clz     w1, w1               ; spočítání sekvence nulových bitů, začíná se od nejvyššího bitu
csinc   w0, wzr, w1, eq      ; podmíněné přičtení jedničky v případě, že je vstup nulový
ret                          ; návrat ze subrutiny
bsf_8bit:
        ands    w0, w0, 255
        rbit    w1, w0
        clz     w1, w1
        csinc   w0, wzr, w1, eq
        ret
 
bsf_16bit:
        ands    w0, w0, 65535
        rbit    w1, w0
        clz     w1, w1
        csinc   w0, wzr, w1, eq
        ret
 
bsf_32bit:
        rbit    w1, w0
        cmp     w0, 0
        clz     w1, w1
        csinc   w0, wzr, w1, eq
        ret
 
bsf_64bit:
        rbit    x1, x0
        cmp     x0, 0
        clz     x1, x1
        csinc   x0, x0, x1, eq
        ret

18. Příloha: soubor Makefile pro překlad příkladů napsaných v assembleru NASM

Všechny výše uvedené demonstrační příklady naprogramované v assembleru NASM je možné přeložit s využitím souboru Makefile, jehož obsah je vypsán níže. Postačuje zadat příkaz make all:

ASM=nasm
LINKER=ld
 
execs := bsf1 bsr1 bsf2 bsr2 bsf3 bsr3 lzcnt1 lzcnt2 tzcnt1 tzcnt2
 
all:    $(execs)
 
clean:
        rm -f *.o $(execs)
 
 
.PHONY: all clean
 
%.o: %.asm linux_macros.asm hex2string.asm
        ${ASM} -f elf $< -o $@
 
bsf1:   bsf1.o
        ld -melf_i386 $< -o $@
 
bsr1:   bsr1.o
        ld -melf_i386 $< -o $@
 
bsf2:   bsf2.o
        ld -melf_i386 $< -o $@
 
bsr2:   bsr2.o
        ld -melf_i386 $< -o $@
 
bsf3:   bsf3.o
        ld -melf_i386 $< -o $@
 
bsr3:   bsr3.o
        ld -melf_i386 $< -o $@
 
lzcnt1: lzcnt1.o
        ld -melf_i386 $< -o $@
 
lzcnt2: lzcnt2.o
        ld -melf_i386 $< -o $@
 
tzcnt1: tzcnt1.o
        ld -melf_i386 $< -o $@
 
tzcnt2: tzcnt2.o
        ld -melf_i386 $< -o $@

19. Repositář s demonstračními příklady

kontrolní výpis výsledku překladu: instrukční kód instrukce BSF
# Příklad Stručný popis Adresa
1 bsf1.asm základní použití instrukce BSF pro jeden vstup https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsf1.asm
2 bsf1_lst.asm https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsf1_lst.asm
3 bsf2.asm základní použití instrukce BSF pro hodnoty 0 až 16 https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsf2.asm
4 bsf3.asm chování instrukce BSF v případě, že je vstup nulový https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsf3.asm
       
5 bsr1.asm základní použití instrukce BSR pro jeden vstup https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsr1.asm
6 bsr1_lst.asm kontrolní výpis výsledku překladu: instrukční kód instrukce BSR https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsr1_lst.asm
7 bsr2.asm základní použití instrukce BSR pro hodnoty 0 až 16 https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsr2.asm
8 bsr3.asm chování instrukce BSR v případě, že je vstup nulový https://github.com/tisnik/8bit-fame/blob/master/pc-linux/bsr3.asm
       
9 lzcnt1.asm základní použití instrukce LZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/lzcnt1.asm
10 lzcnt1_lst.asm kontrolní výpis výsledku překladu: instrukční kód instrukce LZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/lzcnt1_lst.asm
11 lzcnt2.asm alternativní zápis instrukce LZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/lzcnt2.asm
12 lzcnt2_lst.asm kontrolní výpis výsledku překladu: instrukční kód instrukce REP BSF https://github.com/tisnik/8bit-fame/blob/master/pc-linux/lzcnt2_lst.asm
       
13 tzcnt1.asm základní použití instrukce TZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/tzcnt1.asm
14 tzcnt1_lst.asm kontrolní výpis výsledku překladu: instrukční kód instrukce TZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/tzcnt1_lst.asm
15 tzcnt2.asm alternativní zápis instrukce TZCNT https://github.com/tisnik/8bit-fame/blob/master/pc-linux/tzcnt2.asm
16 tzcnt2_lst.asm kontrolní výpis výsledku překladu: instrukční kód instrukce REP BSR https://github.com/tisnik/8bit-fame/blob/master/pc-linux/tzcnt2_lst.asm
       
17 hex2string.asm pomocná makra pro převod hexadecimální hodnoty na řetězec https://github.com/tisnik/8bit-fame/blob/master/pc-linux/hex2string.asm
18 linux_macros.asm pomocná makra použitá v programech psaných pro Linux v assembleru https://github.com/tisnik/8bit-fame/blob/master/pc-linux/linux_macros.asm
       
19 Makefile Makefile určený pro překlad všech výše uvedených demonstračních příkladů https://github.com/tisnik/8bit-fame/blob/master/pc-linux/Makefile

Demonstrační příklady napsané v jazyku C, které jsou určené pro překlad s využitím překladače gcc, byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Jednotlivé demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

# Příklad Stručný popis Adresa
1 rotate_left.c volání vestavěné funkce v C (GCC) realizující rotaci doleva https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_left.c
2 rotate_left_x86_00.asm překlad volání funkce __builtin_stdc_rotate_left na platformě x86–64 bez aplikace optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_left_x86_00.asm
3 rotate_left_x86_09.asm překlad volání funkce __builtin_stdc_rotate_left na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_left_x86_09.asm
4 rotate_left_arm32.asm překlad volání funkce __builtin_stdc_rotate_left pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_left_arm32.asm
5 rotate_left_arm64.asm překlad volání funkce __builtin_stdc_rotate_left pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_left_arm64.asm
       
6 rotate_right.c volání vestavěné funkce v C (GCC) realizující rotaci doleva https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_right.c
7 rotate_right_x86_00.asm překlad volání funkce __builtin_stdc_rotate_right na platformě x86–64 bez aplikace optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_right_x86_00.asm
8 rotate_right_x86_09.asm překlad volání funkce __builtin_stdc_rotate_right na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_right_x86_09.asm
9 rotate_right_arm32.asm překlad volání funkce __builtin_stdc_rotate_right pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_right_arm32.asm
10 rotate_right_arm64.asm překlad volání funkce __builtin_stdc_rotate_right pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rotate_right_arm64.asm
       
11 parity_unsigned.c volání vestavěných funkcí __builtin_parity, __builtin_parityl a __builtin_parityll https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_unsigned.c
12 parity_unsigned_x86_00.asm překlad volání funkcí __builtin_parity* na platformě x86–64 bez aplikace optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_unsigned_x86_00.asm
13 parity_unsigned_x86_09.asm překlad volání funkcí __builtin_parity* na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_unsigned_x86_09.asm
14 parity_unsigned_arm32.asm překlad volání funkcí __builtin_parity* pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_unsigned_arm32.asm
15 parity_unsigned_arm64.asm překlad volání funkcí __builtin_parity* pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_unsigned_arm64.asm
       
16 parityg_unsigned.c volání vestavěné funkce __builtin_parityg https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_unsigned.c
17 parityg_unsigned_arm32.asm překlad volání funkce __builtin_parityg na platformě x86–64 bez aplikace optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_unsigned_arm32.asm
18 parityg_unsigned_arm64.asm překlad volání funkce __builtin_parityg na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_unsigned_arm64.asm
19 parityg_unsigned_x86_00.asm překlad volání funkce __builtin_parityg pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_unsigned_x86_00.asm
20 parityg_unsigned_x86_09.asm překlad volání funkce __builtin_parityg pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_unsigned_x86_09.asm
       
21 parity_signed.c výpočet parity celočíselných hodnot se znaménkem: realizace pomocí vestavěných funkcí __builtin_parity, __builtin_parityl a __builtin_parityll https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parity_signed.c
22 parityg_signed.c výpočet parity celočíselných hodnot se znaménkem: realizace pomocí vestavěné funkce __builtin_parityg https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/parityg_signed.c

Všechny demonstrační příklady z článku Funkce vestavěné v GCC pro provádění nízkoúrovňových aritmetických operací jsou vypsány v následující tabulce:

# Příklad Stručný popis Adresa
1 add_overflow.c volání vestavěné funkce __builtin_add_overflow s předáním operandů různých typů https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_overflow.c
2 add_overflow_x86_64_O0.asm překlad volání funkce __builtin_add_overflow na platformě x86–64 bez aplikace optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_overflow_x86_64_O0.asm
3 add_overflow_x86_64_Os.asm překlad volání funkce __builtin_add_overflow na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_overflow_x86_64_Os.asm
4 add_overflow_arm32.asm překlad volání funkce __builtin_add_overflow pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_overflow_arm32.asm
5 add_overflow_arm64.asm překlad volání funkce __builtin_add_overflow pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_overflow_arm64.asm
       
6 add_diff_types.c součet s využitím různých kombinací hodnot typu charint https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_diff_types.c
7 add_diff_types_x86_64.asm překlad volání funkce __builtin_add_overflow na platformě x86–64 s aplikací optimalizací https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_diff_types_x86_64.asm
8 add_diff_types_arm32.asm překlad volání funkce __builtin_add_overflow pro 32bitové ARMy https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_diff_types_arm32.asm
9 add_diff_types_arm64.asm překlad volání funkce __builtin_add_overflow pro 64bitové ARMy (AArch64) https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/add_diff_types_arm64.asm
       
10 sub_overflow.c operace rozdílu s využitím funkce __builtin_sub_overflow https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/sub_overflow.c
11 sub_overflow.asm překlad volání funkce __builtin_sub_overflow na platformě x86–64 https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/sub_overflow.asm
       
12 addc_subc.c operace součtu tří hodnot a operace rozdílu: s výpůjčkou nebo s přetečením https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/addc_subc.asm
# Příklad Stručný popis Adresa
1 rdrand_support.asm test, jestli je instrukce RDRAND mikroprocesorem podporována https://github.com/tisnik/8bit-fame/blob/master/pc-linux/rdrand_support.asm
2 rdrand_read.asm přečtení jedné 32bitové hodnoty instrukcí RDRAND https://github.com/tisnik/8bit-fame/blob/master/pc-linux/rdrand_read.asm
3 rdrand_read_loop.asm přečtení sekvence 32bitových hodnot instrukcí RDRAND https://github.com/tisnik/8bit-fame/blob/master/pc-linux/rdrand_read_loop.asm
       
4 rdrand_read.c přečtení náhodné 32bitové hodnoty, realizace s využitím vestavěné funkce GCC https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rdrand_read.c
5 rdrand_read.asm výsledek překladu předchozího zdrojového kódu do assembleru https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rdrand_read.asm
       
6 rand_gen.c vygenerování binárního souboru s pseudonáhodnými 32bitovými hodnotami https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rand_gen.c
7 rdrand_gen.c vygenerování binárního souboru s hodnotami vrácenými instrukcí RDRAND https://github.com/tisnik/8bit-fame/blob/master/gcc-builtins/rdrand_gen.c

20. Odkazy na Internetu

  1. Funkce vestavěné v GCC pro provádění nízkoúrovňových aritmetických operací
    https://www.root.cz/clanky/funkce-vestavene-v-gcc-pro-provadeni-nizkourovnovych-aritmetickych-operaci/
  2. Generátor náhodných čísel založený na instrukcích RDSEED a RDRAND
    https://www.root.cz/clanky/generator-nahodnych-cisel-zalozeny-na-instrukcich-rdseed-a-rdrand/
  3. Circular shift (Wikipedia)
    https://en.wikipedia.org/wi­ki/Bitwise_operation#Circu­lar_shift
  4. Parity bit (Wikipedia)
    https://en.wikipedia.org/wi­ki/Parity_bit
  5. Parity function (Wikipedia)
    https://en.wikipedia.org/wi­ki/Parity_function
  6. RDRAND (Wikipedia)
    https://en.wikipedia.org/wiki/RDRAND
  7. RDRAND instruction
    https://www.felixcloutier­.com/x86/rdrand
  8. Random Number Generator
    https://wiki.osdev.org/Ran­dom_Number_Generator
  9. GCC documentation: Extensions to the C Language Family
    https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions
  10. GCC documentation: Using Vector Instructions through Built-in Functions
    https://gcc.gnu.org/online­docs/gcc/Vector-Extensions.html
  11. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  12. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  13. CPU design (Wikipedia)
    http://en.wikipedia.org/wi­ki/CPU_design
  14. GCC Compiler Intrinsics
    https://iq.opengenus.org/gcc-compiler-intrinsics/
  15. Other Built-in Functions Provided by GCC
    https://gcc.gnu.org/online­docs/gcc/Other-Builtins.html
  16. GCC: 6.60 Built-in Functions Specific to Particular Target Machines
    https://gcc.gnu.org/online­docs/gcc/Target-Builtins.html#Target-Builtins
  17. Additional Builtins for Numeric Operations
    https://gcc.gnu.org/online­docs/gcc/Numeric-Builtins.html
  18. Bit Operation Builtins
    https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html
  19. Stránka projektu Compiler Explorer
    https://godbolt.org/
  20. The LLVM Compiler Infrastructure
    https://llvm.org/
  21. GCC, the GNU Compiler Collection
    https://gcc.gnu.org/
  22. Clang
    https://clang.llvm.org/
  23. Clang: Assembling a Complete Toolchain
    https://clang.llvm.org/doc­s/Toolchain.html
  24. Integer overflow
    https://en.wikipedia.org/wi­ki/Integer_overflow
  25. SETcc — Set Byte on Condition
    https://www.felixcloutier­.com/x86/setcc
  26. The ARMv8 instruction sets
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  27. A64 Instruction Set
    https://developer.arm.com/pro­ducts/architecture/instruc­tion-sets/a64-instruction-set
  28. Switching between the instruction sets
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  29. The A64 instruction set
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  30. Introduction to ARMv8 64-bit Architecture
    https://quequero.org/2014/04/in­troduction-to-arm-architecture/
  31. Undefined behavior (Wikipedia)
    https://en.wikipedia.org/wi­ki/Undefined_behavior
  32. Is signed integer overflow still undefined behavior in C++?
    https://stackoverflow.com/qu­estions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c
  33. Allowing signed integer overflows in C/C++
    https://stackoverflow.com/qu­estions/4240748/allowing-signed-integer-overflows-in-c-c
  34. SXTB, SXTH, SXTW
    https://www.scs.stanford.e­du/~zyedidia/arm64/sxtb_z_p_z­.html
  35. BX, BXNS
    https://developer.arm.com/do­cumentation/100076/0200/a32-t32-instruction-set-reference/a32-and-t32-instructions/bx–bxns?lang=en
  36. Carry and Borrow Principles
    https://www.tpub.com/neet­s/book13/53a.htm
  37. In binary subtraction, how do you handle a borrow when there are no bits left to borrow form
    https://stackoverflow.com/qu­estions/68629408/in-binary-subtraction-how-do-you-handle-a-borrow-when-there-are-no-bits-left-to
  38. Is there any legitimate use for Intel's RDRAND?
    https://stackoverflow.com/qu­estions/26771329/is-there-any-legitimate-use-for-intels-rdrand
  39. Intel® Digital Random Number Generator (DRNG) Software Implementation Guide
    https://www.intel.com/con­tent/www/us/en/developer/ar­ticles/guide/intel-digital-random-number-generator-drng-software-implementation-guide.html
  40. Hardware random number generator
    https://en.wikipedia.org/wi­ki/Hardware_random_number_ge­nerator
  41. Random number generator attack
    https://en.wikipedia.org/wi­ki/Random_number_generator_at­tack
  42. random_r.c (Glibc)
    https://github.com/lattera/glib­c/blob/master/stdlib/random_r­.c#L341
  43. Xorshift
    https://en.wikipedia.org/wi­ki/Xorshift
  44. x86 instruction listings
    https://en.wikipedia.org/wi­ki/X86_instruction_listin­gs
  45. Odd inline asm code generation with pointless memory operand
    https://github.com/llvm/llvm-project/issues/56789
  46. Bit scanning equivalencies
    https://fgiesen.wordpress­.com/2013/10/18/bit-scanning-equivalencies/
  47. BSF — Bit Scan Forward
    https://www.felixcloutier.com/x86/bsf
  48. BSR — Bit Scan Reverse
    https://www.felixcloutier.com/x86/bsr
  49. TZCNT — Count the Number of Trailing Zero Bits
    https://www.felixcloutier­.com/x86/tzcnt
  50. LZCNT — Count the Number of Leading Zero Bits
    https://www.felixcloutier­.com/x86/lzcnt
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.