shecc
是從頭開始建構的,針對 32 位元 Arm 和 RISC-V 架構,作為 C 語言子集的自編譯編譯器。儘管其本質很簡單,但它能夠作為獨立的最佳化編譯器執行基本的最佳化策略。
shecc
能夠編譯用以下語法編寫的 C 原始檔:
+=
、 -=
、 *=
int i = [expr]
#define
、 #ifdef
、 #elif
、 #endif
、 #undef
和#error
__VA_ARGS__
標識符的非巢狀可變參數宏後端以Linux ABI的armv7hf為目標,在Raspberry Pi 3上進行了驗證,並且還支援RISC-V 32位元架構,並透過QEMU進行了驗證。
驗證shecc
引導的步驟:
stage0
: shecc
原始碼最初使用普通編譯器編譯,生成本機可執行檔。產生的編譯器可以用作交叉編譯器。stage1
:建置的二進位檔案讀取自己的原始程式碼作為輸入並產生 ARMv7-A/RV32IM 二進位檔案。stage2
:生成的 ARMv7-A/RV32IM 二進位檔案被呼叫(透過 QEMU 或在 Arm 和 RISC-V 裝置上運行),以其自己的原始程式碼作為輸入,並產生另一個 ARMv7-A/RV32IM 二進位檔案。bootstrap
:建構stage1
和stage2
編譯器,並驗證它們在位元組方面是否相同。如果是這樣, shecc
可以編譯自己的原始程式碼並產生相同程式的新版本。 shecc
中的程式碼產生器不依賴外部實用程式。您只需要普通的 C 編譯器,例如gcc
和clang
。但是, shecc
會自行引導,並且需要 Arm/RISC-V ISA 仿真。在 GNU/Linux 上安裝用於 Arm/RISC-V 使用者模擬的 QEMU:
$ sudo apt-get install qemu-user
仍然可以在 macOS 或 Microsoft Windows 上建立shecc
。但是,由於qemu-arm
缺失,第二階段引導將會失敗。
若要執行快照測試,請安裝下列軟體包:
$ sudo apt-get install graphviz jq
配置你想要的後端, shecc
支援ARMv7-A和RV32IM後端:
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
運行make
你應該看到這樣的內容:
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
檔案out/shecc
是第一階段編譯器。其用法:
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >
編譯器選項:
-o
:指定輸出檔名(預設: out.elf
)+m
:使用硬體乘法/除法指令(預設值:停用)--no-libc
:排除嵌入式 C 函式庫(預設值:嵌入式)--dump-ir
: 轉儲中間表示 (IR)例子:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
透過在呼叫make
時指定check-snapshots
目標來驗證發出的 IR 是否與快照相同:
$ make check-snapshots
shecc
帶有單元測試。若要執行測試,請將check
作為參數:
$ make check
參考輸出:
...
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
要清理產生的編譯器文件,請執行命令make clean
。若要重設架構配置,請使用指令make distclean
。
將選項--dump-ir
傳遞給shecc
後,將產生中間表示(IR)。以檔案tests/fib.c
為例。它由遞歸斐波那契序列函數組成。
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}
執行以下命令產生 IR:
$ out/shecc --dump-ir -o fib tests/fib.c
C 原始碼和 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"
} }
。或者,檢查來源lib/cc
中var_arg
的printf
實作。shecc
可根據 BSD 2 條款許可證自由重新分發。此原始程式碼的使用受 BSD 樣式許可證的約束,該許可證可在LICENSE
檔案中找到。