Hlavní navigace

Programovací jazyk Go a assembler (2.část)

Dnes se opět budeme věnovat assembleru. Představíme si základní techniky použitelné jak na platformě x86–64, tak i na procesorech ARM (32bitové i 64bitové řady). Na tento základ navážeme popisem ručních optimalizací.
Pavel Tišnovský 30. 1. 2020
Doba čtení: 55 minut

Sdílet

11. Překlad funkcí pro součet svých operandů

12. Podpora výpočtů s typy float32 a float64 na mikroprocesorech ARM

13. Podmíněné a nepodmíněné skoky jako základ pro realizaci rozvětvení a programových smyček

14. Strojové instrukce určené pro provedení skoku na architekturách i386 a x86–64

15. Překlad funkce Sign pro architekturu x86–64

16. Příznakové a stavové bity na mikroprocesorech s architekturou ARM

17. Překlad funkce Sign pro 32bitovou architekturu ARM

18. AArch64: od podmínkových bitů k podmíněným skokům

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

20. Odkazy na Internetu

1. Architektury mikroprocesorů podporované překladačem a assemblerem programovacího jazyka Go

„There's something beautiful about programming in assembly.“

Na předchozí část seriálu o programovacím jazyku Go dnes navážeme, protože si popíšeme některé další možnosti, které nám nabízí kombinace (relativně) vysokoúrovňového jazyka Go s assemblerem, přesněji řečeno s assemblerem, který je nedílnou součástí standardní sady nástrojů Go (tento assembler je v mnoha ohledech dosti specifický a někdy též matoucí, což ostatně uvidíme v navazujících kapitolách).

Nejprve se zmiňme o architekturách mikroprocesorů, které jsou podporovány jak překladačem jazyka Go, tak i některými základními nástroji (assembler, objdump atd.). Zkrácené názvy těchto architektur, které se specifikují proměnnou prostředí GOARCH, jsou vypsány v navazující tabulce:

# $GOARCH Význam (vybraná architektura)
1 amd64 64bitové mikroprocesory x86–64 (samozřejmě nejenom AMD)
2 386 varianta s použitím instrukcí 387 nebo SSE2
     
3 arm 32bitový ARMv5, ARMv6 nebo ARMv7, bude blíže popsáno v deváté kapitole
4 arm64 ARMv8-A (neboli AArch64)
     
5 ppc64 POWER8 a vyšší (varianta big endian)
6 ppc64le POWER8 a vyšší (varianta little endian)
     
7 mips klasický MIPS (varianta big endian)
8 mipsle klasický MIPS (varianta little endian)
9 mips64 MIPS III a vyšší (varianta big endian)
10 mips64le MIPS III a vyšší (varianta little endian)
     
11 s390× z196 a vyšší
Poznámka: povšimněte si, že u některých architektur je nutné dále rozhodnout, jaká konkrétní varianta se má použít. Týká se to zejména architektury i386 (možná správněji i586) a taktéž 32bitové řady mikroprocesorů s architekturou ARM, která existuje ve více verzích.

Pro úplnost se ještě zmiňme o kombinaci operačního systému (specifikovaného v proměnné prostředí GOOS) a mikroprocesorové. Podporovány jsou pouze některé kombinace, protože mnoho dalších kombinací nedává smysl; typicky v případě procesorů PowerPC a MIPS:

$GOOS $GOARCH
aix ppc64
   
android 386
android amd64
android arm
android arm64
   
darwin 386
darwin amd64
darwin arm
darwin arm64
   
dragonfly amd64
   
freebsd 386
freebsd amd64
freebsd arm
   
illumos amd64
   
js wasm
   
linux 386
linux amd64
linux arm
linux arm64
linux ppc64
linux ppc64le
linux mips
linux mipsle
linux mips64
linux mips64le
linux s390×
   
netbsd 386
netbsd amd64
netbsd arm
   
openbsd 386
openbsd amd64
openbsd arm
openbsd arm64
   
plan9 386
plan9 amd64
plan9 arm
   
solaris amd64
   
windows 386
windows amd64
Poznámka: prozatím v Go chybí oficiální podpora RISC-V, to se však může v poměrně krátké době změnit, společně s širším nasazováním této architektury v praxi.

2. Předávání parametrů přes zásobník na architektuře x86–64

V dnešním článku se budeme zabývat mnoha tématy, které do větší či menší míry souvisí s použitím assembleru. První důležitou věcí je způsob předávání parametrů přes zásobník na architektuře x86–64. Překladač jazyka Go se v tomto případě nemusí příliš ohlížet na jazyk C, takže je možné zvolit jiné konvence. Podívejme se nejprve na způsob předávání celočíselných parametrů všech podporovaných bitových šířek (8, 16, 32 a 64 bitů). Vše si ověříme na čtveřici funkcí, z nichž každá provede součet tří operandů stejné bitové šířky:

package main
 
func Add8(x int8, y int8, z int8) int8 {
        return x + y + z
}
 
func Add16(x int16, y int16, z int16) int16 {
        return x + y + z
}
 
func Add32(x int32, y int32, z int32) int32 {
        return x + y + z
}
 
func Add64(x int64, y int64, z int64) int64 {
        return x + y + z
}
 
func main() {
        println(Add8(1, 2, 3))
        println(Add16(1, 2, 3))
        println(Add32(1, 2, 3))
        println(Add64(1, 2, 3))
}

Překlad je nutné provést s vypnutým inliningem (jinak by se volání funkce nahradilo jejím výsledkem) a taktéž s vypnutou a později zapnutou optimalizací (získáte tak dvě varianty kódu):

$ go build -gcflags '-N -l' asm04.go

Výsledná sekvence instrukcí pro jednotlivé varianty funkce Add vypadá následovně:

