
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 checkSortie 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.cExplication 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"
} }<stdarg.h> 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 .