深入了解Erlang编译器(2012)

2020-06-09 19:24:37

Erlang是一个复杂的系统,我不能在一篇简短的文章中公正地描述它的内部工作方式,但我想深入了解编译和加载模块时发生的事情。与大多数编译器一样,第一步是将文本源代码转换为抽象语法树,但这并不重要。有趣的是,代码经历了三个主要表示形式,您可以查看每一个表示形式。

Erlang的随意作用域规则在函数式语言中是独一无二的。您可以随时随地引入变量,不需要大张旗鼓,而且没有显式作用域造成的缓慢缩进。在幕后,这太离奇了,所以语法树被转换为Core Erlang。核心Erlang看起来很像Haskell或ML,在";let";语句中仔细引用了所有变量。您可以使用shell中的以下命令查看模块的Core Erlang表示:

下一个重大转变是从Core Erlang到基于寄存器的BEAM虚拟机的代码。BEAM的文档很少,但它很像为Prolog开发的Warren Abstract Machine(但不需要回溯)。如果您编写简短的模块并用以下方式检查它们,BEAM并不是很难弄清楚:

示例模块的反汇编BEAM代码编写为example.S.。理解BEAM的关键在于有两组寄存器:一组用于传递参数(";x&34;寄存器),另一组用作函数内的局部变量(";y";寄存器)。

虚拟波束代码是编译器的最终输出,但它仍然不是系统执行的代码。如果您查看Erlang运行时的源代码,您会发现Beam_load.c有6000多行代码。六千行来加载一个模块?这是因为光束加载器做的事情比它的名字透露的更多。

有一个虚拟机指令的优化传递,一些专门用于某些情况,而另一些则组合成超级指令。检查值是否由三个元素组成的元组是通过一对梁操作来完成的:is_tuple和is_ality。波束加载器将这些转换成一条超级指令:is_tuple_of_ality。您可以使用以下命令查看梁代码的简化表示:

反汇编代码写入example.dis。(请注意,必须加载该模块,因此在发出上述命令之前对其进行编译。)。

加载器还将BEAM字节码转换为线程代码:按顺序跳转到的地址列表。现在我该怎么处理这个操作码呢?只需抓取并跳转,抓取并跳转即可。如果您想更多地了解线程化代码,请参考Forth世界。

线程化代码利用标签作为GCC的值扩展。如果您使用另一个编译器(如Visual C++)构建束流仿真器,它将依赖于使用巨大的SWITCH语句进行指令分派,并且会对性能造成重大影响。