WebAssembly 到底处于编译阶段的哪个环节?

阅读数:1049 2019 年 8 月 9 日 08:13

WebAssembly到底处于编译阶段的哪个环节?

相信不少人听说过 WebAssembly,它是由 Google、Microsoft、Mozilla、Apple 等几家大公司合作发起的一个关于面向 Web 的通用二进制和文本格式的项目。现在就让我们一步步揭开 WebAssembly 的神秘面纱,并亲自动手将 WebAssembly 应用在实际业务中。

引言

众所周知,无论是 Chrome、Firefox、Safari、Edge 还是其他浏览器,能够运行的语言就是 Javascript。为了能够让其他语言的代码在浏览器中运行,WebAssembly 被创造出来,详见《WebAssembly 是什么》。我们并不需要亲自编写 WebAssembly 的代码,唯一要做的就是把其他高级语言编译成 WebAssembly 即可,这样便能复用大量其他语言现有的代码。而且 WebAssembly 还拥有比 JavaScript 更好的性能,能够更快的加载和执行。

WebAssembly到底处于编译阶段的哪个环节?

那为啥 WebAssembly 的性能就一定会比 JavaScript 好很多呢?具体原因还得看下它们到底是处于编译阶段哪个环节了。

编译步骤

作为程序员的我们,每天都在用各种高级语言写源代码。但要让机器能读懂这些字符串代码,就得靠编译系统一步步把它们编译成目标代码。

WebAssembly到底处于编译阶段的哪个环节?

预编译

预编译首先会处理源代码中那些以#开头(如#include、#define 等)的预编译指令,在编译开始前就先对原始的代码文件进行调整。经过了预编译之后,你写的代码其实已经有了很大的变化。

词法分析

词法分析是计算机科学中将字符序列转换为单词(Token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(lexical analyzer,简称 lexer),也叫扫描器(Scanner),供语法分析器调用。

词法分析阶段是编译过程的第一个阶段,任务是从左到右以字符流的方式逐行扫描源代码,然后根据构词规则一一识别关键词、标志符、字面量、运算符等,并分割成一个个按顺序排列的标记 Token。

语法分析

语法分析是根据某种给定的形式文法对由单词序列(如英语单词序列)构成的输入文本进行分析并确定其语法结构的一种过程。进行语法分析的程序或者函数叫作语法分析器(parser),供语义分析调用。

语法分析阶段是编译过程的一个逻辑阶段,任务是在词法分析分割出来的标记 Token 的基础上,将 Token 序列组合成各类语法短语如“程序”、“语句”、“表达式”等。

语义分析

语义分析是编译过程的一个最实质性的阶段,任务是对结构上正确的源代码进行上下文有关性质的审查,进行类型审查。

其实经过语法分析之后就能初步得到了抽象语法树,树上的每个节点都是一个表达式,但此时还不确定是否有意义。于是需要通过语义分析,遍历整个抽象语法树,把每个节点的表达式都标识类型,并且验证是否合法。

抽象语法树

抽象语法树(Abstract Syntax Tree,AST),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构,如下图所示:

WebAssembly到底处于编译阶段的哪个环节?

从语法分析开始,就已经生成了初步的抽象语法树。经过了语义分析之后,抽象语法树又变得更加完善,自此最终版的抽象语法树 AST 已经建立完成。

中间码

经过前面几个阶段之后,我们已经有了最终版的 AST。但是该 AST 并不能完美运行在各个硬件平台上,因为不同平台的汇编处理都是不一样的。

于是便在 AST 和多个平台的汇编代码中间,抽象出了一个中间码(Intermediate Representation),在中间码的设计里抹平了硬件平台造成的差异。中间码的强大之处在于跨平台,与语言无关。

目标代码

计算机是通过汇编指令来执行操作的,如移动到内存某个地址、位移多少个字节等。因此需要通过目标平台的汇编器,由 AST 转成的中间码生成目标平台的汇编代码,这些才是机器能读懂的目标代码。

WebAssembly到底处于编译阶段的哪个环节?

编译前端与后端

前面介绍了这么多编译步骤,其实我们可以把中间码当成一个分界线,中间码以前的环节叫做编译前端,中间码以后的环节叫做编译后端。

编译前端

编译前端包括预编译、词法分析、语法分析、语义分析、抽象语法树等,专门用来处理语言专属特性。虽然不同语言的词法关键字、语法规则、语义分析的函数类型校验可能都不一样,甚至某些语言都没有预编译这个环节,但每个语言都可以开发一套编译前端,按照标准生成的统一中间码就可以无缝对接给任意编译后端,这就是语言无关。

编译后端

编译后端这里只包含了目标代码生成部分,其实还应该包括将目标代码链接成为可执行文件的环节。编译后端专门负责处理各个平台的差异,根据不同语言生成的标准中间码,生成对应的目标代码,这就是平台无关。

WebAssembly到底处于编译阶段的哪个环节?

编译工具

GCC

GCC(GNU Compiler Collection,GNU 编译器套装),包含了编译前端与编译后端所有模块。其中,编译前端部分支持 C、C++、Fortran、Pascal、Objective-C、Java 等语言,编译后端支持 x86、mips、Alpha、ARM、AVR、IA-64、SPARC、PowerPC 等 30 多种平台。

GCC 虽然被广泛的使用,但目前也面临了危机。后起之秀的 Clang / LLVM,大有全面赶超 GCC 的势头。

Clang / LLVM

Clang 是一个 C++ 编写、基于 LLVM、发布于 LLVM BSD 许可证下的 C / C++ / Objective-C / Objective-C++ 的编译器。那为啥已经有了 GCC 后还要开发 Clang 呢?Clang 相比于 GCC 有什么优势呢?因为 Clang 是一个高度模块化开发的轻量级编译器,它的编译速度快、占用内存小、非常方便进行二次开发。

而有了 Clang 这个编译器前端还不够,于是就跟 LLVM 这个编译器后端组合成一个完整的编译器套件,如下图所示:

WebAssembly到底处于编译阶段的哪个环节?

WebAssembly 在编译环节的位置

前面说了这么多,但还是不知道 WebAssembly 处于编译阶段哪个环节啊?心急吃不了热豆腐,凡事都要对基础知识有一定了解之后,才能茅塞顿开。

通过下图你便可以一目了然 WebAssembly 所处的位置,它能做到像 Java 字节码一样,一次编译到处运行,具有跨平台特性。以此同时,作为中间码的 WebAssembly 直接省略编译前端的步骤,而 JavaScript 需要实时编译,相比之下性能优势显著。

WebAssembly到底处于编译阶段的哪个环节?

参考资料

评论

发布