shecc
dibuat dari awal, menargetkan arsitektur Arm 32-bit dan RISC-V, sebagai kompiler kompilasi mandiri untuk subset bahasa C. Meskipun sifatnya sederhana, ia mampu melakukan strategi pengoptimalan dasar sebagai kompiler pengoptimalan yang berdiri sendiri.
shecc
mampu mengkompilasi file sumber C yang ditulis dalam sintaks berikut:
+=
, -=
, *=
int i = [expr]
#define
, #ifdef
, #elif
, #endif
, #undef
, dan #error
__VA_ARGS__
Backend menargetkan armv7hf dengan Linux ABI, diverifikasi pada Raspberry Pi 3, dan juga mendukung arsitektur RISC-V 32-bit, diverifikasi dengan QEMU.
Langkah-langkah untuk memvalidasi shecc
bootstrapping:
stage0
: kode sumber shecc
awalnya dikompilasi menggunakan kompiler biasa yang menghasilkan executable asli. Kompiler yang dihasilkan dapat digunakan sebagai kompiler silang.stage1
: Biner yang dibangun membaca kode sumbernya sendiri sebagai input dan menghasilkan biner ARMv7-A/RV32IM.stage2
: Biner ARMv7-A/RV32IM yang dihasilkan dipanggil (melalui QEMU atau dijalankan pada perangkat Arm dan RISC-V) dengan kode sumbernya sendiri sebagai input dan menghasilkan biner ARMv7-A/RV32IM lainnya.bootstrap
: Bangun kompiler stage1
dan stage2
, dan verifikasi bahwa keduanya identik dalam byte. Jika demikian, shecc
dapat mengkompilasi kode sumbernya sendiri dan menghasilkan versi baru dari program yang sama. Pembuat kode di shecc
tidak bergantung pada utilitas eksternal. Anda hanya memerlukan kompiler C biasa seperti gcc
dan clang
. Namun, shecc
akan melakukan bootstrap sendiri, dan emulasi ISA Arm/RISC-V diperlukan. Instal QEMU untuk emulasi pengguna Arm/RISC-V di GNU/Linux:
$ sudo apt-get install qemu-user
shecc
masih dapat dibuat di macOS atau Microsoft Windows. Namun, bootstrap tahap kedua akan gagal karena tidak adanya qemu-arm
.
Untuk menjalankan tes snapshot, instal paket di bawah ini:
$ sudo apt-get install graphviz jq
Konfigurasikan backend mana yang Anda inginkan, shecc
mendukung backend ARMv7-A dan RV32IM:
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
Jalankan make
dan Anda akan melihat ini:
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
adalah kompiler tahap pertama. Penggunaannya:
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >
Opsi kompiler:
-o
: Tentukan nama file keluaran (default: out.elf
)+m
: Gunakan instruksi perkalian/pembagian perangkat keras (default: dinonaktifkan)--no-libc
: Kecualikan pustaka C yang tertanam (default: tertanam)--dump-ir
: Membuang representasi perantara (IR)Contoh:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
Verifikasi bahwa IR yang dipancarkan identik dengan snapshot dengan menentukan target check-snapshots
saat memanggil make
:
$ make check-snapshots
shecc
hadir dengan unit test. Untuk menjalankan pengujian, berikan check
sebagai argumen:
$ make check
Keluaran referensi:
...
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
Untuk membersihkan file kompiler yang dihasilkan, jalankan perintah make clean
. Untuk mengatur ulang konfigurasi arsitektur, gunakan perintah make distclean
.
Setelah opsi --dump-ir
diteruskan ke shecc
, representasi perantara (IR) akan dihasilkan. Ambil file tests/fib.c
misalnya. Ini terdiri dari fungsi deret Fibonacci rekursif.
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}
Jalankan yang berikut ini untuk menghasilkan IR:
$ out/shecc --dump-ir -o fib tests/fib.c
Penjelasan baris demi baris antara sumber C dan 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"
} }
yang dapat digunakan. Alternatifnya, periksa implementasi printf
di source lib/cc
untuk var_arg
. shecc
dapat didistribusikan ulang secara bebas di bawah lisensi klausa BSD 2. Penggunaan kode sumber ini diatur oleh lisensi bergaya BSD yang dapat ditemukan di file LICENSE
.