Shecc:自托管和教育性的C编译器

2021-01-23 13:48:42

shecc是针对32位Arm和RISC-V体系结构从头开始构建的,它是针对C语言子集的自编译器。

两遍编译:在第一遍,它检查语句的语法并构建符号表,而在第二遍,它实际上将程序语句转换为Arm / RISC-V机器代码。

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:

仍然可以在macOS或Microsoft Windows上构建shecc。但是,由于缺少qemu-arm,第二阶段自举将失败。

$ make config ARCH = arm#目标机器代码切换到Arm $ make config ARCH = riscv#目标机器代码切换到RISC-V

... int main(int argc,int argv){exit(sizeof(char)); } => 1int main(int argc,int argv){int a; a = 0;开关(3){情况0:返回2;情况3:a = 10;打破;情况1:传回0; } exit(a); } => 10int main(int argc,int argv){int a; a = 0;开关(3){情况0:返回2;默认值:a = 10;打破; } exit(a); } => 10好

一旦将--dump-ir选项传递给shecc,就会生成中间表示(IR)。以文件tests / fib.c为例。它由一个递归的斐波那契序列函数组成。

int fib(int n){如果(n == 0)返回0;否则(n == 1)返回1;返回fib(n-1)+ fib(n-2);}

C源IR解释------------------- + -------------------------- + ------------------------------------------------- --- int fib(int n)fib:为函数fib {{如果(n == 0)x0 =& n获取变量n的地址保留堆栈帧x0 = * x0(4)从地址将值读入x0,长度= 4(int)x1:= 0将x1设置为零x0 == x1?如果为false,则将x0与x1进行比较,然后转到1641如果x0!= x1,则跳转至标签1641,返回0;否则为0。 x0:= 0将x0设置为零。 x0是返回值。 return(from fib)跳转到函数出口1641:else if(n == 1)x0 =& n获取变量n x0 = * x0(4)的值,从地址读取值到x0,长度= 4(int)x1 := 1将x1设置为1 x0 == x1?将x0与x1比较,如果为true,则转到1649如果x0!= x1,则跳转到标签1649,返回1; x0:= 1将x0设置为1。x0是返回值。 return(from fib)跳转到函数出口1649:return x0 =& n获取变量n的地址fib(n-1)x0 = * x0(4)从地址中读取值到x0中,长度= 4(int)x1: = 1将x1设置为1 x0-= x1从x0中减去x1,即(n-1)+ x0:= fib()@ 1631将函数fib()放入x0中推x0将结果存储在堆栈fib(n-2)中; x0 =& n获取变量n的地址x0 = * x0(4)从地址读取值到x0中,长度= 4(int)x1:= 2将x1设置为2 x0-= x1从x0减去x1即(n- 2)x1:= fib()@ 1631将函数fib()调用到x1 pop x0中将结果从堆栈中检索到x0中x0 + = x1将x1加到x0上,即fib(n-1)+ fib(n-2)的结果)return(from fib)跳转到函数exit}恢复先前的堆栈帧出口fib

不支持一元*运算符,这使得必须使用[0]语法。考虑int x = 5; int * ptr =& x;并且禁止使用* ptr。但是,使用ptr [0]是有效的,其行为与* ptr相同。

不同数量的函数参数的支持不完整。 否< stdarg.h> 或者,检查源lib / c.c中的实现printf是否存在var_arg。 shecc根据BSD 2子句许可证可自由重新分发。此源代码的使用受BSD样式许可证的约束,可以在LICENSE文件中找到该许可证。