Obsah
1. Programovací jazyk Go a assembler
2. Vznik jazyka „assembly language“ a nástroje nazvaného assembler
3. Assemblery na domácích osmibitových mikropočítačích i na počítačích s procesory Motorola 68000
5. Assembler a programovací jazyk Go
6. Kód v assembleru generovaný překladačem jazyka Go
7. Nástroj objdump pro programovací jazyk Go
8. Inlining funkcí při optimalizacích prováděných překladačem jazyka Go
9. Překlad jednoduché funkce se zákazem inliningu
10. Funkce Add přeložená pro jiné architektury
11. Kostra aplikace, v níž je jedna funkce vytvořená v assembleru
12. Vytvoření funkce v assembleru, překlad programu
13. Zjednodušení práce se zásobníkem při čtení operandů funkce
15. Pojmenování výstupní hodnoty funkce s jejím využitím v assembleru
16. Kód v assembleru určený pro další procesorové architektury
17. Seznam dnes použitých instrukcí
18. Obsah následující části seriálu
19. Repositář s demonstračními příklady
1. Programovací jazyk Go a assembler
V dnešní části seriálu o programovacím jazyku Go se budeme zabývat tématem, jehož znalost v praxi využijí s velkou pravděpodobností pouze někteří vývojáři. Na druhou stranu se ovšem jedná o téma, které odhaluje činnost dalších pomocných nástrojů, které jsou nedílnou součástí ekosystému tohoto programovacího jazyka. Zabývat se totiž budeme použitím assembleru v kontextu programovacího jazyka Go. V Go se používá assembler založený na nástrojích pocházejících původně z operačního systému Plan-9, jehož některé vlastnosti jsou odlišné od klasického GNU Assembleru (GAS) či od projektu Netwide Assembler (NASM). Současně se ovšem jedná o assembler, který je univerzální napříč architekturami (pochopitelně až na odlišný instrukční kód a sadu pracovních registrů, což jsou věci, jimiž se jednotlivé procesorové architektury od sebe odlišují).
S assemblerem se v Go setkáme na několika místech, například:
- Bootstraping
- Implementace některých matematických funkcí ze standardní knihovny
- Víceslovní aritmetika a s ní související operace
- Implementace krypto funkcí (ty mnohdy používají specializované instrukce), opět použito i ve standardní knihovně
- Synchronizační primitiva (zámky atd.)
- Kód, který lze převést na vektorové instrukce (audio a video processing atd.)
- A pochopitelně i další části kódu, jehož sémantiku nelze přesně vyjádřit vysokoúrovňovým jazykem (práce s příznaky, různé operace typu prokládání bitů, vyhledávání v bitovém vzorku atd.)
2. Vznik jazyka „assembly language“ a nástroje nazvaného assembler
Assemblery za sebou mají velmi dlouhý vývoj, protože první nástroje, které se začaly tímto názvem označovat, vznikly již v padesátých letech minulého století, a to na mainframech vyráběných společností IBM i jejími konkurenty (UNIVAC, Burroughs, Honeywell, General Electric atd.). Před vznikem skutečných assemblerů byla situace poněkud složitá. První aplikace pro mainframy totiž byly většinou programovány přímo ve strojovém kódu, který bylo možné přímo zadávat z takzvaného řídicího panelu (control panel) počítače či načítat z externích paměťových médií (děrných štítků, magnetických pásek atd.). Ovšem zapisovat programy přímo ve strojovém kódu je zdlouhavé, vedoucí k častým chybám a pro větší aplikace z mnoha důvodů nepraktické, o čemž se ostatně mohli relativně nedávno přesvědčit například i studenti programující na československém mikropočítači PMI-80 (na druhou stranu se ovšem jednalo o vynikající učební pomůcku).
Z důvodu usnadnění práce programátorů a pro snížení počtu chyb (oprava chyby, resp. další iterace vývoje, byla velmi drahá a zdlouhavá) tedy vznikly první utility, jejichž úkolem bylo transformovat programy zapsané s využitím symbolických jmen strojových instrukcí do (binárního) strojového kódu určeného pro konkrétní typ počítače a jeho procesoru.

Obrázek 1: Kód v assembleru je možné, pochopitelně pokud to daný assembler umožňuje, psát i strukturovaně, používat subrutiny a funkce atd.
Těmto programům, jejichž možnosti se postupně vylepšovaly (například do nich přibyla podpora textových maker, řízení víceprůchodového překladu, vytváření výstupních sestav s překládanými symboly, později i skutečné linkování s knihovnami atd.), se začalo říkat assemblery a jazyku pro symbolický zápis programů pak jazyk symbolických instrukcí či jazyk symbolických adres – assembly language (někdy též zkráceně nazývaný assembler, takže toto slovo má vlastně dodnes oba dva významy). Jednalo se o svým způsobem převratnou myšlenku: sám počítač byl použit pro tvorbu programů, čímž odpadla namáhavá práce s tužkou a papírem. Posléze se zjistilo, že i programování přímo v assembleru je většinou pracné a zdlouhavé, takže se na mainframech začaly používat různé vyšší programovací jazyky, zejména FORTRAN a COBOL. Použití vyšších programovacích jazyků bylo umožněno relativně vysokým výpočetním výkonem mainframů i (opět relativně) velkou kapacitou operační paměti; naopak se díky vyšším programovacím jazykům mohly aplikace přenášet na různé typy počítačů, což je nesporná výhoda.

