分类:
2010-03-29 13:40:08
1. why lcc?
工作上的事少了,稍有闲暇,决定强迫自己重拾对技术的热情;由于一直以来都在心不在焉的学编译技术,所以先找 个合适的编译器来看看吧.
为什么选择lcc ? 原来是要打定主意啃该死的gcc, 不过那太庞大了,为了避免半途而废,还是找个小规模的,lcc颇合我意,再加上有<>助阵,心里多少有几分把握.
废话不说了,先把lcc的代码弄下来编译了再说,毕竟阅读一个程序,最利索的办法就是单 步跟踪它的整个生命过程,要是一行行的看,用不了多久就会被其复杂的调用逻辑搞的七晕八素,对于编译器程序而言,无穷尽的递归调用或许是个最常用的方法.
2. 下载lcc-4.1源代码
在如下地址, 可以看到众多版本lcc的代码包,我选择的是lcc4.1版本,4.2与其差不多,随便选一个,把lcc-4.1.tar.gz取下来,顺便把 contrib目录也取下来.
3. 编译lcc
3.1 lcc文件结构
我的整个工作是在redhat linux下进行,把lcc-4.1.tar.gz解开,目录结构如下:
lcc-4.1
|-------alpha
|-------cpp (cpp是一个c预处理器程序,这里是其源代码)
|-------doc (文档)
|-------etc (驱动程序lcc的源代码,对于不同的平台,有不同的代码,比如x86-linux平台下的,就是linux.c,还有bprint程序的代码 bprint.c)
|--------include (各个平台的lcc专用头文件)
|--------lburg (lburg程序代码,稍后详述)
|--------lib (各个平台的lcc专用库代码)
|--------src (编译器程序源代码)
|--------tst (test cases)
|--------x86
|--------mips
|--------sparc
|--------makefile
3.2 lcc工具链程序
打 开makefile文件看看,整个项目编译出来会生成如下几个程序:
cpp:
c预处理器, 所做的工作无非是头文件替换, marco扩展之类,c文件都首先需要经过c预处理器的处理,然后交给编译器程序进行编译.
rcc:
编译器主体程序, 它干的工作跟所有的c编译器没什么两样,最终生成汇编代码,如果你要单独运行rcc,必须指定目标平台,也就是生成什么样的汇编代码,比如:
rcc -target=x86/linux c_source_file.c
lburg:
code-generator-generator. lcc 的后端,也就是代码生成模块,分为两部分,一部分是平台无关的,一部分是平台相关的,平台相关部分的代码主要是一些中间树的指令匹配模板和一些平台专用函 数,为了将lcc移植到不同的平台下,只需要改动这部分平台专有代码. 这部分代码首先写成类似与yacc输入文件的语法规范(以后部分分析这些输入文件的编写方法),然后由lburg转换成c代码,作为rcc的源代码的一部 分.
bprint:
rcc在编译的时候可以加上-a选项生成prof.out文件,该文件描述了程序的basic block和cfg(CONTROL FLOW GRAHIC), bprint就是分析prof.out并打印出可读形式(具体情况有待研究)
liblcc.a
rcc 静态连接库
lcc:
编译驱动程序,类似与gcc.exe的角色,他会根据不同平台对应不同的文件后缀,依次调用 cpp,rcc,连接器生成最终的可执行代码,对于不同的平台,是用不同的宿主文件和lcc.c编译而成,如x86-linux平台下是用 etc/linux.c
3.3 编译lcc
可以在lcc-4.1目录下直接利用makefile来编译,最好先export 一个环境变量BUILDDIR,用来存放编译出来的中间文件. 比如: BUILDDIR=/usr/tmp/lcc-4.1/x86/linux ; export BUILDDIR
然后运行:
make CC=c89 LD=c89 HOSTFILE=etc/linux.c all
CC和LD是指定编译器和连接器, 符合标准C规范的, HOSTFILE是最终生成的驱动程序lcc的源文件.对于linux平台,用etc/linux.c
当然最方便的方法是,将 contrib目录下(先和lcc-4.1.tar.gz一起弄下来的)的BUILD-LCC.sh拷贝到lcc-4.1目录下, 这个脚本会做完所有的事情,1)编译lcc工具链, 2)编译testcases 3) 安装lcc.
运行这个脚本前,你同样可以设置CC 和LD,当然如果你不这么干,它会自动去找;你也可以设置TMPDIR,它是编译生成的中间文件存放的临时目录,默认是/usr/tmp, --exec-prefix,这个是安装目录, lcc最终会安装在--exec-prefix/bin下,相关的lib会放在--exec-prefix/lib下,默认为:/usr/local /bin和/usr/local/lib. 运行这个脚本可以带两个可选参数target,host:
./BUILD-LCC.sh [--exec-prefix=path] [ target [ host ] ]
通常,你不需要这么干,因为脚本会帮你搞定它.
在 我的平台下,往往在编译lburg的时候会报错,这是因为gram.c这个文件有语法错误,实际上gram.c是由gram.y生成的,gram.y是一 个yacc输入文件,描述了lburg的语法规范,如果你不幸也有这种情况,解决的办法很简单,删除掉他自带的gram.c, 然后用yacc生成它.
yacc -o gram.c gram.y
好了,这个程序应该编译成功了,看看$BUILDDIR,都有什么, 上面说的几个程序都应该有了吧.
下 面,该来说说rcc的大致结构了...(传统的编译器的几个组成部分.)