Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Desmistificando o compilador Go: A jornada do f...

Desmistificando o compilador Go: A jornada do func main() até o go run

Slides da minha apresentação na GopherCon Brasil - Setembro - 2019

Alex Sandro Garzão

September 27, 2019
Tweet

Other Decks in Technology

Transcript

  1. Desmistificando o compilador Go: A jornada do func main() até

    o go run Alex Sandro Garzão GopherConBR 2019
  2. Agenda (ou alinhando expectativas) O que é um compilador Compilador

    Go (Histórico, arquitetura, etapas de compilação, …) Hacking (Alegrias, tristezas, trechos de código, …) Considerações finais
  3. Quem sou • Engenheiro de Software na Zenvia • Minhas

    paixões (algumas) ◦ Meus filhos, livros, cinema, andar de bike ◦ Linguagens de programação, compiladores, máquinas virtuais ◦ Soluções simples para domínios complexos, algoritmos, processamento em tempo real ◦ Código bem feito :-) • Já atuei com “Toy compilers” ◦ HoloC, UbiC, Pascal para bytecode JVM, ST para ASM (80C51), …
  4. Vamos nos conhecer um pouco... Quem tem conhecimento sobre como

    um compilador funciona? Quem já atuou na implementação de um compilador (toy ou não)? Quem já olhou e/ou analisou o código de um compilador?
  5. O que é um compilador? “Um compilador é um programa

    que consegue ler um programa em uma linguagem (linguagem de origem) e traduzir para um programa equivalente em outra linguagem (linguagem destino)” [Aho, 2a edição] Compilador Linguagem de origem Linguagem de destino
  6. Arquitetura clássica de um compilador Frontend Código fonte Middleend Backend

    Representação intermediária Código de máquina Representação intermediária
  7. Arquitetura do GCC RTL RTL Optimizer Final Code Generation Assembly

    Back End GENERIC C C++ Java Fortran Front End GIMPLE Inter Procedural Optimizer SSA Optimizer Middle End GCCGO
  8. Arquitetura do LLVM LLVM X86 Backend LLVM PowerPC Backend LLVM

    ARM Backend X86 PowerPC ARM Back End Clang C/C++/ObjC Frontend C llvm-gcc Frontend Fortran GHC Frontend Haskell go-llvm Frontend Front End LLVM Optimizer LLVM IR LLVM IR Middle End
  9. Quem é o gc? • gc (minúsculo) é o compilador

    de go • Sem confusões com o GC (maiúsculo), que é o Garbage Collector • Pacote go/lexer não faz parte do gc ◦ go/lexer é usado por ferramentas como gofmt, golint, …
  10. Histórico do gc SETEMBRO 2007 JANEIRO 2008 METADE 2008 NOVEMBRO

    2009 AGOSTO 2015 Metas de uma nova linguagem Compilador escrito em C Compilador que gerava código C Versão pública do projeto ooo Porquê? Compilador escrito em Go (e asm) Como? Problemas? Só agora?
  11. O que o compilador do Go faz? RUNTIME • garbage

    collector • stack management • concurrency • ...
  12. Backend Middleend Arquitetura do gc / etapas de compilação Analisador

    léxico Stream de caracteres (Programa em Go) Stream de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend
  13. Analisador léxico Analisador léxico Stream de caracteres (Programa em Go)

    Stream de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend Middleend Backend
  14. Analisador léxico FUNC “func” IDENT “main” LPAREN “(” RPAREN “)”

    LBRACE “{” IDENT “y” ATTR “:=” INT “2” RBRACE “}” IDENT “x” ATTR “:=” INT “1” IDENT “fmt” DOT “.” IDENT “Println” LPAREN “(” RPAREN “)” IDENT “x” ADDOP “+” IDENT “y” Espaçamento? Tabulação? Salto de linha?
  15. Analisador sintático Analisador léxico Stream de caracteres (Programa em Go)

    Stream de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend Middleend Backend
  16. Analisador sintático FuncDecl [main] CallExpr SelectorExpr Name [fmt] Name [Println]

    FUNC “func” IDENT “main” LPAREN “(” RPAREN “)” LBRACE “{” IDENT “y” ATTR “:=” INT “2” RBRACE “}” IDENT “x” ATTR “:=” INT “1” IDENT “fmt” DOT “.” IDENT “Println” LPAREN “(” RPAREN “)” IDENT “x” ADDOP “+” IDENT “y” VarDecl [:=] Var [y] IntLiteral [2] VarDecl [:=] Var [x] IntLiteral [1] AddOp [+] Var [x] Var [y] AST? Delimitadores?
  17. Gerador de código intermediário Analisador léxico Stream de caracteres (Programa

    em Go) Stream de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend Middleend Backend
  18. Gerador de código intermediário FuncDecl [main] CallExpr SelectorExpr Name [fmt]

    Name [Println] VarDecl [:=] Var [y] IntLiteral [2] AddOp [+] Var [x] Var [y] VarDecl [:=] Var [x] IntLiteral [1] v5 (?) = Const64 <int> [2] (y[int]) v4 (?) = Const64 <int> [1] (x[int]) ... v7 (8) = Add64 <int> v4 v5 ... v41 (275) = StaticCall <mem> {fmt.Fprintln} [64] v40 ... Semântica?
  19. Otimizador Analisador léxico Stream de caracteres (Programa em Go) Stream

    de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend Middleend Backend
  20. Otimizador v4 (?) = Const64 <int> [1] (x[int]) v5 (?)

    = Const64 <int> [2] (y[int]) v7 (8) = Add64 <int> v4 v5 v41 (275) = StaticCall <mem> {fmt.Fprintln} [64] v40 ... ... ... v7 (8) = Const64 <int> [3] v41 (275) = StaticCall <mem> {fmt.Fprintln} [64] v40 ... ...
  21. Gerador de código de máquina Analisador léxico Stream de caracteres

    (Programa em Go) Stream de tokens Analisador sintático Árvore sintática abstrata anotada Gerador de código intermediário Otimizador SSA específica Gerador de código de máquina Código de máquina Representação intermediária (SSA genérica) Frontend Middleend Backend
  22. Gerador de código de máquina v7 (8) = Const64 <int>

    [3] v41 (275) = StaticCall <mem> {fmt.Fprintln} [64] v40 ... ... # hello.go 00000 (5) TEXT "".main(SB), ABIInternal ... v8 00007 (+8) MOVQ $3, (SP) v9 00008 (8) CALL runtime.convT64(SB) ... # $GOROOT/src/fmt/print.go ... v41 00036 (275) CALL fmt.Fprintln(SB) b4 00037 (?) RET 00038 (?) END
  23. Falar é fácil…. show me the code :-) • Dois

    hacks feitos ◦ while ◦ if ternário (tem proposta para Go 2.0)
  24. Hacking 1 (while) - Define while tem condition Ajuste para

    parser entender que while tem “condition”
  25. Hacking 1 (while) - Gerando o novo gc • Gerar

    os tokens novamente ◦ $ stringer -type token -linecomment tokens.go • Gerar os binários do compilador ◦ $ ./all.bash (gera binário, bibliotecas, executa testes) • $ ./bin/go run hacks/while.go
  26. Hacking 1 (while) - O que NÃO foi feito? •

    Não foi criado WhileStmt • Analisador sintático transforma o while em um ForStmt • As outras etapas acham que é um for...
  27. Qual a “treta”? • gc é LL(1) ◦ L: parser

    Left to Right ◦ L: derivação mais à esquerda ◦ (1): indica o look ahead (número de símbolos parser utiliza para tomar uma decisão) • “Ser” LL1 não é ruim, e para Go está certo... • Porém, eu queria transformar o if ternário em um “if” normal durante o parser
  28. Hacking 2 (if ternário) - O que foi feito? •

    Sequência similar ao while… ◦ Novo token (let) ◦ Novo statement ◦ Gerar os tokens novamente ◦ Gerar a nova versão do compilador
  29. Hacking 2 (if ternário) - Novo statement let <id> =

    <SimpleStmt> if <Cond> else <SimpleStmt>
  30. Resumindo…. • gc é um projeto MUITO legal! • No

    geral, bem legível • Boa documentação • Tem o “legado” da conversão direta de C para Go • Primeiro compilador “real” que tudo é óbvio :-D
  31. Coisas que (NÃO) vale a pena se preocupar • Foque

    em clean code, código organizado… • Programe para outros humanos entenderem, e não para máquinas ◦ Código, no geral, é escrito uma vez, mas lido milhares de vezes • Pode deixar que o gc lida MUITO bem com otimizações :-)
  32. Referências • Links interessantes sobre o gc ◦ Histórico ◦

    Repositório do go: gc está aqui ◦ Introduction to the go compiler ◦ Go contribution guide ◦ SSA rules • Porque reescreveram o gc em go e como • Adding a new statement: part 1 and part 2 • Go toolchain • Hacking go compiler internals • Para gerar SSA: GOSSAFUNC=main go tool compile hello.go (gera ssa.html) • Compiler explorer • Aho, 2nd editon