Obrázek 2: Assembler pro počítače Commodore C64.
3. Assemblery na domácích osmibitových mikropočítačích i na počítačích s procesory Motorola 68000
Oživení zájmu o programování v assembleru přinesl vznik minipočítačů (například známé řady PDP) a na konci sedmdesátých let minulého století pak zcela nového fenoménu, který nakonec přepsal celé dějiny výpočetní techniky – domácích osmibitových mikropočítačů. Na osmibitových domácích mikropočítačích se používaly dva typy assemblerů. Prvním typem byly assemblery interaktivní, které uživateli nabízely poměrně komfortní vývojové prostředí, v němž bylo možné zapisovat jednotlivé instrukce v symbolické podobě, spouštět programy, krokovat je, vypisovat obsahy pracovních registrů mikroprocesoru atd. Výhodou byla nezávislost těchto assemblerů na rychlém externím paměťovém médiu (například disketové jednotce), který mnoho uživatelů a programátorů ani nevlastnilo. Druhý typ assemblerů je používán dodnes – jedná se vlastně o běžné překladače, kterým se na vstupu předloží zdrojový kód (uložený na kazetě či disketě) a po překladu se výsledný nativní kód taktéž uloží na paměťové médium (odkud ho lze následně spustit). Tyto assemblery byly mnohdy vybaveny více či méně dokonalým systémem maker (odtud ostatně pochází i označení macroassembler).

