87
Técnicas Modernas em Compiladores … e como esse conhecimento pode transformar você em um programador melhor. Elemar Júnior @elemarjr [email protected] [email protected] elemarjr.com

Técnicas Modernas em CompiladoresTécnicas Modernas em Compiladores … e como esse conhecimento pode transformar você em um programador melhor. Elemar Júnior @elemarjr [email protected]

  • Upload
    others

  • View
    8

  • Download
    0

Embed Size (px)

Citation preview

  • Técnicas Modernas emCompiladores

    … e como esse conhecimento pode transformarvocê em um programador melhor.

    Elemar Júnior@elemarjr

    [email protected]

    [email protected]

    elemarjr.com

    mailto:[email protected]:[email protected]://elemarjr.com/

  • Olá, eu sou Elemar Jr

  • CompiladorCódigo-fonte Executável

  • São as cicatrizes que contam a história do guerreiro.

  • Era uma vez...

  • $PW$-2*#ESP_LAT#

  • $PW$-2*#ESP_LAT#

  • $PW$-2*#ESP_LAT#

    Lexer

  • -

    Lexer Parser

    $PW$

    2

    *

    #ESP_LAT#

  • -

    $PW$

    2

    *

    #ESP_LAT#

    Lexer ParserType

    Checker

  • -

    $PW$

    2

    *

    #ESP_LAT#

    Lexer ParserType

    Checker

    Frontend

  • Entender como o compilador da sua linguagem funciona te ajuda a entender

    algumas características da linguagem.

  • C++, F#, C# ...

  • -

    $PW$

    2

    *

    #ESP_LAT#

    Lexer ParserType

    CheckerCode

    Generator

    Frontend

  • -

    $PW$

    2

    *

    #ESP_LAT#

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

  • Backend

    -

    $PW$

    2

    *

    #ESP_LAT#

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

    Análise estática

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

    Análise estática

    Code fixing

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

    Análise estática

    Code fixing Edit & Continue

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

    Análise estática

    Code fixing Edit & Continue

  • Compilador

    Código-fonte Executável

    Backend

    Lexer ParserType

    CheckerCode

    GeneratorEmitter

    Frontend

    Colorir o código em um editor

    Identificar erros simples

    de programação

    Completar código. (listas)

    Go todefinition

    Code References

    Análise estática

    Code fixing Edit & Continue

  • ...

  • ROSLYNhttps://github.com/dotnet/roslyn

    https://github.com/dotnet/roslyn

  • ...

  • Roslyn é Thread-Safe!

  • CodeCracker

  • ...

  • static void Main(string[] args){

    SyntaxTree tree = CSharpSyntaxTree.ParseText(@"using System;using System.Collections;using System.Linq;using System.Text;

    namespace HelloWorld{

    class Program{

    static void Main(string[] args){

    Console.WriteLine(""Hello, World!"");}

    }}");

    var root = (CompilationUnitSyntax)tree.GetRoot();var firstMember = root.Members[0];var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember;var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0];var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0];var argsParameter = mainDeclaration.ParameterList.Parameters[0];

    }

  • var firstParameters = from methodDeclaration in root.DescendantNodes()

    .OfType()where methodDeclaration.Identifier.ValueText == "Main"select methodDeclaration.ParameterList.Parameters.First();

    var argsParameter2 = firstParameters.Single();

  • ...

  • readonly string mainCs = @"using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;

    namespace GettingFunWithRoslyn{

    class Program{

    static void Main(string[] args){}

    }}";

  • readonly string @class = @"namespace GettingFunWithRoslyn{

    public class MyClass{

    public string MyProperty { get; set; }public void MyMethod() { }

    }}";

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • Todos os objetos Roslyn são imutáveis e isso habilita o reuso

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • Fácil acessar “nodos filhos” a partir de um “nodo pai”, fácil acessar um “nodo

    pai” a partir de um “nodo filho”.

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • Possível chegar no texto a partir do nodo na AST

  • static void Main(string[] args){

    var mainCsSyntaxTree1 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree2 = CSharpSyntaxTree.ParseText(mainCs);var mainCsSyntaxTree3 = CSharpSyntaxTree.ParseText(mainCs);

    var @classSyntaxTree1 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree2 = CSharpSyntaxTree.ParseText(@class);var @classSyntaxTree3 = CSharpSyntaxTree.ParseText(@class);

    }

  • ...

  • Alocações importam

  • Delays na interface geralmente ocorrem por atividades do GC.

  • ...

  • Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax

  • public static SyntaxTree ParseText(string text,CSharpParseOptions options = null,string path = "",Encoding encoding = null,CancellationToken cancellationToken = default(CancellationToken))

    {return ParseText(SourceText.From(text, encoding), options, path, cancellationToken);

    }

  • public static SyntaxTree ParseText(SourceText text,CSharpParseOptions options = null,string path = "",CancellationToken cancellationToken = default(CancellationToken))

    {if (text == null){

    throw new ArgumentNullException(nameof(text));}

    options = options ?? CSharpParseOptions.Default;

    using (var lexer = new InternalSyntax.Lexer(text, options)){

    using (var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, cancellationToken: cancellationToken))

    {var compilationUnit =(CompilationUnitSyntax)parser.ParseCompilationUnit().CreateRed();var tree = new ParsedSyntaxTree(text, text.Encoding, text.ChecksumAlgorithm, path,

    options, compilationUnit, parser.Directives);tree.VerifySource();return tree;

    }}

    }

  • 2 * 2 - 2 * 2

    2 * 2 2 * 2-

    2 2 * 2 2 *

    The Red Tree

  • 2 * 2 - 2 * 2

    2 * 2

    -

    2

    *

    The Green Tree

  • ...

  • public Lexer(SourceText text, CSharpParseOptions options, boolallowPreprocessorDirectives = true)

    : base(text){

    Debug.Assert(options != null);

    _options = options;_builder = new StringBuilder();_identBuffer = new char[32];_cache = new LexerCache();_createQuickTokenFunction = this.CreateQuickToken;_allowPreprocessorDirectives = allowPreprocessorDirectives;

    }

  • internal LexerCache(){

    _triviaMap = TextKeyedCache.GetInstance();_tokenMap = TextKeyedCache.GetInstance();_keywordKindMap = s_keywordKindPool.Allocate();

    }

  • private TextKeyedCache(ObjectPool pool){

    _pool = pool;_strings = new StringTable();

    }

    private readonly ObjectPool _pool;private static readonly ObjectPool s_staticPool = CreatePool();

    private static ObjectPool CreatePool(){

    ObjectPool pool = null;pool = new ObjectPool(() => new TextKeyedCache(pool), Environment.ProcessorCount * 4);return pool;

    }

    public static TextKeyedCache GetInstance(){

    return s_staticPool.Allocate();}

    public void Free(){

    _pool.Free(this);}

  • internal SyntaxToken LookupToken(char[] textBuffer,int keyStart,int keyLength,int hashCode,Func createTokenFunction)

    {var value = _tokenMap.FindItem(textBuffer, keyStart, keyLength, hashCode);

    if (value == null){

    value = createTokenFunction();_tokenMap.AddItem(textBuffer, keyStart, keyLength, hashCode, value);

    }else{

    #if COLLECT_STATSHit();

    #endif}

    return value;}

  • internal T FindItem(char[] chars, int start, int len, int hashCode){

    // capture array to avoid extra range checksvar arr = _localTable;var idx = LocalIdxFromHash(hashCode);

    var text = arr[idx].Text;

    if (text != null && arr[idx].HashCode == hashCode){

    if (StringTable.TextEquals(text, chars, start, len)){

    return arr[idx].Item;}

    }

    SharedEntryValue e = FindSharedEntry(chars, start, len, hashCode);if (e != null){

    // PERF: the following code does element-wise assignment of a struct// because current JIT produces better code compared to// arr[idx] = new LocalEntry(...)arr[idx].HashCode = hashCode;arr[idx].Text = e.Text;

    var tk = e.Item;arr[idx].Item = tk;

    return tk;}

    return null;}

  • internal void AddItem(char[] chars, int start, int len, int hashCode, T item){

    var text = _strings.Add(chars, start, len);

    // add to the shared table first (in case someone looks for same item)var e = new SharedEntryValue(text, item);AddSharedEntry(hashCode, e);

    // add to the local table toovar arr = _localTable;var idx = LocalIdxFromHash(hashCode);arr[idx].HashCode = hashCode;arr[idx].Text = text;arr[idx].Item = item;

    }

  • private void AddSharedEntry(int hashCode, SharedEntryValue e){

    var arr = _sharedTableInst;int idx = SharedIdxFromHash(hashCode);

    // try finding an empty spot in the bucket// we use quadratic probing here// bucket positions are (n^2 + n)/2 relative to the masked hashcodeint curIdx = idx;for (int i = 1; i < SharedBucketSize + 1; i++){

    if (arr[curIdx].Entry == null){

    idx = curIdx;goto foundIdx;

    }

    curIdx = (curIdx + i) & SharedSizeMask;}

    // or pick a random victim within the bucket range// and replace with new entryvar i1 = NextRandom() & SharedBucketSizeMask;idx = (idx + ((i1 * i1 + i1) / 2)) & SharedSizeMask;

    foundIdx:arr[idx].HashCode = hashCode;Volatile.Write(ref arr[idx].Entry, e);

    }

  • ...

  • Técnicas Modernas emCompiladores

    … e como esse conhecimento pode transformarvocê em um programador melhor.

    Elemar Júnior@elemarjr

    [email protected]

    [email protected]

    elemarjr.com

    mailto:[email protected]:[email protected]://elemarjr.com/