我正在查找pypy项目(Python中的Python),并开始思考运行python外层的问题?当然,我猜想,它不能像古老的说法那样“乌龟一路走下去”!毕竟,python是无效的x86程序集!
很快我就想起了bootstrapping的概念,并查找了编译器引导。 “好的”,我想,“所以它可以用不同的语言编写,也可以用汇编编写”。为了性能,我确信C编译器只是从汇编中构建的。
这一切都很好,但问题仍然存在,计算机如何获得该汇编文件?!
说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?
有人可以向我解释一下吗?
答案 0 :(得分:12)
说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?
我明白你在问什么...如果我们没有C编译器并且不得不从头开始会怎么样?
答案是你必须从装配或硬件开始。也就是说,您可以在软件或硬件中构建编译器。如果全世界都没有编译器,那么现在你可以在组装中更快地完成它;然而,在当天我认为编译器实际上是专用硬件。 wikipedia article有点短暂,并没有支持我,但没关系。
我想下一个问题是今天发生了什么?那些编译器编写者多年来一直忙于编写可移植C,因此编译器应该能够自己编译。值得讨论的是汇编是什么。基本上,您接受一组语句并从中生成程序集。而已。好吧,它实际上比那更复杂 - 你可以用词法分析器和解析器做各种各样的事情,我只理解它的一小部分,但实际上,你正在寻找将C映射到汇编。
在正常操作下,编译器会生成与您的平台匹配的汇编代码,但不一定如此。它可以为您喜欢的任何平台生成汇编代码,前提是它知道如何操作。因此,在您的平台上使C工作的第一步是在现有编译器中创建目标,开始添加指令并使基本代码正常工作。
一旦完成,理论上,您现在可以交叉编译从一个平台到另一个平台。接下来的阶段是:为该平台构建内核,引导加载程序和一些基本的用户态实用程序。
然后,您可以开始编译该平台的编译器(一旦您拥有了工作用户空间以及运行构建过程所需的一切)。如果成功,您将拥有基本实用程序,工作内核,用户空间和编译器系统。你现在就好了。
请注意,在移植编译器的过程中,您可能还需要为该平台编写汇编程序和链接程序。为了简化描述,我省略了它们。
如果感兴趣,Linux from Scratch是一个有趣的读物。它没有告诉你如何从头开始创建一个新目标(这是非常重要的) - 它假设你要为现有的已知目标构建,但它确实向你展示了如何交叉编译基本要素并开始构建系统。
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
在那里你可以看到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;
//.......
}
我认为你明白了。这些是python和java世界的汇编语言,即python解释器和java编译器分别如何思考。
值得一读的其他内容是JonesForth。这既是一个有效的解释器和一个教程,我不能推荐它足以考虑“如何执行”以及如何编写一个简单,轻量级的语言。
答案 1 :(得分:6)
现在,C编译器(几乎?)完全用C语言编写(或者更高级语言 - 例如Clang是C ++)。编译器从包含手写汇编代码中获得的收益甚微。花费大部分时间的事情和它们一样慢,因为它们解决了非常困难的问题,其中“硬”意味着“大计算复杂性” - 在汇编中重写最多会带来不断的加速,但那些在那里并不重要水平。为了提高性能,我确信C编译器只是从汇编中构建的。
此外,大多数编译器都希望具有高可移植性,因此前端和中端的体系结构特定技巧是不可能的(在后端,它们也不可取,因为它们可能会破坏交叉编译)。
说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?
当您安装操作系统时,(通常)没有C编译器运行。安装CD中充满了针对该架构的易于编译的二进制文件。如果包含一个C编译器(就像许多Linux发行版的情况一样),那也是一个已经编译好的可编译器。那些让你构建自己的内核等的发行版也至少包含一个可执行文件 - 编译器。当然,除非你必须使用C编译器在任何现有的安装上编译自己的内核。
如果“新CPU”是指一种新的架构,它不能向后兼容任何尚未支持的架构,那么自托管编译器可以遵循通常的移植过程:首先为该新目标编写后端,然后编译自己对于它,突然间你在新平台上有一个成熟的编译器和一个经过实战编译的(编译完整的编译器)本机后端。
答案 2 :(得分:3)
如果您购买带有预安装操作系统的新机器,它甚至不需要在任何地方都包含编译器,因为所有可执行代码都是在其他机器上编译的,无论谁提供操作系统 - 您的机器都没有不需要自己编译任何东西。
如果你有一个全新的CPU架构,你怎么做到这一点?在这种情况下,您可能首先为新的CPU体系结构编写新的代码生成后端(“目标”),以便在其他平台(“主机”)上运行的现有C编译器 - {{3 }}
一旦您的交叉编译器(在主机上运行)运行良好,可以生成将在目标上运行的正确编译器(以及必要的库等),那么您可以在目标平台上自行编译编译器,最终得到一个目标本机编译器,它在目标上运行并生成在目标上运行的代码。
这与新语言的原理相同:你必须用现有语言编写代码,你有一个工具链,它会将你的新语言编译成你可以使用的东西(我们称之为“引导编译器”) “)。一旦你能够很好地工作,就可以用新语言编写一个编译器(“真正的编译器”),然后使用bootstrap编译器编译真正的编译器。此时,您正在用新语言编写新语言的编译器,并且您的语言被称为“自托管”。