Coleção de Compiladores GNU
A GNU Compiler Collection (GCC) é um compilador otimizador produzido pelo Projeto GNU que suporta várias linguagens de programação, arquiteturas de hardware e sistemas operacionais. A Free Software Foundation (FSF) distribui o GCC como software livre sob a GNU General Public License (GNU GPL). O GCC é um componente chave da cadeia de ferramentas GNU e o compilador padrão para a maioria dos projetos relacionados ao GNU e ao kernel do Linux. Com cerca de 15 milhões de linhas de código em 2019, o GCC é um dos maiores programas gratuitos existentes. Ele desempenhou um papel importante no crescimento do software livre, tanto como ferramenta quanto como exemplo.
Quando foi lançado pela primeira vez em 1987 por Richard Stallman, o GCC 1.0 foi nomeado o GNU C Compiler, pois lidava apenas com a linguagem de programação C. Foi estendido para compilar C++ em dezembro daquele ano. Os front-ends foram posteriormente desenvolvidos para Objective-C, Objective-C++, Fortran, Ada, D e Go, entre outros. As especificações OpenMP e OpenACC também são suportadas nos compiladores C e C++.
O GCC foi portado para mais plataformas e arquiteturas de conjunto de instruções do que qualquer outro compilador e é amplamente implantado como uma ferramenta no desenvolvimento de software livre e proprietário. O GCC também está disponível para muitos sistemas embarcados, incluindo chips baseados em ARM e baseados em Power ISA.
Além de ser o compilador oficial do sistema operacional GNU, o GCC foi adotado como compilador padrão por muitos outros sistemas operacionais de computador modernos semelhantes ao Unix, incluindo a maioria das distribuições do Linux. A maioria dos sistemas operacionais da família BSD também mudou para GCC logo após seu lançamento, embora, desde então, FreeBSD, OpenBSD e Apple macOS tenham mudado para o compilador Clang, em grande parte devido a razões de licenciamento. O GCC também pode compilar código para Windows, Android, iOS, Solaris, HP-UX, AIX e DOS.
História
No final de 1983, em um esforço para inicializar o sistema operacional GNU, Richard Stallman perguntou a Andrew S. Tanenbaum, o autor do Amsterdam Compiler Kit (também conhecido como Free University Compiler Kit) pela permissão para usar esse software para GNU. Quando Tanenbaum o avisou que o compilador não era gratuito e que apenas a universidade era gratuita, Stallman decidiu trabalhar em um compilador diferente. Seu plano inicial era reescrever um compilador existente do Lawrence Livermore National Laboratory de Pastel para C com alguma ajuda de Len Tower e outros. Stallman escreveu um novo front-end C para o compilador Livermore, mas depois percebeu que exigia megabytes de espaço de pilha, uma impossibilidade em um sistema Unix 68.000 com apenas 64 KB, e concluiu que teria que escrever um novo compilador do zero. Nenhum do código do compilador Pastel acabou no GCC, embora Stallman tenha usado o front-end C que ele havia escrito.
O GCC foi lançado pela primeira vez em 22 de março de 1987, disponível por FTP do MIT. Stallman foi listado como o autor, mas citou outros por suas contribuições, incluindo Tower para "partes do analisador, gerador RTL, definições RTL e da descrição da máquina Vax", Jack Davidson e Christopher W. Fraser para o ideia de usar RTL como uma linguagem intermediária e Paul Rubin por escrever a maior parte do pré-processador. Descrito como o "primeiro hit de software livre" por Peter H. Salus, o compilador GNU chegou exatamente no momento em que a Sun Microsystems estava separando suas ferramentas de desenvolvimento de seu sistema operacional, vendendo-as separadamente a um preço combinado mais alto do que o pacote anterior, o que levou muitos dos usuários da Sun para comprar ou baixar o GCC em vez das ferramentas do fornecedor. Enquanto Stallman considerava o GNU Emacs como seu projeto principal, em 1990, o GCC suportava treze arquiteturas de computador, superava vários compiladores de fornecedores e era usado comercialmente por várias empresas.
Forquilha EGCS
Como o GCC era licenciado sob a GPL, os programadores que desejavam trabalhar em outras direções—particularmente aqueles que escreviam interfaces para linguagens diferentes de C—estavam livres para desenvolver seu próprio fork do compilador, desde que atendessem aos termos da GPL, incluindo seus requisitos para distribuir o código-fonte. Múltiplos garfos se mostraram ineficientes e difíceis de manejar, no entanto, e a dificuldade em fazer com que o trabalho fosse aceito pelo projeto oficial do GCC foi muito frustrante para muitos, pois o projeto favorecia a estabilidade em relação aos novos recursos. A FSF manteve um controle tão rigoroso sobre o que foi adicionado à versão oficial do GCC 2.x (desenvolvida desde 1992) que o GCC foi usado como um exemplo da "catedral" modelo de desenvolvimento no ensaio de Eric S. Raymond A Catedral e o Bazar.
Em 1997, um grupo de desenvolvedores formou o Experimental/Enhanced GNU Compiler System (EGCS) para fundir vários forks experimentais em um único projeto. A base da fusão foi um instantâneo de desenvolvimento do GCC (tirado por volta do 2.7.2 e posteriormente seguido até o lançamento do 2.8.1). As fusões incluíram g77 (Fortran), PGCC (P5 Pentium-optimized GCC), muitas melhorias em C++ e muitas novas arquiteturas e variantes de sistema operacional.
Embora ambos os projetos tenham seguido as mudanças um do outro de perto, o desenvolvimento do EGCS se mostrou consideravelmente mais vigoroso, tanto que a FSF interrompeu oficialmente o desenvolvimento de seu compilador GCC 2.x, abençoou o EGCS como a versão oficial do GCC e nomeou o projeto EGCS como o mantenedor do GCC em abril de 1999. Com o lançamento do GCC 2.95 em julho de 1999, os dois projetos foram novamente unidos. Desde então, o GCC tem sido mantido por um grupo variado de programadores de todo o mundo sob a direção de um comitê gestor.
GCC 3 (2002) removeu um front-end para CHILL devido à falta de manutenção.
Antes da versão 4.0, o front-end do Fortran era g77
, que suportava apenas o FORTRAN 77, mas depois foi descartado em favor do novo front-end GNU Fortran que suporta o Fortran 95 e grandes partes do Fortran 2003 e Fortran 2008 também.
A partir da versão 4.8, o GCC é implementado em C++.
O suporte para Cilk Plus existia do GCC 5 ao GCC 7.
GCC foi portado para uma ampla variedade de arquiteturas de conjunto de instruções e é amplamente implantado como uma ferramenta no desenvolvimento de software livre e proprietário. O GCC também está disponível para muitos sistemas embarcados, incluindo Symbian (chamado gcce), chips baseados em ARM e baseados em Power ISA. O compilador pode ter como alvo uma ampla variedade de plataformas, incluindo consoles de videogame como o PlayStation 2, Cell SPE do PlayStation 3 e Dreamcast. Foi portado para mais tipos de processadores e sistemas operacionais do que qualquer outro compilador.
Idiomas suportados
Desde maio de 2021, a recente versão 11.1 do GCC inclui front-ends para C (gcc
), C++ (g++
), Objective-C, Fortran (gfortran
), Ada (GNAT), Go (gccgo
) e D (gdc
, desde 9.1) linguagens de programação, com as extensões de linguagem paralela OpenMP e OpenACC sendo suportado desde o GCC 5.1. Versões anteriores ao GCC 7 também suportavam Java (gcj
), permitindo a compilação de Java para código de máquina nativo. O suporte Modula-2, anteriormente oferecido por terceiros, será incorporado ao GCC 13.
Em relação ao suporte à versão de linguagem para C++ e C, desde o GCC 11.1, o destino padrão é gnu++17, um superconjunto de C++17, e gnu11, um superconjunto de C11, com suporte padrão estrito também disponível. O GCC também fornece suporte experimental para C++20 e o futuro C++23.
Existem front-ends de terceiros para muitos idiomas, como Pascal (gpc
), Modula-3 e VHDL (GHDL
). Existem algumas ramificações experimentais para oferecer suporte a idiomas adicionais, como o compilador GCC UPC para Unified Parallel C ou Rust.
Design
A interface externa do GCC segue as convenções do 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 montador no saída e, opcionalmente, executa o vinculador para produzir um binário executável completo.
Cada um dos compiladores de linguagem é um programa separado que lê o código-fonte e gera o código de máquina. Todos têm uma estrutura interna comum. Um front-end por idioma analisa o código-fonte nesse idioma e produz uma árvore de sintaxe abstrata ("árvore" para abreviar).
Estes são, se necessário, convertidos na representação de entrada do meio, chamada forma GENÉRICA; a extremidade intermediária então gradualmente transforma o programa em sua forma final. Otimizações do compilador e técnicas de análise de código estático (como FORTIFY_SOURCE, uma diretiva de compilador que tenta descobrir alguns estouros de buffer) são aplicadas ao código. Eles funcionam em várias representações, principalmente a representação GIMPLE independente de arquitetura e a representação RTL dependente de arquitetura. Por fim, o código de máquina é produzido usando correspondência de padrões específicos da arquitetura originalmente baseada em um algoritmo de Jack Davidson e Chris Fraser.
O GCC foi escrito principalmente em C, exceto por partes do front-end Ada. A distribuição inclui as bibliotecas padrão para Ada e C++ cujo código é escrito principalmente nessas linguagens. Em algumas plataformas, a distribuição também inclui uma biblioteca de tempo de execução de baixo nível, libgcc, escrita em uma combinação de C independente de máquina e código de máquina específico do processador, projetada principalmente para lidar com operações aritméticas que o processador de destino não pode executar diretamente.
O GCC usa muitas ferramentas adicionais em sua compilação, muitas das quais são instaladas por padrão por muitas distribuições Unix e Linux (mas que, normalmente, não estão presentes nas instalações do Windows), incluindo Perl, Flex, Bison e outras ferramentas comuns. Além disso, atualmente requer a presença de três bibliotecas adicionais para compilar: 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 deveria 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 destruidores e recursos 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. Isso significa que, para construir o GCC a partir de fontes, é necessário um compilador C++ que entenda o padrão ISO/IEC C++03.
Em 18 de maio de 2020, o GCC mudou do padrão ISO/IEC C++03 para o padrão ISO/IEC C++11 (ou seja, precisava compilar, inicializar, o próprio compilador; por padrão, no entanto, compila versões posteriores do C++).
Extremidades frontais
Cada front-end usa um analisador para produzir a árvore de sintaxe abstrata de um determinado arquivo de origem. Devido à abstração da árvore de sintaxe, os arquivos de origem de qualquer uma das diferentes linguagens suportadas podem ser processados pelo mesmo back-end. O GCC começou usando analisadores LALR gerados com Bison, mas gradualmente mudou para analisadores descendentes 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 analisadores descendentes recursivos escritos à mão.
Até o 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 front-ends de idiomas diferentes, e os front-ends podiam fornecer seus próprios códigos de árvore. Isso foi simplificado com a introdução de GENERIC e GIMPLE, duas novas formas de árvores independentes de linguagem que foram introduzidas com o advento do GCC 4.0. GENERIC é mais complexo, baseado na representação intermediária do front-end Java GCC 3.x. GIMPLE é um GENÉRICO simplificado, no qual várias construções são reduzidas a várias instruções GIMPLE. Os front-ends C, C++ e Java produzem GENERIC diretamente no front-end. Em vez disso, outros front-ends têm diferentes representações intermediárias após a análise e as convertem em GENÉRICO.
Em ambos os casos, o chamado "gimplifier" em seguida, converte essa forma mais complexa na forma GIMPLE baseada em SSA mais simples, que é a linguagem comum para um grande número de otimizações globais (escopo de função) independentes de linguagem e arquitetura.
GENÉRICO e GIMPLE
GENERIC é uma linguagem de representação intermediária usada como uma "extremidade intermediária" ao compilar o código-fonte em binários executáveis. Um subconjunto, chamado GIMPLE, é visado por todos os front-ends do GCC.
O estágio intermediário do GCC faz toda a análise e otimização do código, trabalhando independentemente da linguagem compilada e da arquitetura de destino, começando pela representação GENERIC e expandindo-a para registrar a linguagem de transferência (RTL). A representação GENERIC contém apenas o subconjunto das construções de programação imperativas otimizadas pela extremidade intermediária.
Ao transformar o código-fonte em GIMPLE, as 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 na representação SIMPLE proposta no compilador McCAT por Laurie J. Hendren para simplificar a análise e otimização de programas imperativos.
Otimização
A otimização pode ocorrer durante qualquer fase da compilação; no entanto, a maior parte das otimizações é realizada após a análise sintática e semântica do front-end e antes da geração do código do back-end; portanto, um nome comum, embora um tanto autocontraditório, para esta parte do compilador é "extremidade do meio"
O conjunto exato de otimizações do GCC varia de versão para versão à medida que se desenvolve, mas inclui os algoritmos padrão, como otimização de loop, jump threading, eliminação de subexpressão comum, programação de instruções e assim por diante. As otimizações RTL são menos importantes com a adição de otimizações globais baseadas em SSA em árvores GIMPLE, pois as otimizações RTL têm um escopo muito mais limitado e menos informações de alto nível.
Algumas dessas otimizações executadas nesse nível incluem eliminação de código morto, eliminação de redundância parcial, numeração de valor global, propagação constante condicional esparsa e substituição escalar de agregados. Otimizações baseadas em dependência de matriz, como vetorização automática e paralelização automática, também são realizadas. A otimização guiada por perfil também é possível.
Back-end
O back-end do GCC é parcialmente especificado por macros de pré-processador e funções específicas para uma arquitetura de destino, por exemplo, para definir seu endianness, tamanho de palavra e convenções de chamada. A parte frontal do back-end os usa para ajudar a decidir a geração de RTL, portanto, embora o RTL do GCC seja nominalmente independente do processador, a sequência inicial de instruções abstratas já está adaptada ao destino. A qualquer momento, as instruções RTL reais que formam a representação do programa devem estar de acordo com a descrição da máquina da arquitetura de destino.
O arquivo de descrição da máquina contém padrões RTL, juntamente com restrições de operando e trechos de código para produzir a montagem final. As restrições indicam que um determinado padrão RTL pode ser aplicado apenas (por exemplo) a determinados registros de hardware ou (por exemplo) permitir deslocamentos de operandos imediatos de tamanho limitado (por exemplo, 12, 16, 24,... deslocamentos de bits, etc.). Durante a geração de RTL, as restrições para a arquitetura de destino fornecida são verificadas. Para emitir um determinado trecho de RTL, ele deve corresponder a um (ou mais) dos padrões RTL no arquivo de descrição da máquina e satisfazer as restrições desse 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 aos registradores reais da máquina e a um padrão do arquivo de descrição da máquina de destino. Formar RTL estrito é uma tarefa complicada; um passo importante é a alocação de registradores, onde registradores reais de hardware são escolhidos para substituir os pseudo-registradores inicialmente designados. Isso é seguido por um "recarregamento" Estágio; quaisquer pseudo-registradores que não foram atribuídos a um registro de hardware real são 'derramados' para a pilha, e o RTL para executar este derramamento é gerado. Da mesma forma, os deslocamentos que são muito grandes para caber em uma instrução real devem ser quebrados e substituídos por sequências RTL que obedecerão às restrições de deslocamento.
Na fase final, o código de máquina é construído chamando um pequeno trecho de código, associado a cada padrão, para gerar as instruções reais do conjunto de instruções do alvo, usando os registradores finais, deslocamentos e endereços escolhido durante a fase de recarga. O fragmento de geração de montagem pode ser apenas uma string, caso em que é executada uma simples substituição de string dos registros, deslocamentos e/ou endereços na string. O fragmento de geração de assembly também pode ser um pequeno bloco de código C, executando algum trabalho adicional, mas retornando uma string contendo o código assembly válido.
Biblioteca padrão C++ (libstdc++)
O projeto GCC inclui uma implementação da biblioteca padrão C++ chamada libstdc++, licenciada sob a licença GPLv3 com uma exceção para vincular aplicações de código fechado quando as fontes são construídas com GCC. A versão atual é a 11.
Outras características
Alguns recursos do GCC incluem:
- Otimização de tempo de ligação
- A otimização de tempo de links otimiza os limites de arquivos de objetos para melhorar diretamente o binário vinculado. Otimização de tempo de ligação depende de um arquivo intermediário contendo a serialização de alguns Gimple. representação incluída no arquivo objeto. O arquivo é gerado ao lado do arquivo objeto durante a compilação de origem. Cada compilação de origem gera um arquivo de objeto separado e arquivo de ajudante de tempo de ligação. Quando os arquivos de objeto estão ligados, o compilador é executado novamente e usa os arquivos auxiliares para otimizar o código através dos arquivos de objetos compilados separadamente.
- Plugins
- Plugins estender o compilador GCC diretamente. Os plugins permitem que um compilador de estoque seja adaptado às necessidades específicas por código externo carregado como plugins. Por exemplo, os plugins podem adicionar, substituir ou até mesmo remover passes de fim médio operando em Gimple. representações. Vários plugins GCC já foram publicados, notavelmente:
- O plugin Python, que liga contra libpython, e permite que se invoque scripts Python arbitrários de dentro do compilador. O objetivo é permitir que os plugins GCC sejam escritos em Python.
- O plugin MELT fornece uma linguagem de alto nível como Lisp para estender o GCC.
- O suporte dos plugins foi uma vez um problema contencioso em 2007.
- Memória transacional C++
- A linguagem C++ tem uma proposta ativa de memória transacional. Ele pode ser ativado no GCC 6 e mais recente ao compilar com
-fgnu-tm
. - Identificadores de Unicode
- Embora a linguagem C++ requer suporte para caracteres Unicode não ASCII em identificadores, o recurso só foi suportado desde o GCC 10. Tal como acontece com o manuseio existente de literais de cadeia de caracteres, o arquivo de origem é assumido para ser codificado no UTF-8. O recurso é opcional em C, mas foi disponibilizado também desde essa mudança.
- Extensões de C
- GNU C estende a linguagem de programação C com várias características não padronizadas, incluindo funções aninhadas e expressões de tipo.
Arquiteturas
As principais famílias de processadores com suporte (e mais bem testadas) são ARM de 64 e 32 bits, x86_64 e x86 de 64 e 32 bits e PowerPC e SPARC de 64 bits.
As famílias de processadores de destino do GCC a partir da versão 11.1 incluem:
- AArch64
- Alfa.
- ARM
- AVR
- Blackfin
- EBPF
- Epifania (GCC 4.8)
- H8/300
- HC12
- IA-32 (x86)
- IA-64 (Intel Itanium)
- MIPS
- Motorola 68000
- MSP430
- GPU Nvidia
- Nvidia PTX
- PA-RISC
- PDP-11
- PowerPC
- R8C / M16C / M32C
- RISC-V
- SPARC
- Super-H
- Sistema/390 / zSeries
- VAX
- x86-64
Os processadores de destino menos conhecidos suportados na versão padrão incluem:
- 68HC11
- A29K
- C6x
- CR16
- D30V
- DSP16xx
- ETRAX CRIS
- FR-30
- FR-V
- IBM ROMP
- Intel i960
- IP2000
- M32R
- MCORE
- MIL-STD-1750A
- MMIX
- MN10200
- MN10300
- Motorola 88000
- NS32K
- RL78
- Tempestade16
- V850
- Xtensa
Processadores adicionais foram suportados pelas versões GCC mantidas separadamente da versão FSF:
- Cortus APS3
- ARC
- AVR32
- C166 e C167
- D10V
- EISC
- ESi-RISC
- Hexagonal
- Rede de distribuição
- Redefinição
- MeP
- Microbiologia
- Motorola 6809
- MSP430
- Arquitetura NEC SX
- Nios II e Nios
- Abertura
- PDP-10
- PIC24/dsPIC
- PIC32
- Propeller
- Saturno (HP48XGCC)
- Sistema/370
- TIGCC (variante m68k)
- TMS9900
- TriCore
- Z8000
- ZPU
O compilador Java GCJ pode ter como alvo uma arquitetura de linguagem de máquina nativa ou o bytecode Java da máquina virtual Java. Ao redirecionar o GCC para uma nova plataforma, o bootstrapping é frequentemente usado. Motorola 68000, Zilog Z80 e outros processadores também são visados nas versões GCC desenvolvidas para várias calculadoras gráficas programáveis Texas Instruments, Hewlett Packard, Sharp e Casio.
Licença
O GCC está licenciado sob a GNU General Public License versão 3. A exceção de tempo de execução do GCC permite a compilação de programas proprietários (além de software livre) com o GCC. Isso não afeta os termos de licença do código-fonte GCC.
Contenido relacionado
Gerenciamento avançado de energia
Lionhead Studios
Transporte na República Democrática do Congo