Obsah
1. Architektury mikroprocesorů podporované překladačem a assemblerem programovacího jazyka Go
2. Předávání parametrů přes zásobník na architektuře x86–64
3. Malá odbočka – jména instrukcí a velikost parametrů v assembleru jazyka Go
4. Klasický matematický koprocesor i387
5. Technologie SSE/SSE2 a její využití překladačem programovacího jazyka Go
8. Práce s poli a řezy (slices)
9. Podpora 32bitových mikroprocesorů ARM
10. Specifika architektury ARM
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
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šší |
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 |
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
Š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
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
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
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í MMX i 3DNow! 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 XMM0 až XMM7. Na 64bitové platformě (architektura AMD 64) navíc došlo k přidání dalších osmi registrů se jmény XMM8 až XMM15 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 MMX i 3DNow! 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
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.
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
Š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
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, …) |
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
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 |
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:
$ 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
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
- Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - 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/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - 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 - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 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/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 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 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - 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/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - 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 - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - 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/ - 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 - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - 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 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - 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/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go