Overview of GCC extended compilation pipeline, including specialized programs like the preprocessor, assembller and linker.
GCC segue a arquitetura de 3 Estágios típica de compiladores multi-linguagem e multi-CPU. Todas as árvores do programa são convertidas para uma representação abstrata comum no “middle end”, permitindo otimização de código e instalações de geração de código binário para ser compartilhado por todas as linguagens.,
a interface externa do GCC segue as convenções Unix. Os usuários invocam um programa de driver específico da linguagem (gcc
para C, g++
para C++, etc.), que interpreta argumentos de comando, chama o compilador real, executa o conjunto na saída, e então opcionalmente executa o linker para produzir um executável binário completo.
cada um dos compiladores de linguagem é um programa separado que lê código fonte e outputs código Máquina. Todos têm uma estrutura interna comum., Um front end por linguagem analisa o código fonte nessa linguagem e produz uma árvore de sintaxe abstrata (“árvore” para abreviar).
estes são, se necessário, convertidos para a representação de entrada do meio-termo, chamada forma genérica; o meio-termo então gradualmente transforma o programa em sua forma final. Otimizações de Compiladores e técnicas de análise de código estático (como FORTIFY_SOURCE, uma diretiva de compilador que tenta descobrir alguns fluxos de buffer) são aplicadas ao código., Estes trabalhos funcionam em várias representações, principalmente a representação GIMPLE independente de arquitetura e a representação RTL dependente de arquitetura. Finalmente, o código de máquina é produzido usando a combinação de padrões específicos de arquitetura originalmente baseada em um algoritmo de Jack Davidson e Chris Fraser.
GCC foi escrito principalmente em C, exceto para partes da extremidade dianteira da Ada. A distribuição inclui as bibliotecas padrão para Ada, C++ e Java, cujo código é escrito principalmente nessas linguagens., Em algumas plataformas, a distribuição também inclui uma biblioteca de baixo nível de tempo de execução, libgcc, escrito em uma combinação de C independente de máquina e código de máquina específico do processador, projetado principalmente para lidar com operações aritméticas que o processador alvo não pode executar diretamente.
GCC usa muitas ferramentas padrão em sua construção, incluindo Perl, Flex, Bison, e outras ferramentas comuns. Além disso, ele atualmente requer três bibliotecas adicionais para estar presente, a fim de construir: GMP, MPC e MPFR.
Em maio de 2010, o comitê diretor do GCC decidiu permitir o uso de um compilador C++ para compilar o GCC., O compilador foi concebido para ser escrito principalmente em C mais um subconjunto de recursos de c++. Em particular, isso foi decidido para que os desenvolvedores do GCC pudessem usar os recursos destrutivos e genéricos do C++.
Em agosto de 2012, o comitê diretor do GCC anunciou que o GCC agora usa C++ como sua linguagem de implementação. Isto significa que para construir o GCC a partir de fontes, é necessário um compilador C++ que compreenda a norma ISO/IEC C++03.,
Front endsEdit
Front ends consistem em pré-processamento, análise lexical, análise sintática (análise sintática) e análise semântica. Os objetivos do compilador front ends são aceitar ou rejeitar programas candidatos de acordo com a gramática e semântica da linguagem, identificar erros e lidar com representações de programas válidas para fases posteriores do compilador. Este exemplo mostra os passos lexer e parser executados para um programa simples escrito em C.,
cada extremidade frontal usa um analisador para produzir a árvore de sintaxe abstrata de um dado ficheiro-fonte. Devido à abstração de árvore de sintaxe, arquivos de origem de qualquer uma das diferentes linguagens suportadas podem ser processados pela mesma extremidade traseira. GCC começou a usar parsers LALR gerados com Bison, mas gradualmente mudou para parsers recursivos escritos à mão para C++ em 2004, e para C e Objective-C em 2006. A partir de 2021 todos os Front ends usam parsers recursivos escritos à mão.
até GCC 4.,0 a representação em árvore do programa não era totalmente independente do processador alvo. O Significado de uma árvore era um pouco diferente para diferentes front-ends de linguagem, e front-ends poderia fornecer seus próprios códigos de árvore. Isto foi simplificado com a introdução de GENERIC e GIMPLE, duas novas formas de árvores independentes da linguagem que foram introduzidas com o advento do GCC 4.0. Genérico é mais complexo, baseado no GCC 3.representação intermédia do X Java front end. GIMPLE é um genérico simplificado, no qual várias construções são baixadas para múltiplas instruções GIMPLE., O C, C++ e Java front ends produzem GENERIC diretamente no front end. Outras front-ends em vez disso têm representações intermediárias diferentes depois de analisar e converter estes para genéricos.
em ambos os casos, o chamado “gimplifier” então converte esta forma mais complexa na forma GIMPLE simples baseada em SSA que é a linguagem comum para um grande número de poderosas linguagens-e arquitetura-independente de otimizações globais (escopo de funções).,
genérico e GIMPLEEdit
genérico é uma linguagem de representação intermediária usada como um “meio-termo” ao compilar o código-fonte em binários executáveis. Um subconjunto, chamado GIMPLE, é alvo por todas as extremidades frontais do GCC.
o estágio médio do GCC faz toda a análise e otimização do Código, trabalhando independentemente da linguagem compilada e da arquitetura alvo, começando a partir da representação genérica e expandindo-a para registrar a linguagem de transferência (RTL)., A representação genérica contém apenas o subconjunto das construções de programação imperativa otimizadas pelo meio-termo.
ao transformar o código fonte em GIMPLE, expressões complexas são divididas em um código de três endereços usando variáveis temporárias. Esta representação foi inspirada pela representação simples proposta no compilador McCAT por Laurie J. Hendren para simplificar a análise e otimização de programas imperativos.,
OptimizationEdit
a Otimização pode ocorrer durante qualquer fase de compilação; no entanto, a maioria das otimizações são realizadas após a sintaxe e a semântica de análise de front-end e antes da geração de código de back-end; assim, um comum, mesmo que um pouco contraditório, nome para esta parte do compilador é o “meio-fim.”
the exact set of GCC optimizations varies from release to release as it develops, but includes the standard algorithms, such as loop optimization, jump threading, common subexpression elimination, instruction scheduling, and so forth., As otimizações RTL são de menor importância com a adição de otimizações globais baseadas em SSA em árvores GIMPLE, como as otimizações RTL têm um escopo muito mais limitado, e têm menos informações de alto nível.
algumas dessas otimizações realizadas neste nível incluem eliminação de código morto, eliminação de redundância parcial, numeração global de valores, propagação constante condicional escassa, e substituição escalar de agregados. Otimizações baseadas na dependência de Array, tais como vetorização automática e paralelização automática também são realizadas. Otimização guiada pelo perfil também é possível.,
EndEdit de volta
The GCC’s back end is partly specified by preprocessor macros and functions specific to a target architecture, for instance to define its endianness, word size, and calling conventions., A parte frontal do back end usa-os para ajudar a decidir a geração RTL, por isso, embora a RTL do GCC seja nominalmente independente do processador, a sequência inicial de instruções abstratas já está adaptada ao alvo. A qualquer momento, as instruções reais RTL que formam a representação do programa têm que cumprir com a descrição da máquina da arquitetura alvo.
o ficheiro de descrição da máquina contém padrões RTL, juntamente com restrições operárias e excertos de código para a saída da montagem final., The constraints indicate that a particular RTL pattern might only apply (for example) to certain hardware registers, or (for example) allow immediate operand offets of only a limited size (e.g. 12, 16, 24, … bit offets, etc.). Durante a geração RTL, as restrições para a arquitetura de destino indicada são verificadas. A fim de emitir um determinado trecho de RTL, ele deve corresponder a um (ou mais) dos padrões de RTL no arquivo de descrição da máquina, e satisfazer as restrições para esse padrão; caso contrário, seria impossível converter o RTL final em código de máquina.,
no final da compilação, o RTL válido é reduzido a uma forma estrita na qual cada instrução se refere a registros reais de máquinas e um padrão a partir do arquivo de descrição de máquina do alvo. Formar RTL estrito é uma tarefa complicada; um passo importante é a alocação de registro, onde os registros reais de hardware são escolhidos para substituir os pseudo-registradores inicialmente atribuídos. Isto é seguido por uma fase de” recarga”; qualquer pseudo-registradores que não foram atribuídos um registro real de hardware são ‘derramados’ para a pilha, e RTL para realizar este derramamento é gerado., Da mesma forma, as compensações que são muito grandes para caber em uma instrução real devem ser quebradas e substituídas por sequências RTL que obedecerão às restrições de deslocamento.
na fase final, o código da máquina é construído chamando um pequeno trecho de código, associado a cada padrão, para gerar as instruções reais a partir do conjunto de instruções do alvo, usando os registros finais, compensações e endereços escolhidos durante a fase de recarga. O excerto de geração de montagem pode ser apenas uma cadeia, caso em que uma simples substituição de cadeia dos Registradores, compensações e/ou endereços na cadeia é realizada., O excerto de geração de montagem também pode ser um bloco curto de código C, realizando algum trabalho adicional, mas finalmente retornando uma string contendo o código de montagem válido.