SBCL编译过程

SBCL编译过程
[转自]   http://googies.info/blog/751.html

  SBCL源自于CMUCL (Carnegie-Mellon University Common Lisp),而CMUCL出现于上世纪80年代。因此SBCL的代码算是已经比较成熟,从开发的角度来讲,SBCL社区的活跃度已经比较低了。事实上也的确如此,单就我所知道的,sbcl-devel邮件列表几天难得能收到一封邮件。当然,SBCL还远称不上完美;至少有一点,SBCL还不支持SSA。另外,SBCL源代码不失为学习Common Lisp的一种好途径。

SBCL目录结构
  SBCL的src/目录下包含assembly/、code/、cold/、compiler/、pcl/和runtime/六个子目录。其中assembly/子目录中包含的是体系结构相关的一些汇编相关代码;code/中包含的是运行时系统 (如Read-Eval-Print Loop) 和标准的Common Lisp构造等;cold/中包含了编译SBCL过程中所要用到的一些代码;compiler/中包含了Python (SBCL编译器的名字,采用的是CMUCL中的术语) 的实现代码;pcl/(Portable Common Loops)中包含的是CLOS的一种经典实现;而runtime/目录下是一些C代码,主要用以实现一些与体系结构和操作系统平台相关的操作 (例如一些信号处理等)。

SBCL编译过程
  SBCL的编译过程类似于GCC。GCC中的大部分代码都是使用C语言编写的;相应的,SBCL的大部分代码都是使用Common Lisp语言编写的,因此编译SBCL需要一个ANSI Common Lisp可执行编译器来完成SBCL的自举 (bootstrap)。SBCL支持使用多种ANSI Common Lisp编译器来完成这一自举过程,包括SBCL自身、CMUCL、OpenMCL和Clisp等。SBCL的编译过程包括多个阶段。

  最开始的阶段,也可以称为make-config阶段。该阶段主要负责确定目标平台的体系结构和操作系统类型。make-config.sh将这些内容写入local-target-features.lisp-expr。然后分别将src/runtime/下的target-arch-os.h, target-arch.h, target-os.h, target-lispregs.h和Config文件指向对应平台的相应文件。

  第一阶段,也就是make-host-1阶段。首先需要说明的是,由于SBCL支持交叉编译,因此在交叉编译的情况下,make-host-*将会在本地机上运行;而make-target-*在目的机上运行。make-host-1阶段使用既有的Common Lisp编译器来编译和加载SBCL源代码。这一阶段Lisp编译器主要读取src/code/、src/compiler/和src/assembly/目录下的Common Lisp源代码,生成一个交叉编译器 (在下图中标识为xc) 和genesis;同时在src/runtime/genesis/下生成一些*.h文件用以描述Lisp的一些数据结构,这些文件随后将由C编译器读取生成一个可执行程序用以作为SBCL的运行时环境。这里需要注意的是,xc和genesis事实上只是运行在既有Common Lisp环境中的应用程序,因此此时的package等Common Lisp构造的命名格式都是sb!xc和sb!int这种形式,以与Common Lisp中标准的构造相区别。

SBCL编译过程_第1张图片

1. make-host-1 stage

  第二阶段,也就是make-target-1阶段,在交叉编译的情况下,该阶段应该是运行于目标机之上。make-host-1阶段生成的*.h文件将和SBCL的原有的*.c和*.S文件一起由C编译器生成sbcl可执行文件,该可执行文件提供了操作系统服务的一个接口,以及一个垃圾回收器 (garbage collector)。同时,tools-for-build/grovel-headers.c文件被编译运行产生output/stuff-groveled-from-headers.lisp文件,其中包含了一些对系统常量和类型的描述。

SBCL编译过程_第2张图片

2. make-target-1 stage

  第三阶段,也就是make-host-2阶段。该阶段使用make-host-1阶段生成的交叉编译器xc的compile-file函数来重新编译SBCL源文件,此时编译的文件还包括make-target-1阶段产生的Lisp源文件。这一阶段会生成一系列的目标文件,或者称为”FASL文件”。

SBCL编译过程_第3张图片

3. make-host-2 stage

  第四阶段,也就是make-genesis-2阶段。该阶段使用make-host-1阶段生成的genesis来模拟加载make-host-2阶段所产生的FASL文件,然后将之保存为一个内存映像,sbcl可执行文件可以识别并加载这种内存映像。由于make-host-2阶段生成的FASL文件格式使用的是SBCL的格式,因此我们无法使用既有编译器和目标机的load函数,因为前者使用的是本地 (host) 编译器的FASL文件格式;而后者目标机的内存映像文件在该阶段还没有生成。



4. make-genesis-2 stage

  第五阶段,也就是make-target-2阶段。该阶段使用make-target-1阶段生成的sbcl可执行文件来读取make-genesis-2阶段生成的cold-sbcl.core。cold-sbcl.core中包含一个特殊的入口,用来执行一系列的操作处理Common Lisp代码,这一部分称之为”cold init”,然后SBCL代码中的sb!形式的pakcage都将被重新命名为sb-形式。src/pcl/下的CLOS实现代码也相应地将被编译并加载,最终生成一个新的内存映像output/sbcl.core。至此,SBCL的编译过程结束。



5. make-target-2 stage

一些小建议
  SBCL的整个编译过程花费的时间比较长。如果在SBCL代码上做了一些小的修改以后,想要重新编译查看效果,可以尝试使用源代码目录下的slam.sh。slam.sh将会尝试使用交叉编译器xc重新编译修改过的文件,然后使用通常的方式执行genesis和make-target-2阶段。

  当然,另一个小建议是,尽量不要去碰SBCL的代码。关于SBCL的文档实在是少的可怜;而经过一段时间的研究以后,我又发现单纯看SBCL代码实在是非常痛苦的一件事,Orz。。。

  PS1: 这篇文章主要参考了SBCL: a Sanely-Bootstrappable Common Lisp这篇文章、SBCL源代码以及CLiki.net网站。

  PS2: 这篇文章中的图片绘图工具使用的是Dia (sudo apt-get install dia)。据说yEd也很不错。

你可能感兴趣的:(数据结构,gcc,lisp,平台,编译器,loops)