Hlavní navigace

Programovací jazyk Go a assembler

28. 1. 2020
Doba čtení: 52 minut

Sdílet

 Autor: Go Lang
Při tvorbě aplikací v Go se můžeme někdy dostat do situace, v níž je nutné použít nízkoúrovňový přístup na úrovni assembleru. Podíváme se proto na vlastnosti poměrně specifického assembleru jazyka Go.

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

4. Assemblery v Linuxu

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

14. Použití pseudoregistru FP

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

20. Odkazy na Internetu

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:

  1. Bootstraping
  2. Implementace některých matematických funkcí ze standardní knihovny
  3. Víceslovní aritmetika a s ní související operace
  4. Implementace krypto funkcí (ty mnohdy používají specializované instrukce), opět použito i ve standardní knihovně
  5. Synchronizační primitiva (zámky atd.)
  6. Kód, který lze převést na vektorové instrukce (audio a video processing atd.)
  7. 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
Poznámka: povšimněte si, jak se od sebe odlišuje pojmenování registrů, zápis jmen registrů, adresování, zápis poznámek atd. Toto vše jsou syntaktické kategorie, které je možné do určité míry sjednotit (samozřejmě se to netýká instrukčního kódu).

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                          ........
Poznámka: jednotlivými sekcemi se budeme podrobněji zabývat v navazující části tohoto seriálu.

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
Poznámka: povšimněte si prozatím pouze odlišných instrukcí pro různé mikroprocesorové architektury.

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
Poznámka: povšimněte si, že prázdná funkce main je skutečně přeložena tím nejjednodušším a nejkratším kódem – pouhou instrukcí typu return.

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)
Poznámka: prozatím se mohou všechny uváděné postupy zdát poněkud umělé, ovšem znalost všech nástrojů využijeme v dalších příkladech.

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
Poznámka: velmi pěkně jsou zde vidět specifika assembleru použitého v Go, zejména poněkud matoucí pojmenování registrů. AX totiž neodpovídá 16bitovému registru AX, ale EAX (protože je použit v kontextu instrukce MOVQ a ADDQ, kde „Q“ značí „quad word“). Totéž ostatně platí i pro SP, který má dva významy, jména pseudoregistru FP atd. atd.

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
Poznámka: tato architektura používá R13 pro ukazatel na vrchol zásobníku, R14 pro uložení návratové adresy (link register) a R15 pro uložení programového čítače. Bližší informace o architektuře ARM najdete v článcích Mikroprocesory s architekturou ARM, Pohled programátora na mikroprocesory ARM a Architektura mikrořadičů s jádry ARM Cortex-M3.

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
Poznámka: v předchozím příkladu je použit skutečný registr SP, nikoli pseudoregistr (rozlišení je provedeno na základě zápisu operandů).

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
Poznámka: v navazujících kapitolách si ukážeme, že pojmenováním návratové hodnoty (což jazyk Go umožňuje), lze zápis ještě více zlidštit.

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
Poznámka: ve výpisu objektového kódu se opět vyskytuje pouze (skutečný) registr SP, nikoli pseudoregistr FP.

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:

  1. add_amd64.s pro x86–64
  2. add_arm.s pro 32bitové ARMy
  3. 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
Poznámka: poslední instrukce odpovídá návratu z podprogramu.

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:

Cloud23

# 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:

# Příklad Stručný popis Cesta
1 asm01.go prázdná funkce main https://github.com/tisnik/go-root/blob/master/article53/
2 asm02.go volání funkce println https://github.com/tisnik/go-root/blob/master/article53/
3 asm03.go funkce Add https://github.com/tisnik/go-root/blob/master/article53/
       
4 add04/go.mod definice modulu pro projekt v Go https://github.com/tisnik/go-root/blob/master/article53/ad­d04/go.mod
5 add04/add.go kostra aplikace s hlavičkou funkce add https://github.com/tisnik/go-root/blob/master/article53/ad­d04/add.go
6 add04/add_amd64.s implementace funkce add v assembleru x86–64 https://github.com/tisnik/go-root/blob/master/article53/ad­d04/add_amd64.s
       
7 add05/go.mod definice modulu pro projekt v Go https://github.com/tisnik/go-root/blob/master/article53/ad­d05/go.mod
8 add05/add.go kostra aplikace s hlavičkou funkce add https://github.com/tisnik/go-root/blob/master/article53/ad­d05/add.go
9 add05/add_amd64.s použití pseudoregistru SP https://github.com/tisnik/go-root/blob/master/article53/ad­d05/add_amd64.s
       
10 add06/go.mod definice modulu pro projekt v Go https://github.com/tisnik/go-root/blob/master/article53/ad­d06/go.mod
11 add06/add.go kostra aplikace s hlavičkou funkce add https://github.com/tisnik/go-root/blob/master/article53/ad­d06/add.go
12 add06/add_amd64.s použití pseudoregistru FP https://github.com/tisnik/go-root/blob/master/article53/ad­d06/add_amd64.s
       
13 add07/go.mod definice modulu pro projekt v Go https://github.com/tisnik/go-root/blob/master/article53/ad­d07/go.mod
14 add07/add.go kostra aplikace s hlavičkou funkce add https://github.com/tisnik/go-root/blob/master/article53/ad­d07/add.go
15 add07/add_amd64.s pojmenování a použití výstupní hodnoty https://github.com/tisnik/go-root/blob/master/article53/ad­d07/add_amd64.s
       
16 add08/go.mod definice modulu pro projekt v Go https://github.com/tisnik/go-root/blob/master/article53/ad­d08/go.mod
17 add08/add.go kostra aplikace s hlavičkou funkce add https://github.com/tisnik/go-root/blob/master/article53/ad­d08/add.go
18 add08/add_amd64.s implementace funkce add v assembleru x86–64 https://github.com/tisnik/go-root/blob/master/article53/ad­d08/add_amd64.s
19 add08/add_arm.s implementace funkce add v assembleru ARM (32bit) https://github.com/tisnik/go-root/blob/master/article53/ad­d08/add_arm.s

20. Odkazy na Internetu

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

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.