func Add8(x int8, y int8, z int8) int8 {
  0x44ea70              c644241000              MOVB $0x0, 0x10(SP)
  0x44ea75              0fb6442409              MOVZX 0x9(SP), AX
  0x44ea7a              0fb64c2408              MOVZX 0x8(SP), CX
  0x44ea7f              0fb654240a              MOVZX 0xa(SP), DX
  0x44ea84              01c8                    ADDL CX, AX
  0x44ea86              01d0                    ADDL DX, AX
  0x44ea88              88442410                MOVB AL, 0x10(SP)
  0x44ea8c              c3                      RET
Poznámka: povšimněte si, že prvních osm bajtů na zásobníku (resp. přesněji řečeno zásobníkovém rámci) zabírá návratová adresa, za níž následují jednotlivé operandy s offsety +8, +9 a +10. Výsledek je uložen na offsetu +16. Jinými slovy to znamená, že se v případě parametrů neprovádí zarovnání; výsledek je zarovnán na adresu dělitelnou osmi. Provádí se bezznaménkové rozšíření operandů, protože se stejně bere v úvahu jen nejnižších osm bitů výsledku.

Šestnáctibitové součty:

func Add16(x int16, y int16, z int16) int16 {
  0x44ea90              66c74424100000          MOVW $0x0, 0x10(SP)
  0x44ea97              0fb744240a              MOVZX 0xa(SP), AX
  0x44ea9c              0fb74c2408              MOVZX 0x8(SP), CX
  0x44eaa1              0fb754240c              MOVZX 0xc(SP), DX
  0x44eaa6              01c8                    ADDL CX, AX
  0x44eaa8              01d0                    ADDL DX, AX
  0x44eaaa              6689442410              MOVW AX, 0x10(SP)
  0x44eaaf              c3                      RET
Poznámka: i zde můžeme vidět stejný vzor: offsety parametrů na zásobníkovém rámci jsou +8, +10 a +12, ovšem výsledek je již zarovnán na adresu dělitelnou osmi.

32bitové součty:

func Add32(x int32, y int32, z int32) int32 {
  0x44eab0              c744241800000000        MOVL $0x0, 0x18(SP)
  0x44eab8              8b442408                MOVL 0x8(SP), AX
  0x44eabc              0344240c                ADDL 0xc(SP), AX
  0x44eac0              03442410                ADDL 0x10(SP), AX
  0x44eac4              89442418                MOVL AX, 0x18(SP)
  0x44eac8              c3                      RET
Poznámka: protože nyní mají parametry šířku čtyř bajtů, je offset posledního parametru +16 (0×10) a tudíž se musela návratová hodnota posunout na další adresu dělitelnou osmi: +0×18 neboli +24.

A konečně varianta 64bitová:

func Add64(x int64, y int64, z int64) int64 {
  0x44ead0              48c744242000000000      MOVQ $0x0, 0x20(SP)
  0x44ead9              488b442408              MOVQ 0x8(SP), AX
  0x44eade              4803442410              ADDQ 0x10(SP), AX
  0x44eae3              4803442418              ADDQ 0x18(SP), AX
  0x44eae8              4889442420              MOVQ AX, 0x20(SP)
  0x44eaed              c3                      RET

Zajímavé bude zjistit, jak se pracuje s návratovými hodnotami, kterých může být v programovacím jazyce Go větší množství (a u funkcí, které mohou skončit s chybou, se jedná o běžný idiom spočívající v použití více návratových hodnot). Studovat budeme funkci, která vrátí hodnoty svých operandů, ovšem v opačném pořadí:

func Swap(x int64, y int64) (int64, int64) {
        return y, x
}

Výsledkem by měl být následující strojový kód:

func Swap(x int64, y int64) (int64, int64) {
  0x44ea70              48c744241800000000      MOVQ $0x0, 0x18(SP)
  0x44ea79              48c744242000000000      MOVQ $0x0, 0x20(SP)
  0x44ea82              488b442410              MOVQ 0x10(SP), AX
  0x44ea87              4889442418              MOVQ AX, 0x18(SP)
  0x44ea8c              488b442408              MOVQ 0x8(SP), AX
  0x44ea91              4889442420              MOVQ AX, 0x20(SP)
  0x44ea96              c3                      RET

Struktura zásobníkového rámce je v tomto případě zhruba následující:

SP+offset Význam
+0 návratová adresa
+8 první parametr (64 bitů)
+16 druhý parametr (64 bitů)
+24 první návratová hodnota (64 bitů)
+32 druhá návratová hodnota (64 bitů)

3. Malá odbočka – jména instrukcí a velikost parametrů v assembleru jazyka Go

Vraťme se na chvíli k implementaci funkce Add8 a Add16. V osmibitové variantě této funkce jsme mohli vidět trojici instrukcí, které provádí načtení operandů ze zásobníku s jejich rozšířením (konverzí) na plnou bitovou šířku (zero extension). Jedná se o instrukci MOVZX:

  0x44ea75              0fb6442409              MOVZX 0x9(SP), AX
  0x44ea7a              0fb64c2408              MOVZX 0x8(SP), CX
  0x44ea7f              0fb654240a              MOVZX 0xa(SP), DX

Ovšem podobnou instrukci bylo možné zahlédnout i v šestnáctibitové variantě, což je už na první pohled podivné:

  0x44ea97              0fb744240a              MOVZX 0xa(SP), AX
  0x44ea9c              0fb74c2408              MOVZX 0x8(SP), CX
  0x44eaa1              0fb754240c              MOVZX 0xc(SP), DX

Při pozornějším pohledu je však možné zjistit, že kódy instrukcí jsou odlišné. Je tedy vhodnější použít „lepší“ disassembler než ten, který je nabízený přímo v základních nástrojích programovacího jazyka Go. Můžeme například instrukční kódy zkopírovat do webové aplikace dostupné na stránce https://defuse.ca/online-x86-assembler.htm#disassembly2 (do druhého textového pole disassemble) a vybrat architekturu x86–64.

Disassemblingem získáme následující instrukce, z nichž je jasně patrné, že se pokaždé zpracovávají odlišné operandy, i když disassembler jazyka Go tyto „maličkosti“ prozatím nerozlišuje:

0:  0f b6 44 24 09          movzx  eax,BYTE PTR [rsp+0x9]
5:  0f b6 4c 24 08          movzx  ecx,BYTE PTR [rsp+0x8]
a:  0f b6 4c 24 08          movzx  ecx,BYTE PTR [rsp+0x8]
 
f:  0f b7 44 24 0a          movzx  eax,WORD PTR [rsp+0xa]
14: 0f b7 4c 24 08          movzx  ecx,WORD PTR [rsp+0x8]
19: 0f b7 4c 24 08          movzx  ecx,WORD PTR [rsp+0x8]

4. Klasický matematický koprocesor i387

Nyní prozkoumáme způsob překladu této jednoduché funkce:

func AddFloat64(x float64, y float64, z float64) float64 {
        return x + y + z
}

V úvodní kapitole jsme se mj. zmínili o tom, že při výběru architektury i386 (tedy přesněji řečeno 32bitových mikroprocesorů řady i386 resp. i586) je možné zvolit, jakým způsobem se budou překládat instrukce pracující s datovými typy float32, float64, complex64 a complex128. Pro starší typy mikroprocesorů se mohou používat instrukce mikroprocesoru i387, pro novější typy pak instrukce SSE nebo SSE2. Pokud je z nějakého důvodu nutné podporovat mikroprocesory bez podpory SSE a SSE2 (což je již dnes poněkud raritní hardware), lze použít proměnnou prostředí pojmenovanou GO386 s podrobnějším nastavením používané architektury:

$ GOARCH=386 GO386=387 go build -gcflags '-N -l' asm06.go

Neoptimalizovaná varianta funkce pro součet tří hodnot typu float64 je skutečně velmi dlouhá, i když první čtyři instrukce slouží pro práci se zásobníkem:

func AddFloat64(x float64, y float64, z float64) float64 {
  0x808dde0             658b0d00000000          MOVL GS:0, CX
  0x808dde7             8b89fcffffff            MOVL 0xfffffffc(CX), CX
  0x808dded             3b6108                  CMPL 0x8(CX), SP
  0x808ddf0             762f                    JBE 0x808de21
  0x808ddf2             dd0568f50a08            FLD $f64.0000000000000000(SB)
  0x808ddf8             d9c0                    FLD F0
  0x808ddfa             dd5c241c                FSTP 0x1c(SP)
  0x808ddfe             dd44240c                FLD 0xc(SP)
  0x808de02             ddd9                    FSTP F1
  0x808de04             dd442404                FLD 0x4(SP)
  0x808de08             dd442414                FLD 0x14(SP)
  0x808de0c             d9c2                    FLD F2
  0x808de0e             dec2                    FADDP F0, F2
  0x808de10             d9c0                    FLD F0
  0x808de12             dec2                    FADDP F0, F2
  0x808de14             d9c1                    FLD F1
  0x808de16             dd5c241c                FSTP 0x1c(SP)
  0x808de1a             ddd8                    FSTP F0
  0x808de1c             ddd8                    FSTP F0
  0x808de1e             ddd8                    FSTP F0
  0x808de20             c3                      RET

Vyzkoušíme si ještě přeložit variantu s optimalizacemi:

$ GOARCH=386 GO386=387 go build -gcflags '-l' asm06.go

Výsledek je sice kratší, ale stále má k dobrému kódu velmi daleko (celý výpočet lze totiž ve skutečnosti provést jen pěti instrukcemi mikroprocesoru):

func AddFloat64(x float64, y float64, z float64) float64 {
  0x808ddd0             658b0d00000000          MOVL GS:0, CX
  0x808ddd7             8b89fcffffff            MOVL 0xfffffffc(CX), CX
  0x808dddd             3b6108                  CMPL 0x8(CX), SP
  0x808dde0             7621                    JBE 0x808de03
  0x808dde2             dd44240c                FLD 0xc(SP)
  0x808dde6             dd442404                FLD 0x4(SP)
  0x808ddea             d9c0                    FLD F0
  0x808ddec             dec2                    FADDP F0, F2
  0x808ddee             dd442414                FLD 0x14(SP)
  0x808ddf2             ddd9                    FSTP F1
  0x808ddf4             d9c1                    FLD F1
  0x808ddf6             dec1                    FADDP F0, F1
  0x808ddf8             d9c0                    FLD F0
  0x808ddfa             dd5c241c                FSTP 0x1c(SP)
  0x808ddfe             ddd8                    FSTP F0
  0x808de00             ddd8                    FSTP F0
  0x808de02             c3                      RET
Poznámka: z nějakého důvodu je výsledek překladu pro i387 prakticky vždy velmi špatný, takže volbu GO386=387 používejte skutečně jen tehdy, pokud je to nezbytné.

5. Technologie SSE/SSE2 a její využití překladačem programovacího jazyka Go

U procesorů řady x86 se prakticky kontinuálně provádí rozšiřování instrukční sady. Po úspěšném a relativně bezproblémovém zavedení rozšíření MMX3DNow! do praxe není divu, že obě nejvýznamnější společnosti podnikající v oblasti návrhu a prodeje mikroprocesorů patřících do rodiny x86, tj. firmy Intel a AMD, začaly pro tyto typy mikroprocesorů navrhovat i další rozšiřující instrukční sady s „vektorovými“ instrukcemi typu SIMD. V následující tabulce jsou tyto rozšiřující instrukční sady vypsány, včetně roku vzniku dané technologie i informace o tom, v jakém mikroprocesoru byla ta která technologie zpočátku využita:

Název technologie Společnost Rok uvedení Poprvé použito v čipu
MMX Intel 1996 Intel Pentium P5
3DNow! AMD 1998 AMD K6–2
SSE Intel 1999 Intel Pentium III (mikroarchitektura P6)
SSE2 Intel 2001 Intel Pentium 4 (mikroarchitektura NetBurst)
SSE3 Intel 2004 Intel Pentium 4 (Prescott)
SSSE3 Intel 2006 mikroarchitektura Intel Core
SSE4 Intel+AMD 2006 AMD K10 (SSE4a) , mikroarchitektura Intel Core
XOP AMD 2011? založeno na SSE5
CVT16 AMD 2011? založeno na SSE5
AVX Intel+AMD 2013? rozšíření SSE registrů na 256 bitů, celkem 32 registrů

Obrázek 1: Intel Xeon 5600 je zástupcem mikroprocesorů určených pro oblast serverů. Samozřejmě taktéž podporuje SIMD operace: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2

Překladač i assembler jazyka Go podporuje prakticky všechny instrukce SSE i SSE2. Další způsoby a ruční (!) optimalizace budou popsány příště.

6. Nové registry SSE

Nejprve se zaměřme na registry využívané v technologii SSE. U mikroprocesorů implementujících instrukční sadu SSE je využita nová sada registrů pojmenovaných XMM0XMM7. Na 64bitové platformě (architektura AMD 64) navíc došlo k přidání dalších osmi registrů se jmény XMM8XMM15 využitelných pouze v 64bitovém režimu. Všechny nové registry mají šířku 128 bitů, tj. jsou dvakrát širší, než registry používané v MMX3DNow! a čtyřikrát širší, než běžné pracovní registry na platformě x86 (bavíme se o 32bitovém režimu). Do každého registru je možné uložit čtveřici reálných numerických hodnot reprezentovaných v systému plovoucí řádové tečky podle normy IEEE 754, přičemž tato norma je dodržována přesněji, než v případě 3DNow! (různé zaokrouhlovací režimy či práce s denormalizovanými čísly sice mohou vypadat trošku jako černá magie, ovšem například v knihovnách pro numerické výpočty, které musí vždy za specifikovaných okolností dát stejný výsledek, se jedná o velmi důležitou vlastnost). K osmi či šestnácti novým registrům XMM* byl ještě přidán jeden 32bitový registr nazvaný MXCSR, jenž byl určený pro nastavení (řízení) režimů výpočtu.

Obrázek 2: Sada nových pracovních registrů přidaných v rámci SSE.

7. Příklad využití SSE

Zajímavá je podpora jak skalárních operací, tak i operací vektorových v instrukční sadě SSE. Příkladem může být například skalární instrukce součtu ADDSS (SS=single scalar), která může mít dvojí podobu:

ADDSS xmm1, xmm2     ; instrukce pracující s dvojicí registrů SSE
ADDSS xmm1, mem32    ; instrukce pracující s registrem SSE a paměťovým místem (32 bitů)

Vraťme se nyní k dvojici funkcí pro součet trojice hodnot s plovoucí řádovou čárkou:

func AddFloat32(x float32, y float32, z float32) float32 {
        return x + y + z
}
 
func AddFloat64(x float64, y float64, z float64) float64 {
        return x + y + z
}

Překlad založený na instrukcích SSE (ovšem nutno podotknout instrukcích „nevektorových“) je již oproti variantě určené pro i387 mnohem čitelnější, kratší a ve výsledku i rychlejší:

func AddFloat32(x float32, y float32, z float32) float32 {
  0x44ea70              0f57c0                  XORPS X0, X0
  0x44ea73              f30f11442418            MOVSS X0, 0x18(SP)
  0x44ea79              f30f10442408            MOVSS 0x8(SP), X0
  0x44ea7f              f30f5844240c            ADDSS 0xc(SP), X0
  0x44ea85              f30f58442410            ADDSS 0x10(SP), X0
  0x44ea8b              f30f11442418            MOVSS X0, 0x18(SP)
  0x44ea91              c3                      RET
 
func AddFloat64(x float64, y float64, z float64) float64 {
  0x44eaa0              0f57c0                  XORPS X0, X0
  0x44eaa3              f20f11442420            MOVSD_XMM X0, 0x20(SP)
  0x44eaa9              f20f10442408            MOVSD_XMM 0x8(SP), X0
  0x44eaaf              f20f58442410            ADDSD 0x10(SP), X0
  0x44eab5              f20f58442418            ADDSD 0x18(SP), X0
  0x44eabb              f20f11442420            MOVSD_XMM X0, 0x20(SP)
  0x44eac1              c3                      RET

Obě varianty, jak pro typ float32/float, tak i pro typ float64/double jsou sémanticky totožné. Odlišují se pochopitelně offsety parametrů ukládaných na zásobníkový rámec a taktéž tím, že v 64bitové variantě se používají funkce _XMM. Zajímavé taktéž je, že si zde překladač vystačil s jediným 32bitovým popř. 64bitovým registrem X0.

8. Práce s poli a řezy (slices)

Do funkcí je pochopitelně možné předávat i další parametry, například (což je zcela typické) řezy. Příklad použití řezů může vypadat následovně:

func Sum(values []int) int {
        sum := 0
        for _, v := range values {
                sum += v
        }
        return sum
}

Připomeňme si, že řez je tvořen trojicí – ukazatel, délka a kapacita řezu. Používá se takto:

        for _, v := range values {
  0x44ea70              488b442408              MOVQ 0x8(SP), AX
  0x44ea75              488b4c2410              MOVQ 0x10(SP), CX
  0x44ea7a              31d2                    XORL DX, DX
  0x44ea7c              31db                    XORL BX, BX
  0x44ea7e              eb0a                    JMP 0x44ea8a
  0x44ea80              488b34d0                MOVQ 0(AX)(DX*8), SI
  0x44ea84              48ffc2                  INCQ DX
                sum += v
  0x44ea87              4801f3                  ADDQ SI, BX
        for _, v := range values {
  0x44ea8a              4839ca                  CMPQ CX, DX
  0x44ea8d              7cf1                    JL 0x44ea80
        return sum
  0x44ea8f              48895c2420              MOVQ BX, 0x20(SP)
  0x44ea94              c3                      RET
Poznámka: práce s řezy je tak důležitá, že se jimi budeme podrobněji zabývat příště.

9. Podpora 32bitových mikroprocesorů ARM

Ve druhé části dnešního článku se ve stručnosti seznámíme s podporou 32bitových mikroprocesorů ARM překladačem a assemblerem programovacího jazyka Go. V následující tabulce jsou vypsány kombinace proměnných prostředí GOARCH a GOARM:

Architektura Stav GOARM GOARCH
ARMv4 a nižší není podporováno n/a n/a
ARMv5 podporováno GOARM=5 GOARCH=arm
ARMv6 podporováno GOARM=6 GOARCH=arm
ARMv7 podporováno GOARM=7 GOARCH=arm
ARMv8 podporováno n/a GOARCH=arm64

Z tabulky je patrné, že rozhodnutí mezi 32bitovými ARMy a 64bitovou architekturou AArch64 je provedeno na základě proměnné prostředí GOARCH. Obsahem proměnné GOARM se specifikuje verze 32bitových mikroprocesorů a mikrořadičů ARM.

Poznámka: existuje i alternativní projekt nazvaný tinygo, který podporuje další typy mikroprocesorů a především mikrořadičů postavených nad různými jádry ARM. Tímto projektem se budeme zabývat v samostatném článku.

10. Specifika architektury ARM

V klasické architektuře ARM se používá 16 pracovních registrů, každý o šířce 32bitů, přičemž poslední tři registry mívají speciální význam: ukazatel na vrchol zásobníku, link register (návratová adresa z procedury) a programový čítač. Kvůli konstantní šířce všech instrukcí může být problematické uložení konstanty či adresy do některého pracovního registru. Problém je to logický a vlastně shodný pro všechny „klasické“ RISCové mikroprocesory: šířka pracovních registrů je 32 bitů a současně je šířka instrukcí taktéž 32 bitů, tudíž není možné, aby se v instrukci vedle operačního kódu nacházela i 32 bitová konstanta. Tvůrci dalších RISCových mikroprocesorů se s touto problematikou snažili vypořádat různým způsobem, například zavedli speciální instrukci pro naplnění horních šestnácti bitů registru, zatímco pro naplnění spodních šestnácti bitů bylo možné použít například instrukci ADD s konstantou a nulovým registrem R0 (zhruba takovýmto způsobem je tato problematika řešena na mikroprocesorech MIPS). U mikroprocesorů ARM se zdá, že jeho konstruktéři nechtěli „obětovat“ další tranzistory na podobné typy instrukcí, takže se pro načtení konstanty používá dvojice instrukcí se stejným formátem, jako mají ostatní aritmetické a logické instrukce:

# Instrukce Význam
1 MOV načtení osmibitové konstanty 0..255
2 MVN načtení osmibitové konstanty s negací –1..-256

To je samozřejmě pro mnoho účelů zcela nedostatečné, ovšem ve skutečnosti je možné tuto konstantu pomocí barrel shifteru posunout o sudý počet míst 0, 2, 4, .. 30, takže se ve skutečnosti celkový počet konstant zvyšuje na hodnotu 8192 z celkového množství kombinací 232. Aby programátoři mohli relativně snadno načíst libovolnou konstantu do zvoleného registru, nabízí většina assemblerů pro mikroprocesory ARM pseudoinstrukci LDR ve tvaru:

LDR Rx, =konstanta

Podle hodnoty použité konstanty se tato instrukce buď převede na instrukci MOV, alternativně MVN, nebo na instrukci LDR načítající konstantu uloženou někde v programovém kódu (například za tělem subrutiny, kde lze vyhradit prostor pomocí direktivy LTORG). Tato konstanta je potom adresována relativně k hodnotě registru PC, pouze je nutné dát pozor na to, že offset pro relativní adresování má pouze dvanáct bitů, takže tato konstanta nemůže být uložena příliš „daleko“ (na to ostatně upozorní assembler).

11. Překlad funkcí pro součet svých operandů

Otestujme si tedy způsob překladu funkcí pro součet svých parametrů do strojového kódu 32bitových mikroprocesorů ARM:

package main
 
func Add8(x int8, y int8, z int8) int8 {
        return x + y + z
}
 
func Add16(x int16, y int16, z int16) int16 {
        return x + y + z
}
 
func Add32(x int32, y int32, z int32) int32 {
        return x + y + z
}
 
func Add64(x int64, y int64, z int64) int64 {
        return x + y + z
}

Překlad bude proveden tímto příkazem:

$ GOARCH=arm GOARM=5 go build -gcflags '-l' asm04.go

Disasembling zajistí příkaz:

$ go tool objdump -S -s main.Add asm04

V osmibitové variantě můžeme vidět, že operandy leží na offsetech +4, +5 a +6, protože návratová adresa má šířku jen čtyři bajty. O načtení bajtu s jeho rozšířením na celých 32bitů se stará instrukce MOVBS:

TEXT main.Add8(SB) /home/tester/go-root/article_54/asm04.go
func Add8(x int8, y int8, z int8) int8 {
  0x5e794               e1dd00d5                MOVBS 0x5(R13), R0
  0x5e798               e1dd10d4                MOVBS 0x4(R13), R1
  0x5e79c               e0800001                ADD R1, R0, R0
  0x5e7a0               e1dd10d6                MOVBS 0x6(R13), R1
  0x5e7a4               e0810000                ADD R0, R1, R0
  0x5e7a8               e5cd0008                MOVB R0, 0x8(R13)
  0x5e7ac               e28ef000                ADD $0, R14, R15
Poznámka: poslední instrukce je obdobou instrukce RET, protože se zde přenáší hodnota z registru R14 (link register) do registru R15 (program counter).

Šestnáctibitová varianta používá offsety parametrů +4, +6 a +8 a znaménkové rozšíření ze šestnácti bitů na plných 32bitů se stará instrukce MOVHS:

TEXT main.Add16(SB) /home/tester/go-root/article_54/asm04.go
func Add16(x int16, y int16, z int16) int16 {
  0x5e7b0               e1dd00f6                MOVHS 0x6(R13), R0
  0x5e7b4               e1dd10f4                MOVHS 0x4(R13), R1
  0x5e7b8               e0800001                ADD R1, R0, R0
  0x5e7bc               e1dd10f8                MOVHS 0x8(R13), R1
  0x5e7c0               e0810000                ADD R0, R1, R0
  0x5e7c4               e1cd00bc                MOVH R0, 0xc(R13)
  0x5e7c8               e28ef000                ADD $0, R14, R15

Následuje 32bitová varianta, v níž se (podle očekávání) neprovádí znaménkové rozšíření parametrů, neboť všechny výpočty probíhají přímo v 32bitovém režimu, tedy nativním režimu těchto mikroprocesorů:

TEXT main.Add32(SB) /home/tester/go-root/article_54/asm04.go
func Add32(x int32, y int32, z int32) int32 {
  0x5e7cc               e59d0008                MOVW 0x8(R13), R0
  0x5e7d0               e59d1004                MOVW 0x4(R13), R1
  0x5e7d4               e0800001                ADD R1, R0, R0
  0x5e7d8               e59d100c                MOVW 0xc(R13), R1
  0x5e7dc               e0810000                ADD R0, R1, R0
  0x5e7e0               e58d0010                MOVW R0, 0x10(R13)
  0x5e7e4               e28ef000                ADD $0, R14, R15

A konečně se podívejme na variantu 64bitovou. Ta je (na rozdíl od architektury x86–64) složitější, z toho prostého důvodů, že 64bitový součet je nutné na 32bitové architektuře provádět postupně – od nižší poloviny operandů k vyšší polovině (zde je nutné brát v potaz i přenos uložený do příznaku carry):

TEXT main.Add64(SB) /home/tester/go-root/article_54/asm04.go
func Add64(x int64, y int64, z int64) int64 {
  0x5e7e8               e59d0004                MOVW 0x4(R13), R0
  0x5e7ec               e59d100c                MOVW 0xc(R13), R1
  0x5e7f0               e0902001                ADD.S R1, R0, R2
  0x5e7f4               e59d3014                MOVW 0x14(R13), R3
  0x5e7f8               e0924003                ADD.S R3, R2, R4
  0x5e7fc               e58d401c                MOVW R4, 0x1c(R13)
  0x5e800               e0900001                ADD.S R1, R0, R0
  0x5e804               e59d0008                MOVW 0x8(R13), R0
  0x5e808               e59d1010                MOVW 0x10(R13), R1
  0x5e80c               e0a00001                ADC R1, R0, R0
  0x5e810               e0921003                ADD.S R3, R2, R1
  0x5e814               e59d1018                MOVW 0x18(R13), R1
  0x5e818               e0a10000                ADC R0, R1, R0
  0x5e81c               e58d0020                MOVW R0, 0x20(R13)
  0x5e820               e28ef000                ADD $0, R14, R15
Poznámka: varianta instrukce ADD.S nastavuje příznakové bity.

Výše uvedenou sekvenci instrukcí bude vhodné okomentovat:

MOVW     x.low, R0
MOVW     y.low, R1
ADD.S    R1, R0, R2       // R2=mezisoučet x.low + y.low
MOVW     z.low, R3
ADD.S    R3, R2, R4       // R4=mezisoučet x.low + y.low + z.low
MOVW     R4, result.low   // uložení spodních 32bitů výsledku
 
ADD.S    R1, R0, R0       // nastavení příznaků při mezisoučtu x.low + y.low
MOVW     x.high, R0
MOVW     y.high, R1
ADC      R1, R0, R0       // R0=mezisoučet x.high + y.high + přetečení z x.low + y.low
ADD.S    R3, R2, R1       // nastavení příznaků při mezisoučtu všech nižších slov
MOVW     z.high, R1
ADC      R0, R1, R0       // přičtení z.high k mezivýsledku + carry
MOVW     R0, result.high  // uložení horních 32bitů výsledku

12. Podpora výpočtů s typy float32 a float64 na mikroprocesorech ARM

Některé čipy ARM nemají matematický koprocesor. V takovém případě se výpočty s typy float32 a float64 provádí softwarově („armel“). Čipy, které mají matematický koprocesor, se někdy označují „armhf“ (typicky ARMv6 a vyšší). Podívejme se na překlad funkcí AddFloat32 a AddFloat64 pro ty čipy ARM, které matematický koprocesor mají:

$ GOARCH=arm GOARM=6 go build -gcflags '-l' asm06.go

Získání vygenerované sekvence instrukcí:

$ go tool objdump -S -s main.Add asm06
TEXT main.AddFloat32(SB) /home/tester/go-root/article_54/asm06.go
        return x + y + z
  0x5b854               ed9d0a02                MOVF 0x8(R13), F0
  0x5b858               ed9d1a01                MOVF 0x4(R13), F1
  0x5b85c               ee300a01                ADDF F1, F0, F0
  0x5b860               ed9d1a03                MOVF 0xc(R13), F1
  0x5b864               ee310a00                ADDF F0, F1, F0
  0x5b868               ed8d0a04                MOVF F0, 0x10(R13)
  0x5b86c               e28ef000                ADD $0, R14, R15
 
TEXT main.AddFloat64(SB) /home/tester/go-root/article_54/asm06.go
        return x + y + z
  0x5b870               ed9d0b03                MOVD 0xc(R13), F0
  0x5b874               ed9d1b01                MOVD 0x4(R13), F1
  0x5b878               ee300b01                ADDD F1, F0, F0
  0x5b87c               ed9d1b05                MOVD 0x14(R13), F1
  0x5b880               ee310b00                ADDD F0, F1, F0
  0x5b884               ed8d0b07                MOVD F0, 0x1c(R13)
  0x5b888               e28ef000                ADD $0, R14, R15

Překlad je v tomto případě přímočarý – používají se FPU registry F0 a F1, instrukce mají koncovku „F“ pro typ float32 a „D“ pro typ float64.

13. Podmíněné a nepodmíněné skoky jako základ pro realizaci rozvětvení a programových smyček

Velmi důležitým typem strojových instrukcí, které v různé podobě najdeme prakticky u všech modelů mikroprocesorů (resp. přesněji řečeno u mikroprocesorů všech dnes rozšířených mikroprocesorových architektur), jsou instrukce provádějící skoky na nějakou adresu v operační paměti. Implementace skoku není, alespoň na první pohled a u jednodušších architektur bez instrukční pipeline, vlastně nijak složitá, protože se v případě použití absolutní adresy dosadí hodnota z operačního kódu instrukce do registru PC a v případě použití relativní adresy se tato hodnota (nazývaná někdy poněkud nepřesně offset) přičte k aktuální hodnotě registru PC. Relativní adresa je v tomto případě v kódu instrukce uložena se znaménkem, proto se skok může provést dozadu i dopředu (ostatně právě použití relativní adresy uvidíme v dále popisovaných demonstračních příkladech).

Skoky většinou dělíme podle jednoho kritéria (formy zápisu adresy) na absolutní a relativní a podle kritéria druhého (za jakým okolností se skok provede) na skoky podmíněné a nepodmíněné. V závislosti na použité instrukční sadě jsou možné různé kombinace, typicky však u většiny mikroprocesorů nalezneme kombinace nepodmíněný absolutní skok, nepodmíněný relativní skok a podmíněný relativní skok. Skoky nepodmíněné jsou jednodušší a svou podstatou odpovídají příkazu goto známého z některých programovacích jazyků (včetně Go!) a také z mnoha článků, ve kterých autoři mnohdy bez hlubšího zamyšlení se nad původní myšlenkou opakují, že by se goto nemělo při strukturovaném programování používat :-). V assembleru se však skoky vesele používají, neboť právě pomocí nich se vytváří základní konstrukce strukturovaného programování – podmínky a programové smyčky.

Využití podmínek a podmíněných skoků budeme testovat na následujícím kódu – implementaci funkce typu Sign/Sgn:

package main
 
func Sign(value int) int {
        if value < 0 {
                return -1
        } else if value > 0 {
                return 1
        } else {
                return 0
        }

}
 
func main() {
        println(Sign(-100))
        println(Sign(100))
        println(Sign(0))
}

14. Strojové instrukce určené pro provedení skoku na architekturách i386 a x86_64

U architektury mikroprocesorů 32bitové řady i386 a taktéž 64bitové řady x86–64 je základní strojovou instrukcí určenou pro provedení nepodmíněného skoku instrukce nazvaná jednoduše a přímočaře JMP (což je, jak jste zajisté zjistili, mnemotechnická zkratka slova jump). V assembleru většinou za mnemotechnickou zkratkou jména instrukce následuje návěští (label), z něhož assembler odvodí reálnou adresu.

Alternativně je možné použít i další způsoby adresování, čímž se například implementuje tabulka skoků (jedna z možných realizací stavového automatu) atd., ovšem tyto techniky pro účely dnešního článku prozatím nepotřebujeme znát. Mnohem zajímavější jsou podmíněné skoky, které se při programování v assembleru či ve strojovém kódu používají pro implementaci programových smyček while, do-while, for a taktéž konstrukcí typu if-then-else atd. Podmíněný skok je proveden či naopak neproveden na základě nějaké podmínky. Vzhledem k tomu, že pracujeme na té nejnižší programové úrovni, tj. na úrovni strojových instrukcí, není samozřejmě možné podmínku definovat nějakým složitým a sofistikovaným způsobem – musí se jednat o operaci, kterou mikroprocesor dokáže jednoduše a především dostatečně rychle zpracovat (i přesto představují skoky úzké místo v programech).

Z tohoto prostého důvodu – podmínky musí být realizovány dostatečně jednoduchým způsobem pro snadnou implementaci na čipu – jsou na mikroprocesorových architekturách i386 a x86–64 podmínky založeny na testování jednoho z takzvaných příznakových bitů, negací těchto bitů či dokonce jejich kombinací. Pokud z důvodu zjednodušení výkladu celé relativně rozsáhlé problematiky budeme ignorovat některé speciálnější příznaky a především pak rozdíly mezi hodnotami bez znaménka (unsigned) a se znaménkem (signed), můžeme zpočátku použít především příznaky nazvané Carry flag, Sign flag a Zero flag, tj. příznak přenosu, příznak záporného výsledku a příznak nulovosti. Význam těchto příznakových bitů se shrnut v následující tabulce:

Příznak Význam zkratky Poznámka
ZF zero flag výsledek předchozí operace je nulový
CF carry flag přenos (bezznaménková aritmetika)
SF sign flag výsledek je záporný (nastaven nejvyšší bit bajtu či slova)

Strojové instrukce určené pro provedení podmíněných skoků jsou ve své základní variantě (existují pro ně totiž i jmenné aliasy zmíněné níže) pojmenovány jednoduše a přímočaře – začínají písmenem J (jump), za nímž následuje volitelné písmeno N (negace) a jednoznaková zkratka příznaku. Instrukce JNC tedy znamená „proveď skok, pokud příznak Carry není nastaven“, zatímco instrukce JZ znamená „proveď skok pouze při nastavení příznaku Zero“:

Mnemotechnická zkratka instrukce Význam instrukce podmíněného skoku
JC podmíněný skok za předpokladu, že je nastaven příznak přenosu (Carry flag)
JNC podmíněný skok za předpokladu, že je vynulován příznak přenosu (Carry flag)
JZ podmíněný skok za předpokladu, že je nastaven příznak nulovosti (Zero flag)
JNZ podmíněný skok za předpokladu, že je vynulován příznak nulovosti (Zero flag)
JS podmíněný skok za předpokladu, že je nastaven příznak záporného výsledku (Sign flag)
JNS podmíněný skok za předpokladu, že je vynulován příznak záporného výsledku (Sign flag)

Jmenné aliasy podporované většinou assemblerů:

Instrukce Alias
JZ JE
JNZ JNE
JC JB, JNAE
JNC JNB, JAE
JS nemá alias
JNS nemá alias

15. Překlad funkce Sign pro architekturu x86–64

Podívejme se nyní na způsob překladu funkce Sign pro 64bitovou architekturu x86–64. Překlad se provede příkazem:

$ go build -gcflags '-l' asm08.go

Výpis strojových instrukcí pro funkci Sign příkazem:

$ go tool objdump -S -s main.Sign asm08

Výsledek:

TEXT main.Sign(SB) /home/tester/go-root/article_54/asm08.go
        if value < 0 {
  0x44ea70              488b442408              MOVQ 0x8(SP), AX
  0x44ea75              4885c0                  TESTQ AX, AX
  0x44ea78              7c16                    JL 0x44ea90
        } else if value > 0 {
  0x44ea7a              7e0a                    JLE 0x44ea86
                return 1
  0x44ea7c              48c744241001000000      MOVQ $0x1, 0x10(SP)
  0x44ea85              c3                      RET
                return 0
  0x44ea86              48c744241000000000      MOVQ $0x0, 0x10(SP)
  0x44ea8f              c3                      RET
                return -1
  0x44ea90              48c7442410ffffffff      MOVQ $-0x1, 0x10(SP)
  0x44ea99              c3                      RET

Můžeme zde vidět kombinaci testu (nastavuje příznakové bity) a podmíněného skoku:

TESTQ AX, AX
JL ...

a:

TESTQ AX, AX
JLE ...

16. Příznakové a stavové bity na mikroprocesorech s architekturou ARM

Nyní se již můžeme zabývat populární architekturou ARM. Kromě patnácti 32bitových pracovních registrů a programového čítače obsahují mikroprocesory s touto architekturou i registry, v nichž se uchovávají různé příznaky. V uživatelském režimu se pracuje s příznaky uloženými v registru nazvaném CPSR (Current Program Status Register) a pro každý další režim existuje navíc zvláštní registr nazvaný SPSR (Saved Program Status Register), v němž jsou uchovány původní příznaky ze CPSR. Podobně jako všechny pracovní registry, mají i registry CPSR a SPSR shodnou šířku 32 bitů, což má svoje výhody. Mimo jiné i to, že šířka 32 bitů ponechala konstruktérům procesorů ARM mnoho prostoru pro uložení různých důležitých informací do registrů CPSR/SPSR, takže se nemuseli uchylovat k nepříliš promyšleným technikám známým například z platformy x86, kde se původně šestnáctibitový registr FLAGS (8086) postupně změnil na 32bitový registr EFLAGS (80386), vedle něho vznikl registr MSW (80286) rozšířený na CR0 atd.

Ve výše zmíněných stavových registrech CPSR/SPSR mikroprocesorů ARM jsou uloženy především příznakové bity nastavované aritmeticko-logickou jednotkou při provádění základních aritmetických instrukcí či bitových operací, dále pak bity určující, jakou instrukční sadu mikroprocesor v daný okamžik zpracovává (ARM, Thumb, Jazelle), příznak pořadí zpracovávání bajtů (little/big endian) a taktéž příznaky používané u SIMD operací. Zdaleka ne všechny mikroprocesory ARM však skutečně pracují se všemi bity, což je logické, protože například příznak Q je používán jen u mikroprocesorů podporujících aritmetiku se saturací, příznak J u čipů s podporou technologie Jazelle atd. Pojďme si tedy jednotlivé příznakové i stavové bity vypsat. Povšimněte si, že především první čtyři bity mají prakticky shodný název i stejný význam, jako je tomu u již popsané architektury i386 a x86–64 (rozdíl je jen v pojmenování příznaku sign flag a negative flag, význam je však shodný):

Příznak Význam zkratky Poznámka
N negative výsledek ALU operace je záporný
V overflow přetečení (znaménková aritmetika, signed)
Z zero výsledek je nulový
C carry přenos (bezznaménková aritmetika, unsigned)
Q sticky overflow aritmetika se saturací, od ARMv5e výše
     
I interrupt zákaz IRQ (přerušení)
F fast interrupt zákaz FIRQ (rychlého přerušení)
     
T thumb příznak zpracování instrukční sady Thumb (jen u procesorů se znakem „T“ v názvu)
J jazelle příznak zpracování instrukční sady Jazelle (jen u procesorů se znakem „J“ v názvu)
E endianness pořadí bajtů při práci s RAM (big/little endian)
     
GE 4 bity použito u SIMD operací (pouze některé čipy)
IF 5 bitů použito u instrukcí Thumb2 (pouze některé čipy)
M 5 bitů režim práce mikroprocesoru (user, IRQ, FIRQ, …)
Poznámka: v tabulce zobrazené výše nejsou jednotlivé bity uvedeny v takovém pořadí, v jakém se nachází ve stavovém registru; sdruženy jsou podle své funkce.

U klasické RISCové instrukční sady ARM se v nejvyšších čtyřech bitech každé instrukce nachází takzvaný kód podmínky. Konstruktéři těchto mikroprocesorů totiž (alespoň částečně) vyřešili problematiku podmíněných skoků tím, že umožnili vykonat každou instrukci pouze v tom případě, že je splněna podmínka, jejíž kód je zapsán právě v oněch čtyřech nejvyšších bitech instrukce. A o jakou problematiku podmíněných skoků se vlastně jedná? Podmíněné skoky představují pro klasickou RISCovou pipeline obtížný úkol: důvodem existence instrukční pipeline je to, aby se v každém taktu v ideálním případě dokončila jedna instrukce. U skoků, zvláště těch podmíněných, se však již před rozhodnutím, zda se skok provede či nikoli, začnou zpracovávat další instrukce umístěné za skokem, což však znamená, že se v případě provedení skoku tyto instrukce ve skutečnosti nemají vykonat. Konstruktéři RISCových a posléze i CISCových mikroprocesorů tedy hledali různé způsoby řešení této problematiky, ať se již jedná o spekulativní provádění instrukcí (příliš mnoho tranzistorů) či o prediktory skoků (ne vždy jsou úspěšné).

Díky tomu, že u mikroprocesorů ARM lze podmínku vykonání zadat u každé instrukce, je možné, aby se celkový počet podmíněných skoků v programu minimalizoval. Zejména se to týká skoků používaných pro implementaci programové konstrukce if-then-else, kde se v jednotlivých větvích nachází jen malé množství instrukcí. Aby však mělo použití podmínkových kódů smysl, musela se změnit ještě jedna vlastnost procesorů ARM: jejich aritmeticko-logická jednotka totiž změní stavové bity carry, zero, overflow a negative pouze v tom případě, že je to explicitně v instrukčním kódu zapsáno (výjimku tvoří porovnávací instrukce). Touto vlastností se budeme zabývat až v následujícím textu.

První sada podmínkových kódů se používá pro provedení či naopak neprovedení instrukce na základě hodnoty jednoho z příznakových bitů zero, overflow či negative. Poslední podmínkový kód z této skupiny má název AL (Any/Always) a značí, že se instrukce provede v každém případě. Tento podmínkový kód se tudíž většinou v assembleru nezapisuje, protože je (celkem pochopitelně) považován za implicitní:

Kód Přípona Význam Testovaná podmínka
0000 EQ Z set rovnost (či nulový výsledek)
0001 NE Z clear nerovnost (či nenulový výsledek)
0100 MI N set výsledek je záporný
0101 PL N clear výsledek je kladný či 0
0110 VS V set nastalo přetečení
0111 VC V clear nenastalo přetečení
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ů carry a zero, přesněji řečeno kombinací těchto bitů:

Kód Přípona Význam Testovaná podmínka
0010 CS/HS C set >=
0011 CC/LO C clear <
1000 HI C set and Z clear >
1001 LS C clear or Z set <=

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 zero testují kombinace bitů negative, overflow a zero:

Kód Přípona Význam Testovaná podmínka
1010 GE N and V the same >=
1011 LT N and V differ <
1100 GT Z clear, N == V >
1101 LE Z set, N != V <=

Mezi základní aritmetické instrukce patří samozřejmě instrukce součtu a rozdílu. U instrukcí rozdílu je zajímavé, že existují ve dvou variantách podle toho, zda se odečítá první operand od druhého nebo naopak. Motivace je zřejmá – pro oba operandy existují odlišná pravidla:

# Instrukce Význam
1 ADD operand1+operand2
2 ADC operand1+operand2+carry
3 SUB operand1-operand2
4 SBC operand1-operand2+carry-1
5 RSB operand2-operand1
6 RSC operand2-operand1+carry-1

Tyto instrukce navíc ještě ve svém slově obsahují takzvaný S-bit určující, zda má instrukce nastavit příznaky ALU (N, V, Z, C) na základě výsledku operace. Jediné instrukce, u nichž je tento bit nastaven stále, jsou instrukce provádějící porovnání bez uložení výsledku operace (popsané ihned v následujícím odstavci):

# Instrukce Význam
1 ADDS operand1+operand2 a současně nastavení příznakových bitů
2 ADCS operand1+operand2+carry a současně nastavení příznakových bitů
3 SUBS operand1-operand2 a současně nastavení příznakových bitů
4 SBCS operand1-operand2+carry-1 a současně nastavení příznakových bitů
5 RSBS operand2-operand1 a současně nastavení příznakových bitů
6 RSCS operand2-operand1+carry-1 a současně nastavení příznakových bitů

Další skupinou instrukcí jsou instrukce provádějící nějakou aritmetickou či logickou operaci. Ovšem výsledek této operace se nikam neuloží, pouze se nastaví příznakové bity (navíc se tyto bity nastaví vždy, není zde možnost volby bitu S):

# Instrukce Význam
1 CMP operand1-operand2
2 CMN operand1+operand2 (compare negative)
3 TST operand1 and operand2
4 TEQ operand1 xor operand2

17. Překlad funkce Sign pro 32bitovou architekturu ARM

Zkusme si tedy naši funkci Sign přeložit pro 32bitovou architekturu ARM. Postup je prakticky stejný s postupem naznačeným v předchozích kapitolách:

$ GOARCH=arm GOARM=5 go build -gcflags '-l' asm08.go
$ go tool objdump -S -s main.Sign asm08

Výsledek do značné míry odpovídá kódu, který jsme viděli u architektury x86–64 – kombinace instrukce CMP a podmíněného skoku B.LT resp. B.LE:

TEXT main.Sign(SB) /home/tester/go-root/article_54/asm08.go
        if value < 0 {
  0x5e794               e59d0004                MOVW 0x4(R13), R0
  0x5e798               e3500000                CMP $0, R0
  0x5e79c               ba000006                B.LT 0x5e7bc
        } else if value > 0 {
  0x5e7a0               da000002                B.LE 0x5e7b0
                return 1
  0x5e7a4               e3a00001                MOVW $1, R0
  0x5e7a8               e58d0008                MOVW R0, 0x8(R13)
  0x5e7ac               e28ef000                ADD $0, R14, R15
                return 0
  0x5e7b0               e3a00000                MOVW $0, R0
  0x5e7b4               e58d0008                MOVW R0, 0x8(R13)
  0x5e7b8               e28ef000                ADD $0, R14, R15
                return -1
  0x5e7bc               e3e00000                MVN $0, R0
  0x5e7c0               e58d0008                MOVW R0, 0x8(R13)
  0x5e7c4               e28ef000                ADD $0, R14, R15
Poznámka: povšimněte si instrukce MVN pro získání hodnoty –1.

18. AArch64: od podmínkových bitů k podmíněným skokům

Procesory s architekturou AArch64 sice používají shodné podmínkové bity, ty jsou ovšem použity jen v několika instrukcích. Příznak přetečení je, podobně jako u mnoha dalších typů procesorů, používán při aritmetických operacích a testy podmínkových bitů lze provádět především u podmíněných skoků, tj. u instrukcí, jejichž mnemotechnická zkratka začíná znakem „B“ od slova „Branch“. Rozeznáváme následující typy nepodmíněných podmíněných skoků:

# Instrukce Alternativní zápis
1 B BAL
2 B.EQ BEQ
3 B.NE BNE
4 B.MI BMI
5 B.PL BPL
6 B.VS BVS
7 B.VC BVC
8 B.CS BCS
9 B.CC BCC
10 B.HI BHI
11 B.LS BLS
12 B.GE BGE
13 B.LT BLT
14 B.GT BGT
15 B.LE BLE
Poznámka: alternativní zápis je podporován například GNU Assemblerem.

Nastavení příznakových bitů a podmíněné skoky si otestujeme na demonstračním příkladu, v němž je implementována počítaná programová smyčka, ve které se naplňuje řetězec (resp. přesněji řečeno předem zvolená oblast paměti) znakem „*“. První varianta tohoto příkladu vypadá na architektuře AArch64 takto: používá se sekvence tří instrukcí určených pro snížení hodnoty počitadla smyčky o jedničku, testu, zda již počitadlo dosáhlo nuly a podmíněného skoku provedeného za předpokladu, že se nuly nedosáhlo:

loop:
        strb  w3, [x1]               // zapis znaku do bufferu
        add   x1, x1, #1             // uprava ukazatele do bufferu
        sub   x2, x2, #1             // zmenseni pocitadla
        cmp   x2, #0                 // otestovani, zda jsme jiz nedosahli nuly
        bne   loop                   // pokud jsme se nedostali k nule, skok na zacatek smycky

Vraťme se nyní k funkci Sign, kterou přeložíme pro AArch64 takto:

Hackinbg

$ GOARCH=arm64 go build -gcflags '-l' asm08.go
$ go tool objdump -S -s main.Sign asm08

Výsledek:

TEXT main.Sign(SB) /home/tester/go-root/article_54/asm08.go
        if value < 0 {
  0x58f30               f94007e0                MOVD 8(RSP), R0
  0x58f34               b7f80100                TBNZ $63, R0, 8(PC)
  0x58f38               eb1f001f                CMP ZR, R0
        } else if value > 0 {
  0x58f3c               5400008d                BLE 4(PC)
                return 1
  0x58f40               b24003e0                ORR $1, ZR, R0
  0x58f44               f9000be0                MOVD R0, 16(RSP)
  0x58f48               d65f03c0                RET
                return 0
  0x58f4c               f9000bff                MOVD ZR, 16(RSP)
  0x58f50               d65f03c0                RET
                return -1
  0x58f54               92800000                MOVD $-1, R0
  0x58f58               f9000be0                MOVD R0, 16(RSP)
  0x58f5c               d65f03c0                RET
Poznámka: ZR je registr obsahující konstantní nulu, trikem s instrukcí ORR tedy získáme konstantu 1, 0 atd.

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

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně pět až šest megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 asm04.go funkce pro součet tří operandů https://github.com/tisnik/go-root/blob/master/article54/asm04.go
2 asm05.go funkce vracející své parametry v opačném pořadí https://github.com/tisnik/go-root/blob/master/article54/asm05.go
3 asm06.go funkce pro součet tří operandů (typ float32 a float64) https://github.com/tisnik/go-root/blob/master/article54/asm06.go
4 asm07.go funkce pro součet položek v řezu https://github.com/tisnik/go-root/blob/master/article54/asm07.go
5 asm08.go funkce typu Sing/Sgn https://github.com/tisnik/go-root/blob/master/article54/asm08.go

20. Odkazy na Internetu

  1. Online x86 / x64 Assembler and Disassembler
    https://defuse.ca/online-x86-assembler.htm#disassembly2
  2. The Design of the Go Assembler
    https://talks.golang.org/2016/as­m.slide#1
  3. A Quick Guide to Go's Assembler
    https://golang.org/doc/asm
  4. AssemblyPolicy
    https://github.com/golang/go/wi­ki/AssemblyPolicy
  5. Geohash in Golang Assembly
    https://mmcloughlin.com/posts/geohash-assembly
  6. Command objdump
    https://golang.org/cmd/objdump/
  7. Assembly
    https://goroutines.com/asm
  8. Go & Assembly
    http://www.doxsey.net/blog/go-and-assembly
  9. A Foray Into Go Assembly Programming
    https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/
  10. Golang Capturing log.Println And fmt.Println Output
    https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4
  11. Stránka projektu plotly
    https://plot.ly/
  12. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  13. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  14. Michael Fogleman's projects
    https://www.michaelfogleman­.com/projects/tagged/grap­hics/
  15. Color Graphs of Complex Functions
    https://web.archive.org/web/20120511021419/htt­p://w.american.edu/cas/mat­hstat/lcrone/ComplexPlot.html
  16. A Gallery of Complex Functions
    http://wismuth.com/complex/ga­llery.html
  17. package glot
    https://godoc.org/github.com/A­rafatk/glot
  18. Gnuplotting: Output terminals
    http://www.gnuplotting.org/output-terminals/
  19. Introducing Glot the plotting library for Golang
    https://medium.com/@Arafat­./introducing-glot-the-plotting-library-for-golang-3133399948a1
  20. Introducing Glot the plotting library for Golang
    https://blog.gopheracademy.com/advent-2018/introducing-glot/
  21. Glot is a plotting library for Golang built on top of gnuplot
    https://github.com/Arafatk/glot
  22. Example plots (gonum/plot)
    https://github.com/gonum/plot/wi­ki/Example-plots
  23. A repository for plotting and visualizing data (gonum/plot)
    https://github.com/gonum/plot
  24. golang library to make https://chartjs.org/ plots
    https://github.com/brentp/go-chartjs
  25. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  26. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  27. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  28. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  29. gonum
    https://github.com/gonum
  30. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  31. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  32. Gonum Numerical Packages
    https://www.gonum.org/
  33. Stránky projektu MinIO
    https://min.io/
  34. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  35. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  36. MinIO Python Client API Reference
    https://docs.min.io/docs/python-client-api-reference.html
  37. Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
    https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/
  38. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  39. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  40. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  41. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  42. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  43. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  44. Ginkgo
    http://onsi.github.io/ginkgo/
  45. Gomega
    https://onsi.github.io/gomega/
  46. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  47. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  48. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  49. Balíček goexpect
    https://github.com/google/goexpect
  50. Balíček go-expect
    https://github.com/Netflix/go-expect
  51. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  52. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  53. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  54. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  55. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  56. goblin na GitHubu
    https://github.com/franela/goblin
  57. Mocha framework
    https://mochajs.org/
  58. frisby na GitHubu
    https://github.com/verdverm/frisby
  59. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  60. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  61. Cucumber for golang
    https://github.com/DATA-DOG/godog
  62. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  63. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  64. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  65. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  66. xml.org
    http://www.xml.org/
  67. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  68. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  69. JSON to YAML
    https://www.json2yaml.com/
  70. Data Format Converter
    https://toolkit.site/format.html
  71. Viper na GitHubu
    https://github.com/spf13/viper
  72. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  73. The fantastic ORM library for Golang
    http://gorm.io/
  74. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  75. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  76. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  77. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  78. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  79. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  80. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  81. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  82. Sémantické verzování
    https://semver.org/
  83. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  84. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  85. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  86. Modules
    https://github.com/golang/go/wi­ki/Modules
  87. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  88. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  89. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  90. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  91. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  92. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  93. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  94. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  95. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  96. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  97. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  98. gopls
    https://github.com/golang/go/wi­ki/gopls
  99. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  100. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  101. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  102. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  103. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  104. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  105. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  106. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  107. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  108. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  109. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  110. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  111. Package trace
    https://golang.org/pkg/runtime/trace/
  112. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  113. Command trace
    https://golang.org/cmd/trace/
  114. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  115. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  116. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  117. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  118. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  119. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  120. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  121. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  122. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  123. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  124. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  125. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  126. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  127. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  128. The LLDB Debugger
    http://lldb.llvm.org/
  129. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  130. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  131. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  132. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  133. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  134. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  135. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  136. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  137. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  138. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  139. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  140. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  141. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  142. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  143. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  144. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  145. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  146. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  147. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  148. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  149. go-cron
    https://github.com/rk/go-cron
  150. gocron
    https://github.com/jasonlvhit/gocron
  151. clockwork
    https://github.com/whiteShtef/cloc­kwork
  152. clockwerk
    https://github.com/onatm/clockwerk
  153. JobRunner
    https://github.com/bamzi/jobrunner
  154. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  155. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  156. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  157. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  158. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  159. go-prompt
    https://github.com/c-bata/go-prompt
  160. readline
    https://github.com/chzyer/readline
  161. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  162. go-readline
    https://github.com/fiorix/go-readline
  163. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  164. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  165. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  166. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  167. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  168. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  169. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  170. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  171. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  172. Editline Library (libedit)
    http://thrysoee.dk/editline/
  173. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  174. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  175. WinEditLine
    http://mingweditline.sourceforge.net/
  176. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  177. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  178. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  179. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  180. history(3) – Linux man page
    https://linux.die.net/man/3/history
  181. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  182. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  183. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  184. Balíček ogletest
    https://github.com/jacobsa/ogletest
  185. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  186. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  187. package testing
    https://golang.org/pkg/testing/
  188. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  189. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  190. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  191. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  192. GoConvey
    http://goconvey.co/
  193. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  194. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  195. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  196. package gg
    https://godoc.org/github.com/fo­gleman/gg
  197. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  198. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  199. The Go image package
    https://blog.golang.org/go-image-package
  200. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  201. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  202. YAML
    https://yaml.org/
  203. edn
    https://github.com/edn-format/edn
  204. Smile
    https://github.com/FasterXML/smile-format-specification
  205. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  206. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  207. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  208. Introducing JSON
    http://json.org/
  209. Package json
    https://golang.org/pkg/encoding/json/
  210. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  211. Go by Example: JSON
    https://gobyexample.com/json
  212. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  213. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  214. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  215. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  216. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  217. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  218. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  219. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  220. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  221. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  222. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  223. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  224. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  225. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  226. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  227. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  228. Algorithms to Go
    https://yourbasic.org/
  229. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  230. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  231. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  232. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  233. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  234. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  235. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  236. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  237. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  238. The Go Programming Language (home page)
    https://golang.org/
  239. GoDoc
    https://godoc.org/
  240. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  241. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  242. The Go Programming Language Specification
    https://golang.org/ref/spec
  243. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  244. Package builtin
    https://golang.org/pkg/builtin/
  245. Package fmt
    https://golang.org/pkg/fmt/
  246. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  247. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  248. Learning Go
    https://www.miek.nl/go/
  249. Go Bootcamp
    http://www.golangbootcamp.com/
  250. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  251. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  252. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  253. The Go Blog
    https://blog.golang.org/
  254. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  255. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  256. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  257. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  258. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  259. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  260. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  261. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  262. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  263. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  264. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  265. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  266. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  267. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  268. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  269. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  270. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  271. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  272. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  273. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  274. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  275. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  276. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  277. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  278. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  279. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  280. Go vs. Python
    https://www.peterbe.com/plog/govspy
  281. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  282. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  283. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  284. Go by Example: Slices
    https://gobyexample.com/slices
  285. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  286. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  287. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  288. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  289. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  290. nils In Go
    https://go101.org/article/nil.html
  291. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  292. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  293. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  294. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  295. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  296. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  297. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  298. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  299. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  300. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  301. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  302. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  303. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  304. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  305. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  306. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  307. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  308. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  309. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  310. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  311. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  312. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  313. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  314. Selectors
    https://golang.org/ref/spec#Selectors
  315. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  316. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  317. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  318. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  319. Part 21: Goroutines
    https://golangbot.com/goroutines/
  320. Part 22: Channels
    https://golangbot.com/channels/
  321. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  322. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  323. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  324. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  325. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  326. Control Structures
    https://www.golang-book.com/books/intro/5
  327. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  328. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  329. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  330. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  331. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  332. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  333. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  334. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  335. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  336. Effective Go
    https://golang.org/doc/ef­fective_go.html
  337. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  338. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  339. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  340. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  341. Nils in Go
    https://www.doxsey.net/blog/nils-in-go