
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.cC 源代码和 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> 。或者,检查源lib/cc中var_arg的printf实现。shecc可根据 BSD 2 条款许可证自由重新分发。此源代码的使用受 BSD 样式许可证的约束,该许可证可在LICENSE文件中找到。