shecc
est construit à partir de zéro, ciblant à la fois les architectures Arm et RISC-V 32 bits, en tant que compilateur auto-compilable pour un sous-ensemble du langage C. Malgré sa nature simpliste, il est capable d'exécuter des stratégies d'optimisation de base en tant que compilateur d'optimisation autonome.
shecc
est capable de compiler des fichiers sources C écrits dans la syntaxe suivante :
+=
, -=
, *=
int i = [expr]
#define
, #ifdef
, #elif
, #endif
, #undef
et #error
__VA_ARGS__
Le backend cible armv7hf avec Linux ABI, vérifié sur Raspberry Pi 3, et prend également en charge l'architecture RISC-V 32 bits, vérifiée avec QEMU.
Les étapes pour valider le bootstrapping shecc
:
stage0
: le code source shecc
est initialement compilé à l'aide d'un compilateur ordinaire qui génère un exécutable natif. Le compilateur généré peut être utilisé comme compilateur croisé.stage1
: Le binaire construit lit son propre code source en entrée et génère un binaire ARMv7-A/RV32IM.stage2
: Le binaire ARMv7-A/RV32IM généré est invoqué (via QEMU ou exécuté sur des appareils Arm et RISC-V) avec son propre code source en entrée et génère un autre binaire ARMv7-A/RV32IM.bootstrap
: construisez les compilateurs stage1
et stage2
et vérifiez qu'ils sont identiques en octets. Si tel est le cas, shecc
peut compiler son propre code source et produire de nouvelles versions de ce même programme. Le générateur de code dans shecc
ne repose pas sur des utilitaires externes. Vous n'avez besoin que de compilateurs C ordinaires tels que gcc
et clang
. Cependant, shecc
s'amorcerait tout seul et l'émulation Arm/RISC-V ISA est requise. Installez QEMU pour l'émulation utilisateur Arm/RISC-V sur GNU/Linux :
$ sudo apt-get install qemu-user
Il est toujours possible de créer shecc
sur macOS ou Microsoft Windows. Cependant, l’amorçage de la deuxième étape échouerait en raison de l’absence qemu-arm
.
Pour exécuter le test d'instantané, installez les packages ci-dessous :
$ sudo apt-get install graphviz jq
Configurez le backend souhaité, shecc
prend en charge le backend ARMv7-A et RV32IM :
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
Lancez make
et vous devriez voir ceci :
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
est le compilateur de première étape. Son utilisation :
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >
Options du compilateur :
-o
: Spécifiez le nom du fichier de sortie (par défaut : out.elf
)+m
: Utiliser les instructions matérielles de multiplication/division (par défaut : désactivé)--no-libc
: Exclure la bibliothèque C intégrée (par défaut : intégrée)--dump-ir
: Vider la représentation intermédiaire (IR)Exemple:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
Vérifiez que les IR émis sont identiques aux instantanés en spécifiant la cible check-snapshots
lors de l'appel make
:
$ make check-snapshots
shecc
est livré avec des tests unitaires. Pour exécuter les tests, donnez check
en argument :
$ make check
Sortie de référence :
...
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
Pour nettoyer les fichiers du compilateur générés, exécutez la commande make clean
. Pour réinitialiser les configurations d'architecture, utilisez la commande make distclean
.
Une fois l'option --dump-ir
passée à shecc
, la représentation intermédiaire (IR) sera générée. Prenons par exemple le fichier tests/fib.c
. Il s’agit d’une fonction de séquence de Fibonacci récursive.
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}
Exécutez ce qui suit pour générer IR :
$ out/shecc --dump-ir -o fib tests/fib.c
Explication ligne par ligne entre la source C et 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"
} }
ne peut être utilisé. Vous pouvez également vérifier l'implémentation printf
dans la source lib/cc
pour var_arg
. shecc
est librement redistribuable sous la licence clause BSD 2. L'utilisation de ce code source est régie par une licence de type BSD qui se trouve dans le fichier LICENSE
.