boottrpping如何为gcc工作?

Raz*_*orm 10 architecture compiler-construction boot gcc bootstrapping

我正在查找pypy项目(Python中的Python),并开始思考运行python外层的问题?当然,我猜想,它不能像古老的说法那样"乌龟一路走下去"!毕竟,python是无效的x86程序集!

很快我就想起了bootstrapping的概念,并查找了编译器引导."好的",我想,"所以它可以用不同的语言编写,也可以用汇编编写".为了性能,我确信C编译器只是从汇编中构建的.

这一切都很好,但问题仍然存在,计算机如何获得该汇编文件?!

假设我买了一个没有任何东西的新cpu.在第一次操作期间,我希望安装一个运行C的操作系统.什么运行C编译器?BIOS中是否有微型C编译器?

谁可以给我解释一下这个?

小智 12

假设我买了一个没有任何东西的新cpu.在第一次操作期间,我希望安装一个运行C的操作系统.什么运行C编译器?BIOS中是否有微型C编译器?

我理解你在问什么...如果我们没有C编译器并且必须从头开始会发生什么?

答案是你必须从装配或硬件开始.也就是说,您可以在软件或硬件中构建编译器.如果全世界都没有编译器,那么现在你可以在组装中更快地完成它; 然而,在当天我认为编译器实际上是专用硬件.在维基百科的文章是有点短,不支持我上,不过没关系.

我想下一个问题是今天发生了什么?那些编译器编写者多年来一直忙于编写可移植C,因此编译器应该能够自己编译.值得讨论的是汇编是什么.基本上,您接受一组语句并从中生成程序集.而已.好吧,它实际上比那更复杂 - 你可以用词法分析器和解析器做各种各样的事情,我只理解它的一小部分,但实际上,你正在寻找将C映射到汇编.

在正常操作下,编译器会生成与您的平台匹配的汇编代码,但它不需要.它可以为您喜欢的任何平台生成汇编代码,前提是它知道如何操作.因此,在您的平台上使C工作的第一步是在现有编译器中创建目标,开始添加指令并使基本代码正常工作.

一旦完成,理论上,您现在可以从一个平台交叉编译到另一个平台.接下来的阶段是:为该平台构建内核,引导加载程序和一些基本的用户态实用程序.

然后,您可以编译该平台的编译器(一旦您拥有了工作用户空间以及运行构建过程所需的一切).如果成功,您将拥有基本实用程序,工作内核,用户空间和编译器系统.你现在好了.

请注意,在移植编译器的过程中,您可能还需要为该平台编写汇编程序和链接程序.为了简化描述,我省略了它们.

如果有兴趣,Scratch的Linux是一本有趣的读物.它没有告诉你如何从头开始创建一个新目标(这是非常重要的) - 它假设你要为现有的已知目标构建,但它确实向你展示了如何交叉编译基本要素并开始构建系统.

Python实际上并没有组装到程序集.首先,正在运行的python程序会跟踪对象的引用计数,这是cpu不会为你做的事情.但是,基于指令的代码的概念也是Python的核心.玩这个:

>>> def hello(x, y, z, q):
...     print "Hello, world"
...     q()
...     return x+y+z
... 
>>> import dis
dis.dis(hello)


  2           0 LOAD_CONST               1 ('Hello, world')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_FAST                3 (q)
              8 CALL_FUNCTION            0
             11 POP_TOP             

  4          12 LOAD_FAST                0 (x)
             15 LOAD_FAST                1 (y)
             18 BINARY_ADD          
             19 LOAD_FAST                2 (z)
             22 BINARY_ADD          
             23 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

在那里你可以看到Python如何看待你输入的代码.这是python字节码,即python的汇编语言.如果你喜欢实现这种语言,它实际上有自己的"指令集".这是虚拟机的概念.

Java有着完全相同的想法.我拿了一个类函数并运行javap -c class得到这个:

invalid.site.ningefingers.main:();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iconst_0
   3:   istore_1
   4:   iload_1
   5:   aload_0
   6:   arraylength
   7:   if_icmpge   57
   10:  getstatic   #2; 
   13:  new #3; 
   16:  dup
   17:  invokespecial   #4; 
   20:  ldc #5; 
   22:  invokevirtual   #6; 
   25:  iload_1
   26:  invokevirtual   #7; 
   //.......
}
Run Code Online (Sandbox Code Playgroud)

我认为你明白了.这些是python和java世界的汇编语言,即python解释器和java编译器分别如何思考.

值得一读的其他东西是JonesForth.这既是一个工作的解释器和一个教程,我不能推荐它足以考虑"如何执行"以及如何编写一个简单,轻量级的语言.


小智 6

为了性能,我确信C编译器只是从汇编中构建的.

如今,C编译器(几乎?)完全用C语言编写(或者更高级语言 - 例如Clang是C++).编译器从包含手写汇编代码中获得的收益甚微.花费大部分时间的事情和它们一样慢,因为它们解决了非常困难的问题,"硬"意味着"大计算复杂性" - 在汇编中重写最多会带来不断的加速,但那些在那里并不重要水平.

此外,大多数编译器都希望具有高可移植性,因此前端和中端的体系结构特定技巧是不可能的(在后端,它们也不可取,因为它们可能会破坏交叉编译).

假设我买了一个没有任何东西的新cpu.在第一次操作期间,我希望安装一个运行C的操作系统.什么运行C编译器?BIOS中是否有微型C编译器?

当您安装操作系统时,(通常)没有C编译器运行.安装CD中充满了针对该架构的易于编译的二进制文件.如果包含一个C编译器(就像许多Linux发行版的情况一样),那也是一个已经编译好的可编译器.那些让你构建自己的内核等的发行版也至少包含一个可执行文件 - 编译器.当然,除非你必须使用C编译器在任何现有的安装上编译自己的内核.

如果"新CPU"意味着一个新的架构不能向后兼容任何尚未支持的东西,那么自托管编译器可以遵循通常的移植过程:首先为该新目标编写后端,然后自己编译,突然间你在新平台上得到了一个成熟的编译器,其中包含一个经过强制攻击(编译完整编译器)的本机后端.