
shecc é construído do zero, visando as arquiteturas Arm e RISC-V de 32 bits, como um compilador autocompilação para um subconjunto da linguagem C. Apesar de sua natureza simplista, ele é capaz de executar estratégias básicas de otimização como um compilador de otimização independente.
shecc é capaz de compilar arquivos fonte C escritos na seguinte sintaxe:
+= , -= , *=int i = [expr]#define , #ifdef , #elif , #endif , #undef e #error__VA_ARGS__O backend tem como alvo armv7hf com Linux ABI, verificado no Raspberry Pi 3, e também suporta arquitetura RISC-V de 32 bits, verificada com QEMU.
As etapas para validar a inicialização shecc :
stage0 : o código-fonte shecc é inicialmente compilado usando um compilador comum que gera um executável nativo. O compilador gerado pode ser usado como um compilador cruzado.stage1 : O binário construído lê seu próprio código-fonte como entrada e gera um binário ARMv7-A/RV32IM.stage2 : O binário ARMv7-A/RV32IM gerado é invocado (via QEMU ou em execução em dispositivos Arm e RISC-V) com seu próprio código-fonte como entrada e gera outro binário ARMv7-A/RV32IM.bootstrap : Construa os compiladores stage1 e stage2 e verifique se eles são idênticos em bytes. Nesse caso, shecc pode compilar seu próprio código-fonte e produzir novas versões desse mesmo programa. O gerador de código no shecc não depende de utilitários externos. Você só precisa de compiladores C comuns, como gcc e clang . No entanto, shecc se inicializaria e a emulação Arm/RISC-V ISA é necessária. Instale QEMU para emulação de usuário Arm/RISC-V no GNU/Linux:
$ sudo apt-get install qemu-user Ainda é possível construir shecc no macOS ou Microsoft Windows. No entanto, a inicialização do segundo estágio falharia devido à ausência qemu-arm .
Para executar o teste de snapshot, instale os pacotes abaixo:
$ sudo apt-get install graphviz jq Configure qual backend você deseja, shecc suporta backend ARMv7-A e RV32IM:
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
Execute make e você verá isto:
CC+LD out/inliner
GEN out/libc.inc
CC out/src/main.o
LD out/shecc
SHECC out/shecc-stage1.elf
SHECC out/shecc-stage2.elf
File out/shecc é o compilador de primeiro estágio. Seu uso:
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >Opções do compilador:
-o : Especifique o nome do arquivo de saída (padrão: out.elf )+m : Use instruções de multiplicação/divisão de hardware (padrão: desabilitado)--no-libc : Exclui biblioteca C incorporada (padrão: incorporada)--dump-ir : representação intermediária de despejo (IR)Exemplo:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib Verifique se os IRs emitidos são idênticos aos instantâneos especificando o destino check-snapshots ao invocar make :
$ make check-snapshots shecc vem com testes de unidade. Para executar os testes, dê check como argumento:
$ make checkSaída de referência:
...
int main(int argc, int argv) { exit(sizeof(char)); } => 1
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } exit(a); } => 10
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10
OK
Para limpar os arquivos do compilador gerados, execute o comando make clean . Para redefinir as configurações da arquitetura, use o comando make distclean .
Assim que a opção --dump-ir for passada para shecc , a representação intermediária (IR) será gerada. Veja o arquivo tests/fib.c por exemplo. Consiste em uma função de sequência de Fibonacci recursiva.
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}Execute o seguinte para gerar IR:
$ out/shecc --dump-ir -o fib tests/fib.cExplicação linha por linha entre a fonte C e IR:
C Source IR Explanation
-- -- -- -- -- -- -- -- -- + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
int fib ( int n ) def int @ fib ( int % n ) Indicate a function definition
{ {
if ( n == 0 ) const %. t1001 , $0 Load constant 0 into a temporary variable ".t1001"
%. t1002 = eq % n , %. t1001 Test "n" equals ".t1001" or not , and write the result in temporary variable ".t1002"
br %. t1002 , . label . 1177 , . label . 1178 If ".t1002" equals zero , goto false label ".label.1178" , otherwise ,
goto true label ".label.1177"
. label . 1177
return 0 ; const %. t1003 , $0 Load constant 0 into a temporary variable ".t1003"
ret %. t1003 Return ".t1003"
j . label . 1184 Jump to endif label ".label.1184"
. label . 1178
else if ( n == 1 ) const %. t1004 , $1 Load constant 1 into a temporary variable ".t1004"
%. t1005 = eq % n , %. t1004 Test "n" equals ".t1004" or not , and write the result in temporary variable ".t1005"
br %. t1005 , . label . 1183 , . label . 1184 If ".t1005" equals zero , goto false label ".label.1184" . Otherwise ,
goto true label ".label.1183"
. label . 1183
return 1 ; const %. t1006 , $1 Load constant 1 into a temporary variable ".t1006"
ret %. t1006 Return ".t1006"
. label . 1184
return
fib ( n - 1 ) const %. t1007 , $1 Load constant 1 into a temporary variable ".t1007"
%. t1008 = sub % n , %. t1007 Subtract ".t1007" from "n" , and store the result in temporary variable ".t1008"
push %. t1008 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
+ retval %. t1009 Store return value in temporary variable ". t1009 "
fib ( n - 2 ); const %. t1010 , $2 Load constant 2 into a temporary variable ".t1010"
%. t1011 = sub % n , %. t1010 Subtract ".t1010" from "n" , and store the result in temporary variable ".t1011"
push %. t1011 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
retval %. t1012 Store return value in temporary variable ". t1012 "
%. t1013 = add %. t1009 , %. t1012 Add ". t1009 " and ". t1012 ", and store the result in temporary variable " . t1013 "
ret %. t1013 Return ".t1013"
} }<stdarg.h> pode ser usado. Alternativamente, verifique a implementação printf na fonte lib/cc para var_arg . shecc é livremente redistribuível sob a licença da cláusula BSD 2. O uso deste código-fonte é regido por uma licença estilo BSD que pode ser encontrada no arquivo LICENSE .