Post on 04-Feb-2021
Técnicas Modernas emCompiladores
… e como esse conhecimento pode transformarvocê em um programador melhor.
Elemar Júnior@elemarjr
falecom@elemarjr.com
elemarjr@ravendb.net
elemarjr.com
mailto:falecom@elemarjr.commailto:elemarjr@ravendb.nethttp://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
falecom@elemarjr.com
elemarjr@ravendb.net
elemarjr.com
mailto:falecom@elemarjr.commailto:elemarjr@ravendb.nethttp://elemarjr.com/