Obrázek 3: Atari Macro Assembler.
Assemblery byly mezi programátory poměrně populární i na osobních počítačích Amiga a Atari ST, a to i díky tomu, že instrukční kód mikroprocesorů Motorola 68000 byl do značné míry ortogonální, obsahoval relativně velké množství registrů (univerzální datové registry D0 až D7 a adresové registry A0 až A7) a navíc bylo možné používat i takové adresovací režimy, které korespondovaly s konstrukcemi používanými ve vyšších programovacích jazycích (přístupy k prvkům polí, přístup k lokálním proměnným umístěných v zásobníkovém rámci, autoinkrementace adresy atd.). Podívejme se na jednoduchý příklad rutiny (originál najdete zde), která sečte všechny prvky (16bitové integery – načítá se vždy jen 16bitové slovo) v poli. V tomto příkladu se používá autoinkrementace adresy při adresování prvků polí a taktéž instrukce DBRA provádí dvě činnosti – snížení hodnoty registru o jedničku a skok v případě, že je výsledek nenulový:
moveq #0, d0 ; potřebujeme vynulovat horních 16 bitů d0 moveq #0, d1 ; mezivýsledek loop: move.w (a0)+, d0 ; horních 16 bitů d0 je pořád nastaveno na 0 add.l d0, d1 dbra d2, loop ; d2 je použit jako počitadlo
4. Assemblery v Linuxu
V této kapitole budeme pod termínem „assembler“ chápat programový nástroj určený pro transformaci zdrojového kódu naprogramovaného v jazyku symbolických adres do strojového kódu. Pro Linux vzniklo hned několik takových nástrojů, přičemž některé nástroje jsou komerční a jiné patří mezi open source. Z nekomerčních nástrojů, které nás samozřejmě zajímají především, se jedná o známý GNU Assembler, dále pak o nástroj nazvaný Netwide assembler (NASM), nástroj Yasm Modular Assembler či až překvapivě výkonný vasm. NASM a Yasm jsou pro první krůčky v assembleru velmi dobře použitelné, neboť mají dobře zpracovaný mechanismus reakce na chyby, dají se v nich psát čitelné programy atd. Určitý problém nastává v případě, kdy je nutné vyvíjet aplikace určené pro jinou architekturu, než je i386 či x86_64, a to z toho důvodu, že ani Netwide assembler ani Yasm nedokážou pracovat s odlišnou instrukční sadou. Naproti tomu GNU Assembler tímto problémem ani zdaleka netrpí, ovšem zápis assembleru se pro každou architekturu odlišuje (což se například týká i zápisu poznámek atd.).
GNU Assembler (gas) je součástí skupiny nástrojů nazvaných GNU Binutils. Jedná se o nástroje určené pro vytváření a správu binárních souborů obsahujících takzvaný „objektový kód“, dále nástrojů určených pro práci s knihovnami strojových funkcí i pro profilování. Mezi GNU Binutils patří vedle GNU Assembleru i linker ld, profiler gprof, správce archivů strojových funkcí ar, nástroj pro odstranění symbolů z objektových a spustitelných souborů strip a několik pomocných utilit typu nm, objdump, size a strings. GNU Assembler je možné použít buď pro překlad uživatelem vytvořených zdrojových kódů nebo pro zpracování kódů vygenerovaných překladači vyšších programovacích jazyků (GCC atd.). Zajímavé je, že všechny moderní verze GNU Assembleru podporují jak původní AT&T syntaxi, tak i (podle mě čitelnější) syntaxi používanou společností Intel.
Netwide Assembler (NASM) vznikl v době, kdy začali na operační systém Linux přecházet programátoři znající operační systémy DOS a (16/32bit) Windows. Tito programátoři byli většinou dobře seznámeni s možnostmi assemblerů, které se na těchto platformách používaly nejčastěji – Turbo Assembleru (TASM) společnosti Borland i Microsoft Macro Assembleru (MASM) a tak jim možnosti GNU Assembleru (který má své kořeny na odlišných architekturách) příliš nevyhovovaly. Výsledkem snah o vytvoření nástroje podobnému TASMu či MASMu byl právě NASM, který podporuje stejný způsob zápisu operandů instrukcí a navíc ještě zjednodušuje zápis těch instrukcí, u nichž je jeden operand tvořen nepřímou adresou. NASM byl následován projektem Yasm (fork+přepis), ovšem základní vlastnosti a především pak vazba na platformu i386 a x86_64 zůstaly zachovány (to mj. znamená, že například na Raspberry Pi možnosti těchto dvou nástrojů plně nevyužijeme, což je určitě škoda; situace je o to zajímavější, že původní autor NASMu nyní pracuje pro společnost ARM).
5. Assembler a programovací jazyk Go
Jak jsme si již naznačili v úvodní kapitole, používá se v programovacím jazyku Go assembler odvozený od assembleru z operačního systému Plan-9. Důvodů, proč se používá zvláštní assembler a nikoli například výše zmíněný GNU Assembler je několik. Kromě toho, že autoři Go (pochopitelně) velmi dobře znají Plan-9 je assembler využitý v Go napsán unifikovaně – velká část jeho zdrojového kódu je sdílena pro všechny podporované mikroprocesorové architektury. Taktéž syntaxe assembleru v Go je totožná pro všechny architektury, což v případě GNU Assembleru neplatí, protože zde nalezneme snahu o dodržení konvencí zavedených různými výrobci čipů. O tom se ostatně můžeme snadno přesvědčit, například na aplikaci typu „Hello world“.
Architektura i386:
# Linux kernel system call table sys_exit=1 sys_write=4 .section .data hello_lbl: .string "Hello World!\n" .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov $sys_write, %eax # cislo syscallu pro funkci "write" mov $1,%ebx # standardni vystup mov $hello_lbl,%ecx # adresa retezce, ktery se ma vytisknout mov $13,%edx # pocet znaku, ktere se maji vytisknout int $0x80 # volani Linuxoveho kernelu movl $sys_exit,%eax # cislo sycallu pro funkci "exit" movl $0,%ebx # exit code = 0 int $0x80 # volani Linuxoveho kernelu
Architektura Aarch64:
# Linux kernel system call table sys_exit=93 sys_write=64 .section .data hello_lbl: .string "Hello World!\n" .section .bss .section .text .global _start // tento symbol ma byt dostupny i linkeru _start: mov x8, #sys_write // cislo sycallu pro funkci "write" mov x0, #1 // standardni vystup ldr x1, =hello_lbl // adresa retezce, ktery se ma vytisknout mov x2, #13 // pocet znaku, ktere se maji vytisknout svc 0 // volani Linuxoveho kernelu mov x8, #sys_exit // cislo sycallu pro funkci "exit" mov x0, #0 // exit code = 0 svc 0 // volani Linuxoveho kernelu
Architektura s390 (autorem je v tomto případě Honza Horák):
# Linux kernel system call table sys_exit=1 sys_write=4 .section .data hello_lbl: .string "Hello World!\n" .section .bss .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: basr 13,0 # nastaveni literal poolu .L0: ahi 13,.LT0-.L0 la 1,sys_write # cislo syscallu pro funkci "write" la 2,1 # standardni vystup l 3,.LC1-.LT0(13) # adresa retezce, ktery se ma vytisknout la 4,13 # pocet znaku, ktere se maji vytisknout svc 0 # volani Linuxoveho kernelu la 1,sys_exit # cislo sycallu pro funkci "exit" la 2,0 # exit code = 0 svc 0 # volani Linuxoveho kernelu # literal pool .LT0: .LC1: .long hello_lbl
Architektura PPC64 (autorem je Rafael Fonseca):
# Linux kernel system call table sys_exit=1 sys_write=4 .section .data hello_lbl: .ascii "Hello World!\n" hello_length = . - hello_lbl .section .text .global _start .section ".opd", "aw" .balign 8 _start: .quad ._start, .TOC.@tocbase, 0 .previous .global ._start ._start: li 0,sys_write # cislo syscallu pro funkci "write" li 3,1 # fd = 1 (stdout) lis 4,hello_lbl@highest # load buffer ori 4,4,hello_lbl@higher rldicr 4,4,32,31 oris 4,4,hello_lbl@h ori 4,4,hello_lbl@l li 5,hello_length # size sc # volani Linuxoveho kernelu li 0,sys_exit # cislo syscallu pro funkci "exit" li 3,0 # exit code = 0 sc # volani Linuxoveho kernelu
6. Kód v assembleru generovaný překladačem jazyka Go
Nejprve se podívejme na možnosti vybraných nástrojů, které jsou součástí standardní instalace programovacího jazyka Go. První nástroj se jmenuje compile a provádí – jak již jeho název naznačuje – překlad zdrojových kódů z Go do objektového kódu. Přepínačem -S můžeme vynutit výpis generovaného objektového kódu v symbolické podobě. Překládat budeme tento zdrojový kód (nejkratší korektně zapsaný program):
package main func main() { }
Překlad s výpisem (pseudo)objektového kódu se provede takto:
$ go tool compile -S asm01.go
Výsledkem může být následující text:
# command-line-arguments "".main STEXT nosplit size=1 args=0x0 locals=0x0 0x0000 00000 (/home/tester/go-root/article_53/asm01.go:3) TEXT "".main(SB), NOSPLIT, $0-0 0x0000 00000 (/home/tester/go-root/article_53/asm01.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (/home/tester/go-root/article_53/asm01.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (/home/tester/go-root/article_53/asm01.go:3) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (/home/tester/go-root/article_53/asm01.go:4) RET 0x0000 c3 . "".init STEXT size=80 args=0x0 locals=0x8 0x0000 00000 (<autogenerated>:1) TEXT "".init(SB), $8-0 0x0000 00000 (<autogenerated>:1) MOVQ (TLS), CX 0x0009 00009 (<autogenerated>:1) CMPQ SP, 16(CX) 0x000d 00013 (<autogenerated>:1) JLS 73 0x000f 00015 (<autogenerated>:1) SUBQ $8, SP 0x0013 00019 (<autogenerated>:1) MOVQ BP, (SP) 0x0017 00023 (<autogenerated>:1) LEAQ (SP), BP 0x001b 00027 (<autogenerated>:1) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001b 00027 (<autogenerated>:1) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001b 00027 (<autogenerated>:1) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001b 00027 (<autogenerated>:1) PCDATA $2, $0 0x001b 00027 (<autogenerated>:1) PCDATA $0, $0 0x001b 00027 (<autogenerated>:1) MOVBLZX "".initdone·(SB), AX 0x0022 00034 (<autogenerated>:1) CMPB AL, $1 0x0025 00037 (<autogenerated>:1) JLS 48 0x0027 00039 (<autogenerated>:1) PCDATA $2, $-2 0x0027 00039 (<autogenerated>:1) PCDATA $0, $-2 0x0027 00039 (<autogenerated>:1) MOVQ (SP), BP 0x002b 00043 (<autogenerated>:1) ADDQ $8, SP 0x002f 00047 (<autogenerated>:1) RET 0x0030 00048 (<autogenerated>:1) JNE 57 0x0032 00050 (<autogenerated>:1) PCDATA $2, $0 0x0032 00050 (<autogenerated>:1) PCDATA $0, $0 0x0032 00050 (<autogenerated>:1) CALL runtime.throwinit(SB) 0x0037 00055 (<autogenerated>:1) UNDEF 0x0039 00057 (<autogenerated>:1) MOVB $2, "".initdone·(SB) 0x0040 00064 (<autogenerated>:1) MOVQ (SP), BP 0x0044 00068 (<autogenerated>:1) ADDQ $8, SP 0x0048 00072 (<autogenerated>:1) RET 0x0049 00073 (<autogenerated>:1) NOP 0x0049 00073 (<autogenerated>:1) PCDATA $0, $-1 0x0049 00073 (<autogenerated>:1) PCDATA $2, $-1 0x0049 00073 (<autogenerated>:1) CALL runtime.morestack_noctxt(SB) 0x004e 00078 (<autogenerated>:1) JMP 0 0x0000 64 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 3a 48 dH..%....H;a.v:H 0x0010 83 ec 08 48 89 2c 24 48 8d 2c 24 0f b6 05 00 00 ...H.,$H.,$..... 0x0020 00 00 80 f8 01 76 09 48 8b 2c 24 48 83 c4 08 c3 .....v.H.,$H.... 0x0030 75 07 e8 00 00 00 00 0f 0b c6 05 00 00 00 00 02 u............... 0x0040 48 8b 2c 24 48 83 c4 08 c3 e8 00 00 00 00 eb b0 H.,$H........... rel 5+4 t=16 TLS+0 rel 30+4 t=15 "".initdone·+0 rel 51+4 t=8 runtime.throwinit+0 rel 59+4 t=15 "".initdone·+-1 rel 74+4 t=8 runtime.morestack_noctxt+0 go.loc."".main SDWARFLOC size=0 go.info."".main SDWARFINFO size=33 0x0000 02 22 22 2e 6d 61 69 6e 00 00 00 00 00 00 00 00 ."".main........ 0x0010 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00 01 ................ 0x0020 00 . rel 9+8 t=1 "".main+0 rel 17+8 t=1 "".main+1 rel 27+4 t=29 gofile../home/tester/go-root/article_53/asm01.go+0 go.range."".main SDWARFRANGE size=0 go.isstmt."".main SDWARFMISC size=0 0x0000 04 01 00 ... go.loc."".init SDWARFLOC size=0 go.info."".init SDWARFINFO size=33 0x0000 02 22 22 2e 69 6e 69 74 00 00 00 00 00 00 00 00 ."".init........ 0x0010 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00 01 ................ 0x0020 00 . rel 9+8 t=1 "".init+0 rel 17+8 t=1 "".init+80 rel 27+4 t=29 gofile..<autogenerated>+0 go.range."".init SDWARFRANGE size=0 go.isstmt."".init SDWARFMISC size=0 0x0000 04 0f 04 0c 03 07 01 05 02 09 01 07 02 09 01 09 ................ 0x0010 02 07 00 ... "".initdone· SNOPTRBSS size=1 gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8 0x0000 01 00 00 00 00 00 00 00 ........
Překlad lze provést pro různé architektury mikroprocesorů a pro různé operační systémy (provádí se tedy crosspřeklad). Použijeme přitom standardní prostředky programovacího jazyka Go; žádné další nástroje ani knihovny nejsou zapotřebí!:
$ GOOS=linux GOARCH=386 go tool compile -S asm01.go
$ GOOS=linux GOARCH=arm go tool compile -S asm01.go
$ GOOS=linux GOARCH=arm64 go tool compile -S asm01.go
7. Nástroj objdump pro programovací jazyk Go
Další nástroj, který je užitečný zejména při studiu strojového kódu vygenerovaného překladačem programovacího jazyka Go, je utilita nazvaná objdump. Tento nástroj (ve variantě pro Go) se spouští pomocí go tool objdump a přepínačem -s lze zvolit funkci či další typ objektu, který se ve výsledném spustitelném souboru vyhledá a provede se jeho/její disassembling, tj. zpětná rekonstrukce kódu v assembleru. Pro výše uvedený program s prázdnou funkcí main:
package main func main() { }
tedy získáme:
$ go tool objdump -s main.main asm01 TEXT main.main(SB) /home/tester/go-root/article_53/asm01.go asm01.go:4 0x44ea70 c3 RET
Nyní se pokusme přeložit a následně disassemblovat poněkud složitější implementaci funkce main, která již obsahuje volání jiné funkce (jedná se o builtin funkci s názvem println):
package main func main() { println("Hello world!") }
Postup bude následující:
$ go build asm02.go $ go tool objdump -s main.main asm02 TEXT main.main(SB) /home/tester/go-root/article_53/asm02.go asm02.go:3 0x44ea70 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX asm02.go:3 0x44ea79 483b6110 CMPQ 0x10(CX), SP asm02.go:3 0x44ea7d 763b JBE 0x44eaba asm02.go:3 0x44ea7f 4883ec18 SUBQ $0x18, SP asm02.go:3 0x44ea83 48896c2410 MOVQ BP, 0x10(SP) asm02.go:3 0x44ea88 488d6c2410 LEAQ 0x10(SP), BP asm02.go:4 0x44ea8d e82e41fdff CALL runtime.printlock(SB) asm02.go:4 0x44ea92 488d0590e50100 LEAQ 0x1e590(IP), AX asm02.go:4 0x44ea99 48890424 MOVQ AX, 0(SP) asm02.go:4 0x44ea9d 48c74424080d000000 MOVQ $0xd, 0x8(SP) asm02.go:4 0x44eaa6 e8354afdff CALL runtime.printstring(SB) asm02.go:4 0x44eaab e89041fdff CALL runtime.printunlock(SB) asm02.go:5 0x44eab0 488b6c2410 MOVQ 0x10(SP), BP asm02.go:5 0x44eab5 4883c418 ADDQ $0x18, SP asm02.go:5 0x44eab9 c3 RET asm02.go:3 0x44eaba e83183ffff CALL runtime.morestack_noctxt(SB) asm02.go:3 0x44eabf ebaf JMP main.main(SB)
Předchozí kód může být u delších funkcí poněkud matoucí, ovšem přepínačem -S můžeme zajistit, že se kromě vlastních instrukcí assembleru vždy vypíšou i příslušné příkazy, které programátor použil v původním zdrojovém kódu:
$ go tool objdump -S -s main.main asm02 TEXT main.main(SB) /home/tester/go-root/article_53/asm02.go func main() { 0x44ea70 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX 0x44ea79 483b6110 CMPQ 0x10(CX), SP 0x44ea7d 763b JBE 0x44eaba 0x44ea7f 4883ec18 SUBQ $0x18, SP 0x44ea83 48896c2410 MOVQ BP, 0x10(SP) 0x44ea88 488d6c2410 LEAQ 0x10(SP), BP println("Hello world!") 0x44ea8d e82e41fdff CALL runtime.printlock(SB) 0x44ea92 488d0590e50100 LEAQ 0x1e590(IP), AX 0x44ea99 48890424 MOVQ AX, 0(SP) 0x44ea9d 48c74424080d000000 MOVQ $0xd, 0x8(SP) 0x44eaa6 e8354afdff CALL runtime.printstring(SB) 0x44eaab e89041fdff CALL runtime.printunlock(SB) } 0x44eab0 488b6c2410 MOVQ 0x10(SP), BP 0x44eab5 4883c418 ADDQ $0x18, SP 0x44eab9 c3 RET func main() { 0x44eaba e83183ffff CALL runtime.morestack_noctxt(SB) 0x44eabf ebaf JMP main.main(SB)
8. Inlining funkcí při optimalizacích prováděných překladačem jazyka Go
Podobným způsobem, jaký byl popsán v předchozí kapitole, se pokusme přeložit a analyzovat následující jednoduchý prográmek, v němž je deklarována funkce provádějící součet svých dvou celočíselných operandů:
package main func Add(x int, y int) int { return x + y } func main() { println(Add(1, 2)) }
Překlad:
$ go build asm02.go
Analýza funkce main nástrojem objdump:
$ go tool objdump -S -s main.main asm03 TEXT main.main(SB) /home/tester/go-root/article_53/asm03.go func main() { 0x44ea70 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX 0x44ea79 483b6110 CMPQ 0x10(CX), SP 0x44ea7d 7634 JBE 0x44eab3 0x44ea7f 4883ec10 SUBQ $0x10, SP 0x44ea83 48896c2408 MOVQ BP, 0x8(SP) 0x44ea88 488d6c2408 LEAQ 0x8(SP), BP println(Add(1, 2)) 0x44ea8d e82e41fdff CALL runtime.printlock(SB) 0x44ea92 48c7042403000000 MOVQ $0x3, 0(SP) 0x44ea9a e89148fdff CALL runtime.printint(SB) 0x44ea9f e89c43fdff CALL runtime.printnl(SB) 0x44eaa4 e89741fdff CALL runtime.printunlock(SB) } 0x44eaa9 488b6c2408 MOVQ 0x8(SP), BP 0x44eaae 4883c410 ADDQ $0x10, SP 0x44eab2 c3 RET func main() { 0x44eab3 e83883ffff CALL runtime.morestack_noctxt(SB) 0x44eab8 ebb6 JMP main.main(SB)
Povšimněte si, že se v tomto kódu nikde nevyskytuje volání funkce add! Veškerá volání (tedy instrukce CALL) slouží pro výpis textů na standardní výstup. Jak je to možné? Vždyť při spuštění programu můžeme vidět, že se výpočet provedl, resp. přesněji řečeno, že se vypsal jeho výsledek.
Překladač programovacího jazyka Go ve skutečnosti provádí řadu optimalizací (a to implicitně). Jednou z těchto optimalizací je inlining funkcí, aby se zamezilo pomalým instrukcím typu CALL a RET, které porušují pipeline v procesoru. A po provedení inliningu se provádí i další optimalizace typu vyhodnocení konstantních výrazů atd.
O tom, že se inlining skutečně provádí, se můžeme snadno přesvědčit, a to použitím přepínačů -gcflags a -m při překladu:
$ go build -gcflags -m asm03.go
Nyní se při překladu vypíšou informace o provedeném inliningu, z něhož je zřejmé, že volání funkce Add v disassemblovaném strojovém kódu neuvidíme:
# command-line-arguments ./asm03.go:3:6: can inline Add ./asm03.go:7:6: can inline main ./asm03.go:8:13: inlining call to Add
9. Překlad jednoduché funkce se zákazem inliningu
Nyní se pokusme přeložit stejný zdrojový kód, ovšem bez použití inliningu:
package main func Add(x int, y int) int { return x + y } func main() { println(Add(1, 2)) }
Překlad se zákazem optimalizací zajistí takto upravený příkaz build:
$ go build -gcflags '-N -l' asm03.go
Nyní si můžeme zobrazit neoptimalizovanou variantu funkce main, ve které je jasně patrné předávání parametrů do Add a volání této funkce (zvýrazněno):
$ go tool objdump -S -s main.main asm03 TEXT main.main(SB) /home/tester/go-root/article_53/asm03.go func main() { 0x44ea90 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX 0x44ea99 483b6110 CMPQ 0x10(CX), SP 0x44ea9d 7655 JBE 0x44eaf4 0x44ea9f 4883ec28 SUBQ $0x28, SP 0x44eaa3 48896c2420 MOVQ BP, 0x20(SP) 0x44eaa8 488d6c2420 LEAQ 0x20(SP), BP println(Add(1, 2)) 0x44eaad 48c7042401000000 MOVQ $0x1, 0(SP) 0x44eab5 48c744240802000000 MOVQ $0x2, 0x8(SP) 0x44eabe e8adffffff CALL main.Add(SB) 0x44eac3 488b442410 MOVQ 0x10(SP), AX 0x44eac8 4889442418 MOVQ AX, 0x18(SP) 0x44eacd e8ee40fdff CALL runtime.printlock(SB) 0x44ead2 488b442418 MOVQ 0x18(SP), AX 0x44ead7 48890424 MOVQ AX, 0(SP) 0x44eadb e85048fdff CALL runtime.printint(SB) 0x44eae0 e85b43fdff CALL runtime.printnl(SB) 0x44eae5 e85641fdff CALL runtime.printunlock(SB) } 0x44eaea 488b6c2420 MOVQ 0x20(SP), BP 0x44eaef 4883c428 ADDQ $0x28, SP 0x44eaf3 c3 RET func main() { 0x44eaf4 e8f782ffff CALL runtime.morestack_noctxt(SB) 0x44eaf9 eb95 JMP main.main(SB)
A navíc budeme mít k dispozici i disassemblovaný kód funkce Add, který využijeme v dalším textu:
$ go tool objdump -S -s main.Add asm03 TEXT main.Add(SB) /home/tester/go-root/article_53/asm03.go func Add(x int, y int) int { 0x44ea70 48c744241800000000 MOVQ $0x0, 0x18(SP) return x + y 0x44ea79 488b442408 MOVQ 0x8(SP), AX 0x44ea7e 4803442410 ADDQ 0x10(SP), AX 0x44ea83 4889442418 MOVQ AX, 0x18(SP) 0x44ea88 c3 RET
Tento kód je již tak jednoduchý, že ho můžeme relativně snadno analyzovat pochopit. Nejdříve je nutné si uvědomit, že jak parametry funkce, tak i její návratové hodnoty jsou ukládány na zásobníkový rámec (stack frame) vytvářený při volání funkce, což (poněkud zjednodušeně řečeno) zajistily tyto instrukce:
SUBQ $0x28, SP MOVQ BP, 0x20(SP) LEAQ 0x20(SP), BP
Adresování parametrů je provedeno relativně vůči hodnotě uložené v SP. Přitom velikost typu int je na 64bitové architektuře AMD64 (x86–64, …) rovna osmi bajtům. První operand je uložen s offsetem 8, druhý s offsetem 16 (0×10) a výsledek je na offsetu 24 (0×18). Nyní je tedy kód zřejmý:
MOVQ $0x0, 0x18(SP) ; výsledek je nejdříve vynulován (nebylo optimalizováno) MOVQ 0x8(SP), AX ; první operand na AX ADDQ 0x10(SP), AX ; druhý operand je přičten k obsahu AX MOVQ AX, 0x18(SP) ; uložení výsledku do příslušného místa v zásobníkovém rámci RET ; návrat z funkce
10. Funkce Add přeložená pro jiné architektury
Pro zajímavost se můžeme podívat, jak se stejná funkce Add přeloží pro další architektury. Nejprve bude uvedena neoptimalizovaná varianta pro 32bitovou architekturu i386:
$ GOOS=linux GOARCH=386 go build -gcflags '-N -l' asm03.go
Analýza vygenerovaného kódu:
$ go tool objdump -S -s main.Add asm03
S výsledkem, který v tomto případě není tak stručný, jako v případě 64bitové varianty (liší se práce se zásobníkovým rámcem):
TEXT main.Add(SB) /home/tester/go-root/article_53/asm03.go func Add(x int, y int) int { 0x808d840 658b0d00000000 MOVL GS:0, CX 0x808d847 8b89fcffffff MOVL 0xfffffffc(CX), CX 0x808d84d 3b6108 CMPL 0x8(CX), SP 0x808d850 7615 JBE 0x808d867 0x808d852 c744240c00000000 MOVL $0x0, 0xc(SP) return x + y 0x808d85a 8b442404 MOVL 0x4(SP), AX 0x808d85e 03442408 ADDL 0x8(SP), AX 0x808d862 8944240c MOVL AX, 0xc(SP) 0x808d866 c3 RET func Add(x int, y int) int { 0x808d867 e894b1ffff CALL runtime.morestack_noctxt(SB) 0x808d86c ebd2 JMP main.Add(SB)
32bitový ARM, zde používající klasickou instrukční sadu, tedy nikoli Thumb ani Thumb-2:
$ GOOS=linux GOARCH=arm go build -gcflags '-N -l' asm03.go $ go tool objdump -S -s main.Add asm03 TEXT main.Add(SB) /home/tester/go-root/article_53/asm03.go func Add(x int, y int) int { 0x5e794 e3a00000 MOVW $0, R0 0x5e798 e58d000c MOVW R0, 0xc(R13) return x + y 0x5e79c e59d0008 MOVW 0x8(R13), R0 0x5e7a0 e59d1004 MOVW 0x4(R13), R1 0x5e7a4 e0810000 ADD R0, R1, R0 0x5e7a8 e58d000c MOVW R0, 0xc(R13) 0x5e7ac e28ef000 ADD $0, R14, R15
A nakonec 64bitový ARM neboli architektura AArch64:
$ GOOS=linux GOARCH=arm64 go build -gcflags '-N -l' asm03.go $ go tool objdump -S -s main.Add asm03 TEXT main.Add(SB) /home/tester/go-root/article_53/asm03.go func Add(x int, y int) int { 0x58f30 f9000fff MOVD ZR, 24(RSP) return x + y 0x58f34 f9400be0 MOVD 16(RSP), R0 0x58f38 f94007e1 MOVD 8(RSP), R1 0x58f3c 8b000020 ADD R0, R1, R0 0x58f40 f9000fe0 MOVD R0, 24(RSP) 0x58f44 d65f03c0 RET 0x58f48 00000000 ? 0x58f4c 00000000 ?
11. Kostra aplikace, v níž je jedna funkce vytvořená v assembleru
Nyní si již můžeme připravit jednoduchý projekt, v němž bude vybraná funkce implementovaná v assembleru a následně bude slinkovaná se zbytkem programu. Vytvoříme si nový adresář, v němž bude projekt uložen:
$ mkdir add05
V tomto adresáři vytvoříme kostru projektu:
$ cd add05 $ go mod init add
Předchozí příklad by měl vytvořit soubor „go.mod“, který bude obsahovat jediný řádek:
module add
Ve třetím kroku vytvoříme zdrojový kód programu uložený do souboru „add.go“. Povšimněte si, že se sice jedná o syntakticky korektně zapsaný zdrojový kód, který však nebude samostatně přeložitelný, a to z toho důvodu, že u funkce add chybí její tělo:
package main func add(x int64, y int64) int64 func main() { println(add(1, 2)) }
Pokud se pokusíte tento program přeložit, měl by překladač vypsat chybu:
$ go build # add ./add.go:3:6: missing function body
12. Vytvoření funkce v assembleru, překlad programu
Nyní nám zbývá pouhá „maličkost“ – doimplementovat funkci add v assembleru a nějakým způsobem instruovat překladač, aby ji použil namísto hlavičky funkce uvedené ve zdrojovém kódu zapsaném v jazyku Go.
Vytvoříme tedy nový soubor pojmenovaný „add_amd64.s“ (použití jména architektury je zde nutné!), jehož obsah bude následující:
TEXT ·add(SB),$0 MOVQ $0x0, 0x18(SP) MOVQ 0x8(SP), AX ADDQ 0x10(SP), AX MOVQ AX, 0x18(SP) RET
Tento kód si žádá podrobnější vysvětlení. Samotná sekvence instrukcí v assembleru pro nás nebude nová, protože se jedná o kód vypůjčený z předchozí analýzy (nijak jsme ho nemodifikovali). Ovšem nový je první řádek. Podívejme se na něj ještě jednou:
TEXT ·add(SB),$0
Šestým znakem je „·“ je znak s pozicí v Unikódu 183, jedná se tedy o znak mimo ASCII (a tudíž obtížně zapsatelný na klávesnici). Toto je jedna ze specialit assembleru odvozeného od nástrojů z Plan 9, která je však v ekosystému programovacího jazyka Go používaná na více místech.
Dále můžeme v kódu vidět několik jmen registrů a pseudoregistrů:
Registr | Význam |
---|---|
AX | registr architektury x86–64 (zde AX/EAX/RAX) |
FP | frame pointer – pseudoregistr shodný na všech architekturách |
SB | static base pointer – pseudoregistr shodný na všech architekturách |
SP | stack pointer – pseudoregistr shodný na všech architekturách |
SP | stack pointer (stejné jméno!) – registr architektury x86–64 |
Program nyní přeložíme běžným způsobem, protože překladač jazyka Go již bude vědět, kde hledat implementaci funkce add a jak tuto funkci zařadit do výsledného programu:
$ go build
O správném zařazení funkce add se lze snadno přesvědčit:
$ go tool objdump -s main.add add TEXT main.add(SB) /home/tester/go-root/article_53/add04/add_amd64.s add_amd64.s:2 0x44ebb0 48c744241800000000 MOVQ $0x0, 0x18(SP) add_amd64.s:3 0x44ebb9 488b442408 MOVQ 0x8(SP), AX add_amd64.s:4 0x44ebbe 4803442410 ADDQ 0x10(SP), AX add_amd64.s:5 0x44ebc3 4889442418 MOVQ AX, 0x18(SP) add_amd64.s:6 0x44ebc8 c3 RET
13. Zjednodušení práce se zásobníkem při čtení operandů funkce
Předchozí kód v assembleru nebyl ve skutečnosti příliš čitelný, neboť jsme v něm ručně specifikovali offsety parametrů uložených na zásobníkovém rámci:
TEXT ·add(SB),$0 MOVQ $0x0, 0x18(SP) MOVQ 0x8(SP), AX ADDQ 0x10(SP), AX MOVQ AX, 0x18(SP) RET
Kód je však možné upravit takovým způsobem, že se offsety sice stále budou používat, ale společně s názvem parametru funkce (je jedno, jaký parametr se zapíše, důležitá je syntax). Syntaxe zápisu je parametr+offset(SP). Úpravou syntaxe se automaticky změní i použitý registr: namísto registru SP na architektuře x86–64 se použije pseuodoregistr (zde však má stejné offsety, takže to ani nepoznáme). Další změnou je použití dekadické soustavy a taktéž vynechání první instrukce, která pouze vynuluje obsah 64bitového slova, do kterého o tři instrukce dál zapíšeme vypočtený výsledek:
TEXT ·add(SB),$0 MOVQ x+8(SP), AX ADDQ y+16(SP), AX MOVQ AX, 24(SP) RET
Po vytvoření binárního souboru:
$ go build
se můžeme přesvědčit, že výsledek je stále stejný, jako tomu bylo v příkladu předchozím:
$ go tool objdump -s main.add add TEXT main.add(SB) /home/tester/go-root/article_53/add05/add_amd64.s add_amd64.s:2 0x44ebb0 488b442408 MOVQ 0x8(SP), AX add_amd64.s:3 0x44ebb5 4803442410 ADDQ 0x10(SP), AX add_amd64.s:4 0x44ebba 4889442418 MOVQ AX, 0x18(SP) add_amd64.s:5 0x44ebbf c3 RET
Pokud si budete chtít vyzkoušet samotný assembler (tedy překladač z jazyka assembly language do objektového kódu), použijte následující příkaz:
$ go tool asm -debug add_amd64.s
Přepínač -debug zajistí, že assembler bude opisovat zpracovávaný kód:
1 00001 (add_amd64.s:1) TEXT "".add(SB), $0 2 00002 (add_amd64.s:2) MOVQ x+8(SP), AX 3 00003 (add_amd64.s:3) ADDQ y+16(SP), AX 4 00004 (add_amd64.s:4) MOVQ AX, 24(SP) 5 00005 (add_amd64.s:5) RET
14. Použití pseudoregistru FP
V assembleru programovacího jazyka Go je možné pro adresování parametrů funkcí, návratových hodnot atd. použít i pseudoregistr FP. Nejedná se ovšem o registr související s matematickým koprocesorem (Floating Point Unit), ale o registr, jehož zkratka FP odpovídá sousloví „Frame Pointer“. Tento pseudoregistr tedy umožňuje mj. použít offsety, u nichž nemusíme brát v úvahu fakt, že na zásobníkovém rámci je uložena i návratová adresa. První parametr bude mít vždy offset 0 (oproti FP, nikoli SP), druhý offset odpovídající velikosti prvního parametru atd. atd. Zápis se tedy – minimálně z hlediska programátora – nepatrně zjednoduší a bude vypadat následovně:
TEXT ·add(SB),$0 MOVQ x+0(FP), AX ADDQ y+8(FP), AX MOVQ AX, 0x18(SP) RET
Malý test, zda je výsledkem překladu assemblerem stále shodný strojový kód:
$ go build $ go tool objdump -s main.add add TEXT main.add(SB) /home/tester/go-root/article_53/add06/add_amd64.s add_amd64.s:2 0x44ebb0 488b442408 MOVQ 0x8(SP), AX add_amd64.s:3 0x44ebb5 4803442410 ADDQ 0x10(SP), AX add_amd64.s:4 0x44ebba 4889442418 MOVQ AX, 0x18(SP) add_amd64.s:5 0x44ebbf c3 RET
15. Pojmenování výstupní hodnoty funkce s jejím využitím v assembleru
V předchozích kapitolách jsme si ukázali, že k parametrům funkce je možné v assembleru přistupovat přes jejich jména, což zpřehlední zápis kódu. Jak je tomu ovšem v případě výsledné hodnoty či výsledných hodnot funkce? Pokud tuto hodnotu nebo hodnoty pojmenujeme, což programovací jazyk Go podporuje, bude možné i k takovým hodnotám přistupovat přímo z assembleru:
package main func add(x int64, y int64) (result int64) func main() { println(add(1, 2)) }
Samotnou implementaci funkce add v assembleru bude nyní možné zapsat následovně:
TEXT ·add(SB),$0 MOVQ x+0(FP), AX ADDQ y+8(FP), AX MOVQ AX, result+16(FP) RET
Po vytvoření binárního souboru, opět nám již známým příkazem:
$ go build
uvidíme, že výsledek překladu bude stále shodný s předchozími třemi příklady, i když assemblerovský zdrojový kód je nyní mnohem čitelnější:
$ go tool objdump -s main.add add TEXT main.add(SB) /home/tester/go-root/article_53/add07/add_amd64.s add_amd64.s:2 0x44ebb0 488b442408 MOVQ 0x8(SP), AX add_amd64.s:3 0x44ebb5 4803442410 ADDQ 0x10(SP), AX add_amd64.s:4 0x44ebba 4889442418 MOVQ AX, 0x18(SP) add_amd64.s:5 0x44ebbf c3 RET
16. Kód v assembleru určený pro další procesorové architektury
Pokud se pokusíme náš příklad přeložit pro jiné architektury, dojde pochopitelně k chybě, protože příslušná implementace funkce neexistuje:
$ GOOS=linux GOARCH=386 go build # add ./add.go:3:6: missing function body
Popř.:
$ GOOS=linux GOARCH=arm go build # add ./add.go:3:6: missing function body
Řešení je nasnadě – je nutné vytvořit pro každou podporovanou architekturu zvláštní soubor s příslušným kódem v assembleru:
- add_amd64.s pro x86–64
- add_arm.s pro 32bitové ARMy
- atd.
Vytvoříme si tedy další soubor, tentokrát pro 32bitové ARMy:
TEXT ·add(SB),$0 MOVW 0x8(R13), R0 MOVW 0x4(R13), R1 ADD R0, R1, R0 MOVW R0, 0xc(R13) ADD $0, R14, R15
Překlad (crosspřeklad) pro 32bitovou architekturu ARM:
$ GOOS=linux GOARCH=arm go build $ file add add: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
17. Seznam dnes použitých instrukcí
V tabulce umístěné pod tímto odstavcem jsou vypsány všechny instrukce 64bitových procesorů s architekturou x86–64 (AMD64), které jsme explicitně použili v dnešních demonstračních příkladech:
# | Mnemotechnická zkratka | Stručný popis instrukce |
---|---|---|
1 | MOVQ | přesun obsahu prvního 64bitového operandu do operandu druhého (naopak, než jste možná zvyklí) |
2 | ADDQ | přičtení prvního 64bitového operandu k obsahu operandu druhého |
3 | RET | návrat z podprogramu, návratový kód je uložen v paměti na adrese adresované obsahem registru SP (ESP) |
18. Obsah následující části seriálu
Dnes jsme si vysvětlili pouze velmi malou část celé problematiky související s použitím programovacího jazyka Go společně s assemblerem. Příště si ukážeme složitější příklady, včetně několika příkladů praktičtějších, v nichž se již využijí specifické instrukce, které skutečně povedou ke zrychlení výsledné aplikace (typicky se jedná o instrukce, které nemají přímou obdobu ve vysokoúrovňovém kódu napsaném v Go či v dalším vysokoúrovňovém programovacím jazyku).
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:
20. Odkazy na Internetu
- 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