Upload
internet
View
107
Download
3
Embed Size (px)
Citation preview
Phoenix Framework
Infra-estrutura para construção de compiladores
Guilherme Amaral [email protected]
Agenda
• Introdução
• Core Componentes
• Mapeando Estruturas
• Gerando Código
• Exemplo Prático
Phoenix - Proposta
VS
O que é o Phoenix?• Plataforma para desenvolvimento de compiladores e
ferramentas de análise e otimização• Escrito em C++/CLI (performance e extensibilidade)• Estrutura
– Robusta – rica API e ferramentas– Extensível – modelo de plugins– Flexível – readers, writers e IR
• Ecossistema rico– Academia – técnicas de construção e funcionamento de um
compilador– Pesquisas – laboratório para testes e pesquisas– Indústria – ferramentas customizadas e alteração de programas
• Futuros Compiliadores Microsoft– JITs, PreJITs e C++ Backend
Phoenix
Phoenix - Aplicações• Rápida implementação e teste de
novas técnicas de compilação• Estender o Visual Studio (Add-on),
para fazer análise de código– Control Flow Graph e Dynamic Slicing
• Instrumentação de código– Novas funcionalidades, AOP e profiling
• Construção de compiladores
Core Componentes
• Intermediate Representation (IR)– Instruções– Unidades e Lifetimes– Sistema de símbolos– Sistema de tipos
• Phases
• Plugins e Controls
Representação Intermediária (IR)
• Representação fortemente tipada• Vários níveis de abstração
– HIR (High-level IR) – totalmente independente– MIR (Mid-level IR) – dependente do ambiente de
execução– LIR (Low-lever IR) – totalmente dependente– EIR (Encoded IR) – código binário
• Represnta o fluxo de instruções de uma função como uma lista de instruções duplamente ligada
IR - Instruções
• Operador (opcode)
• Lista Operandos– Origem– Destino
• Apresenta explicitamente todos os recursos (efeitos colaterais)
IR - Instruções
IR - Operandos • Phx.IR.Operand• Recursos utilizados pelos operadores
– Dados, labels, referências.
• Armazena tipos e campos• VariableOperand, MemoryOperand,
ImmediateOperand, LabelOperand, FunctionOperand.
• Referência ou valor (Operand.isAdress)
IR - Instruções• Instruções reais
– Simples: operações lógicas e aritiméticas (ValueInstruction e CompareInstruction)
– Complexas: chamadas diretas/indiretas a procedimentos (CallInstruction)
– Control flow: desvios condicionais e incondicionais (BranchInstruction e SwitchInstruction)
– Summary: blocos inline (OutlineInstruction)
• Pseudo instruções– Label, Pragma, Data
OBS: API em dois níveis de Operandos e Instruções (propriedades)
IR – Instruções
Phx.IR.ImmediateOperand op1 = Phx.IR.ImmediateOperand.New(functionUnit, typeTable.Int32Type, 1);
Phx.IR.ImmediateOperand op2 = Phx.IR.ImmediateOperand.New(functionUnit, typeTable.Int32Type, 2);
Phx.IR.VariableOperand dst = Phx.IR.VariableOperand.New(functionUnit, sym.Type, sym);
Phx.IR.Instruction instr = Phx.IR.ValueInstruction.NewBinary (functionUnit, Phx.Common.Opcode.Add, dst, op1, op2);
IR – Unidades• Classe Phx.Unit• Unidades básicas de um programa
– Assemblies, programas, módulos, funções e dados
• Repositório IR– Tabela de símbolos– Instruções
• Representação Hierárquica
IR - Lifetimes
• Phx.Lifetimes
• Gerenciamento de memória do Phoenix
• Tempo de vida de objetos Phoenix
• Associado às unidades (Phx.Lifetimes.LifetimeKind)– Function, Module, Global, etc
IR – Unidades e LifetimesPhx.Lifetime lifetime =
Phx.Lifetime.New(Phx.LifetimeKind.Module, null);
Phx.Name moduleName = Phx.Name.New(lifetime, "main.obj");
Phx.ModuleUnit moduleUnit = Phx.ModuleUnit.New(lifetime, moduleName, null,
typeTable, runtime.Architecture, runtime);
IR – Sistema de Símbolos• Identificam entidades Phoenix
– Variáveis, tipos, labels, funções e módulos (importados e exportados)
• São armazenados em tabelas (Phx.Symbols.Table)
• Units/Table – escopo dos símbolos• Proxies - simbolos em outras tabelas• Lookup usando objetos (Phx.Symbols.Map)
– LocalIdMap, ExternIdMap, NameMap
IR – Sistema de Símbolos// Criando a tabela Phx.Symbols.Table moduleSymTable =
Phx.Symbols.Table.New(moduleUnit, 256, true);
// Adicionando mapeamentoPhx.Symbols.Map moduleNameMap = Phx.Symbols.NameMap.New(moduleSymTable, 256);
//Adicionando símboloPhx.Symbols.GlobalVariableSymbol xSym =
Phx.Symbols.GlobalVariableSymbol.New(moduleSymTable, externId, symName, symType, Phx.Symbols.Visibility.GlobalReference);
//Fazendo busca por nomePhx.Symbols.Symbol sym = moduleSymTable.NameMap.Lookup(symName);
IR – Criando um Proxy//Símbolo da variável externaPhx.Symbols.GlobalVariableSymbol varSym;
//Adicionar o símbolo externo a tabela de símbolo da função
Phx.Symbols.NonLocalVariableSymbol varProxy = Phx.Symbols.NonLocalVariableSymbol.New(SymbolTable, varSym);
IR – Sistema de Tipos• Fornece aparato para criação de tipos e
regras– Phx.Types.Type– Phx.Types.Check
• Checagem HIR,MIR e LIR• Phx.Types.Table
– Repositório de tipos– Métodos para obter principais tipos
• Dependente da arquitetura
IR – Tipos Phoenix• PrimitiveTypes - int, float, uknown• PointerTypes - Object, managed, unmanaged • AggregateTypes - Classes, structs, interfaces
– Metapropriedades (IsEnum, IsSealed, IsAbstract, etc)
• ArrayTypes - managed, unmanaged• FunctionTypes
– Lista de tipos dos parâmetros– Lista de tipos de resultados– Convenção de chamada (CDecl, ClrCall, FastCall)
Controls• Manipulam opções de linha de comando• Tipos
– Boolean (SetBooleanControl, ClearBooleanControl )– Integer (IntControl, CounterControl) – String (StringControl, PluginControl)
• Deve ser stático• Constituído:
– Nome– Valor default– Descrição– Localização
Phx.Controls.IntControl myIntCtrl = Phx.Controls.IntControl.New( "myint", 999, “exemplo IntControl", “exemplo.cs" );
Phases• Divisão em fases específicas
– Alocação de registradores, Otimizações, etc
• Opera sobre Unidades (FunctionUnit)• Infraestrutura de fases Phoenix:
– Phx.Phases.Phase– Phx.Phases.PhaseConfiguration – Phx.Phases.PrePhaseEvent e
Phx.Phases.PostPhaseEvent– Phx.Phases.List
Phases
Phases – Criando uma Phaseclass TestPhase : Phx.Phases.Phase {
public static TestPhase New (Phx.Phases.PhaseConfiguration config) {
TestPhase phase = new TestPhase; phase.Initialize(config, "MyPhase");
return phase;}override void Execute (Phx.Unit unit) {
Phx.Output.WriteLine("TestPhase {0}", unit.ToString()); }
}
Phases – Construindo Uma Lista de Fases
Phx.Phases.PhaseConfiguration config = Phx.Phases.PhaseConfiguration.New(
Phx.GlobalData.GlobalLifetime, "Hello Phases");
Phx.Phases.PhaseList phaseList = config.PhaseList;
phaseList.AppendPhase(TestPhase.New (config));
Config.PhaseList.DoPhaseList(unit)
Plugins• Plugins e Phases• Permite modificar programas Phoenix
– Adicionando, removendo ou alterando fases
• Deve ser um Dll gerenciado• Interface Phx.Plugin• Métodos RegisterObjects e BuildPhases• Phx.GlobalData.BuildPlugInPhases(config)
Plugins
Plugins – Criando um Pluginpublic class PlugIn : Phx.Plugin {
public override void RegisterObjects() { }
public override void BuildPhases (Phx.Phases.PhaseConfiguration config )
{ Phx.Phases.Phase encodingPhase; Phx.Phases.Phase testPhase = TestPhase.New(config);encodingPhase = config.PhaseList.FindByName("Encoding"); encodingPhase.InsertBefore(testPhase);
}}
Plugins - Demo
Plugins - Teste
#include <stdio.h>
main(){
int i = 2;i = 8+6*i;
printf ("i= %d",i);}
Main.c
cl –d2plugin:<caminho p/ o plugin> main.c
Mapeando Estruturas• O que foi visto?
– Criação de Fases e Plugins– Criação de Instruções (operandos e operadores)– Criação de Unidades– Criação de Tabela de símbolos e tipos
• Próximas estruturas– Construindo Variáveis– Atribuindo Valor a uma Variável– Construindo uma Função
Criando Variável Local// cria o símbolo da variável
Phx.Symbols.LocalVariableSymbol sym = Phx.Symbols.LocalVariableSymbol.New (
Table symbolTable,
uint externId,
Name symbolName,
Type symbolType,
StorageClass storageClass ) ; // static, argument, // parameter,
auto, illegal
sym.Alignment = Phx.Alignment.NaturalAlignment(sym.Type);
Atribuindo Valor a Variável// Cria os operandos de origem (expressão) e destino (variável) Phx.Symbols.Symbol sym = symbolTable.LookupByName(symbolName);Phx.IR.VariableOperand dst = Phx.IR.VariableOperand.New(
functionUnit, sym.Type, sym);Phx.IR.Operand src = Expression();
// Cria a instrução de atribuiçãoPhx.Opcode opcode = Phx.Common.Opcode.Assign;Phx.IR.Instruction instr = Phx.IR.ValueInstruction.New(func, opcode);instr.AppendSource(src);instr.AppendDestination(dst);
// Insere a instrução de atribuiçãofunctionUnit.LastInstruction.InsertBefore(instr);
Construindo Funções Phoenix
1. Criar o Tipo Função– Phx.Types.Table.GetFunctionType– Phx.Types.FunctionTypeBuilder
2. Símbolo, lifetime e unidade que representem a função
3. Tabela de símbolos da função
4. Instruções
Construindo Funções PhoenixPhx.Types.FunctionType funcType = typeTable.GetFunctionType(
Phx.Types.CallingConventionKind.CDecl, intType, null, null, null, null);
funcSym = Phx.Symbols.FunctionSymbol.New(moduleSymbolTable, 0, Phx.Name.New(lifetime, "_main"), funcType, Phx.Symbols.Visibility.GlobalDefinition);
Phx.Lifetime funcLifetime = Phx.Lifetime.New(Phx.LifetimeKind.Function, moduleUnit);
functionUnit = Phx.FunctionUnit.New(funcLifetime, funcSym, Phx.CodeGenerationMode.Native, typeTable,
moduleUnit.Architecture, moduleUnit.Runtime, moduleUnit, 1);
Construindo Funções PhoenixPhx.Symbols.Table funcSymTable =
Phx.Symbols.Table.New(functionUnit, 64, true);
Phx.IR.LabelInstruction enterInstruction = Phx.IR.LabelInstruction.New(functionUnit, Phx.Common.Opcode.EnterFunction, funcSym);
functionUnit.FirstInstruction.InsertAfter(enterInstruction);
Phx.IR.LabelOperand labelOperand = Phx.IR.LabelOperand.New(functionUnit, enterInstruction);
functionUnit.FirstInstruction.AppendLabelSource( Phx.IR.LabelOperandKind.Technical, labelOperand);
Construindo Funções Phoenix
\\ Inserir aqui as instruções que compõem a função
exitInstruction = Phx.IR.LabelInstruction.New(functionUnit,
Phx.Common.Opcode.ExitFunction);
functionUnit.LastInstruction.InsertBefore(
exitInstruction)
Gerando Código
• Diversas arquiteturas (GURL - Grand Unified Retargeting Language )
• LIR e EIR
• EncodePhase
• ObjectWriter
• COFF e PEWriter
Exemplo Prático
• Compilador para uma mini-linguagem de expressões– Uma ModuleUnit e uma FunctionUnit– Arquitetura e Runtime x86– Objeto linkável
• www.cin.ufpe.br/~gaa/phoenix/exemplo
Exemplo Prático1. www.cin.ufpe.br/~gaa/phoenix/exemplo
2. Abrir Visual Studio e criar um projeto vazio
3. Adicionar ao projeto os arquivos frontend.cs e compiler.cs
4. Adicionar referência as bibliotecas phoenix1. Phxd.dll
2. architecture-x86d.dll
3. runtime-vccrt-win32-x86d.dll
5. Habilitar unsafe code nas propriedades do projeto
Exemplo Prático
• Frontend.cs– Parser do código fonte– Messages de erro de sintaxe– Informação para debug
• Compiler.cs– Geração da Representação Intermediária– Geração de código
Exemplo Prático – Passos1. Inicialização do Framework2. Criação da Lista de Fases3. Símbolos externos4. Criação de dados inicializados5. Parser e criação da IR6. Execução da lista de fases7. Geração de código
ExecícioExtender MiniComp adicionando variáveis e atribuições• Opções:
– Definir escopo utilizando a hierarquia de unidades e tabela de símbolos
– Criar uma lista de mapeamentos. Exemplo : List<Phx.Symbols.NameMap>
• Observações:– Criação de Proxy para variáveis externas– Variáveis globais devem ser armazenadas na seção .data
Referências
• http://research.microsoft.com/phoenix/
• http://blogs.msdn.com/kangsu/default.aspx
• http://channel9.msdn.com/Showpost.aspx?postid=188589 (Jim Hogg)