$30 off During Our Annual Pro Sale. View Details »

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

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

    View Slide

  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

    View Slide

  3. Não dá tempo para tudo….

    View Slide

  4. 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), …

    View Slide

  5. 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?

    View Slide

  6. 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

    View Slide

  7. 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

    View Slide

  8. 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

    View Slide

  9. 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

    View Slide

  10. 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, …

    View Slide

  11. 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?

    View Slide

  12. O que o compilador do Go faz?
    RUNTIME
    ● garbage collector
    ● stack management
    ● concurrency
    ● ...

    View Slide

  13. 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

    View Slide

  14. Mas como isso acontece?

    View Slide

  15. 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

    View Slide

  16. 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?

    View Slide

  17. 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

    View Slide

  18. 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?

    View Slide

  19. 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

    View Slide

  20. 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 [2] (y[int])
    v4 (?) = Const64 [1] (x[int])
    ...
    v7 (8) = Add64 v4 v5
    ...
    v41 (275) = StaticCall {fmt.Fprintln} [64] v40
    ...
    Semântica?

    View Slide

  21. 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

    View Slide

  22. Otimizador
    v4 (?) = Const64 [1] (x[int])
    v5 (?) = Const64 [2] (y[int])
    v7 (8) = Add64 v4 v5
    v41 (275) = StaticCall {fmt.Fprintln} [64] v40
    ...
    ...
    ...
    v7 (8) = Const64 [3]
    v41 (275) = StaticCall {fmt.Fprintln} [64] v40
    ...
    ...

    View Slide

  23. 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

    View Slide

  24. Gerador de código de máquina
    v7 (8) = Const64 [3]
    v41 (275) = StaticCall {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

    View Slide

  25. Falar é fácil…. show me the code :-)
    ● Dois hacks feitos
    ○ while
    ○ if ternário (tem proposta para Go 2.0)

    View Slide

  26. Hacking (while)

    View Slide

  27. Hacking (while) - go run while.go

    View Slide

  28. Hacking 1 (while) - Novo token
    tokens.go

    View Slide

  29. Hacking 1 (while) - Novo statement
    parser.go
    Statement???

    View Slide

  30. Hacking 1 (while) - func whileStmt
    ForStmt ???
    func whileStmt em
    parser.go

    View Slide

  31. Hacking 1 (while) - Define while tem condition
    Ajuste para parser
    entender que while
    tem “condition”

    View Slide

  32. Hacking 1 (while) - gc usa perfect hash
    scanner.go

    View Slide

  33. 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

    View Slide

  34. 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...

    View Slide

  35. Hacking 2 (if ternário) - Expectativa...

    View Slide

  36. 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

    View Slide

  37. Como eu queria resolver...

    View Slide

  38. LL(1)
    IDENT…
    SimpleStmt!
    ATTR…
    DEFINE!
    IfStmt não é
    compatível com
    SimpleStmt :-/
    INT

    View Slide

  39. Expectativa vs realidade...

    View Slide

  40. Hacking 2 (if ternário) - Realidade :-(
    Porque assim
    funciona???

    View Slide

  41. Hacking 2 (if ternário) - go run ternary_if.go

    View Slide

  42. 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

    View Slide

  43. Hacking 2 (if ternário) - Novo statement
    parser.go

    View Slide

  44. Hacking 2 (if ternário) - Novo statement
    let =
    if
    else

    View Slide

  45. 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

    View Slide

  46. 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 :-)

    View Slide

  47. 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

    View Slide

  48. Dúvidas????
    Alex Sandro Garzão
    [email protected]
    https://www.linkedin.com/in/alexgarzao/
    https://github.com/alexgarzao
    https://twitter.com/alexgarzao

    View Slide