随想录(再谈ucc开源编译器)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

    之前写过一篇ucc的文章,也就是这一篇。这篇文章对ucc的流程说了挺多,但是怎么把ucc移植到新的cpu上面,却没有说很多,后来自己又看了一下代码,发现还是有不少新的收获。

1、emit.c

    emit.c文件是真正的后端入口,所有的汇编文件的整理、组织部分都是这里完成的。当然这部分只是框架的内容,告诉我们一个大概,全局变量怎么放,字符串怎么放、函数怎么放等等。涉及到具体的内容还要依赖于具体的cpu体系文件。
 

2、x86linux.c & x86win32.c

    因为ucc只是涉及到了x86的部分,所以这里只谈x86。当然,同时因为在linux和windows平台上面,两者的汇编文件格式有所差别,所以这里有两个翻译文件。上面变量怎么放、字符串怎么放,emit.c就会调用这两个文件里面的对应函数。
 

3、x86.c

    如果说是普通的格式,那么x86linux.c & x86win32.c都可以完成,但是如果是具体函数的翻译,那么只能在x86.c里面完成了。当然,涉及到具体的PutASMCode指令部分,还是需要调用x86linux.c & x86win32.c这两个文件的。因为编译的过程比较复杂,特别是对于堆栈的计算、函数的压栈和出栈部分,这部分需要仔细研究和推敲。
 

4、x86linux.tpl & x86linux.tpl

    这是两个翻译模块,分别被包含在上面x86linux.c & x86win32.c文件中。分离出来,只是为了让结构更清晰一点。这部分和编译器生成的中间代码,几乎是一一对应的。
 

5、opcode.h

    这部分是比较容易忽视的部分,其实这部分最重要。因为其实它就包含在x86.c文件。本身描述的也是中间代码格式,后端翻译的本质就是将中间代码,按照模板翻译成汇编,就是这么简单。
 

6、reg.c

    寄存器分配是几乎所有体系结构都会遇到的问题,但是ucc处理的方法比较简单,就是尽量把临时变量保存到寄存器,实在保存不了,只好压栈。这样效率不一定高,但是不会出错。
 

7、其他体系应该怎么移植

    如果要移植到其他的汇编语言,比如mips,那么也需要成立这么几个文件,即mips.c、mips_linux.c、mips_linux.tpl文件,同时mips_linux.tpl包含在mips_linux.c中,opcode.h包含在mips.c中,mips.c调用mips_linux.c的PutASMCode函数,按照中间代码的类型生成每一个emit函数,比如EmitMove。mips_linux.tpl需要像x86linux.tpl一样,对opcode.h中的每一个中间格式进行翻译。有兴趣的同学可以试一试。
 

8、github链接

    ucc本身还是非常不错的代码,常看常新,github上也有对应的链接。代码内容也非常适合用来学习和研究。
 

9、编译和链接

    ucc的本身完整编译依赖于gcc生成预处理文件、asm文件生成obj文件、obj文件生成exe文件,这就是ucc的高明之处。因为ucc自己只实现了ucl,也就是c文件到asm文件的这部分,而这部分的工作确是非常有意义的。一方面对于学生来说,可以知道编译原理的基本思路,另外一方面,对于某些行业、比如安全关键行业,完全可以做一些私人化的定制,这是很有意义的。我们可以看一下,完整ucc运行时,依赖哪些命令,

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "ucc.h"

#define _P_WAIT 0
#define UCCDIR "/home/iron/bin/"


/**
	ucc -E -v hello.c -I../ -DREAL=double -o hello.ii
		For $1/$2/$3	,see BuildCommand() for detail.
		$1		-I../ -DREAL=double,	command-line options
		$2		hello.c,		input file
		$3		hello.ii,		output file
*/
char *CPPProg[] = 
{ 
	"/usr/bin/gcc", "-m32", "-U__GNUC__", "-D_POSIX_SOURCE", "-D__STRICT_ANSI__",
	"-Dunix", "-Di386", "-Dlinux", "-D__unix__", "-D__i386__", "-D__linux__", 
	"-D__signed__=signed", "-D_UCC", "-I" UCCDIR "include", "$1", "-E", "$2", "-o", "$3", 0 
};


/**
	ucc -S -v hello.c --dump-ast -o hello.asm
		------->
	/home/iron/bin/ucl -o hello.asm --dump-ast hello.i
		$1	--dump-ast,	some command-line options
		$2	hello.i,	input file
		$3	hello.asm,	output file
*/
char *CCProg[] = 
{
	UCCDIR "ucl", "-o", "$3", "$1", "$2", 0 
};

char *ASProg[] = 
{ 
	"/usr/bin/gcc", "-m32", "-c", "-o", "$3", "$1", "$2", 0 
};

char *LDProg[] = 
{
	"/usr/bin/gcc", "-m32", "-o", "$3", "$1", "$2", UCCDIR "assert.o", "-lc", "-lm", 0 
};


char *ExtNames[] = { ".c", ".i", ".s", ".o", ".a;.so", 0 };

int Execute(char **cmd)
{
	int pid, n, status;

	pid = fork();
	if (pid == -1)
	{
		fprintf(stderr, "no more processes\n");
		return 100;
	}
	else if (pid == 0)
	{
		execv(cmd[0], cmd);
		perror(cmd[0]);
		fflush(stdout);
		exit(100);
	}
	/**
		wait():  on success, returns the process ID of the terminated child; on
		error, -1 is returned.
	*/
	while ((n = wait(&status)) != pid && n != -1)
		;
	if (n == -1)
		status = -1;
	if (status & 0xff)
	{
		fprintf(stderr, "fatal error in %s\n", cmd[0]);
		status |= 0x100;
	}

	return (status >> 8) & 0xff;
}

void SetupToolChain(void)
{
}

 
 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页