如果我说,C可以像Java一样被虚拟机解释执行,也可以对热点代码使用Just-In-Time(JIT)技术编译,也可以实现“一次编译,到处运行”,你信吗?
少侠且慢动手,听我解释!无论是相对高级的语言,如Python、Java(这里说的高级是说接近自然语言,而不是字面意思),还是相对底层的C/C++,甚至是汇编语言,都只不过人们对一个问题描述的文本体现。Java所具备的内存托管、垃圾回收机制,归根到底,是Java虚拟机(JVM)赋予它的,因此只要能让JVM接受,不管是什么语言都能享受Java的待遇,例如Groovy、Kotlin、Scala等语言。
多种不同语言都能运行在JVM上的原因,是因为它们都被编译成了一种JVM能识别的中间表示形式(IR,Intermediate Representation)——字节码(bytecode),JVM识别字节码并翻译到机器码,最终实现了代码的执行。
对于JVM,Java摸得,我C语言就摸不得?只要把C编译到字节码,C++就能实现内存托管、“一次编译,到处运行”。我们先不管为什么要大费周章,也不管这样做得利弊得失,我们只是为一个想法找到了一条切实可行得路线。有趣得是,很多事情当你想到得时候,别人已经在做,或者已经完成了。将C++编译成运行在JVM上得字节码,并非仅仅存在于脑洞中,有好事者已经将它实现了:它就是LLJVM项目,代码开源地址为 https://github.com/davidar/lljvm 有兴趣得小伙伴可以前去观摩。从这个角度来说,那些论坛上喋喋不休的争论哪个语言好哪个语言优秀的可以歇一歇了,这些争论其实是没有意义的,你们所骄傲的是编译器赋予你们的,有什么样的编译器,就会有什么样的语言。语言只是个工具,工具是没有好坏之分的,只有顺不顺手。
LLJVM怎么把C编译到JVM上呢?主要有以下三步:
兜兜转转一圈之后,C语言编写的代码逻辑终于在JVM上执行了。除了将C代码编译到字节码,目前已经实现的方案中还有另外一种使得C代码可以解释执行的方案,它就是LLVM提供了一个名叫lli
的解释器,专门用来解释执行LLVM IR,与JVM类似,lli也有Just-In-Time(JIT)等功能。由此,我们引出了今天的主角:LLVM。
从编译说起
编译器,就是一个将源代码编译到目标机器代码的一个程序(图1)。
Figure 1 Compiler
经典的编译过程分为词法分析、语法分析、优化、代码生成等阶段。而一个经典编译器就可以根据这些阶段分成三部分(图2):
- 前端(Frontend):负责此法分析、语法分析,产生一个中间结果;
- 优化器(Optimizer):负责代码逻辑优化,比如做一些等效代换、去掉一些无用代码等,使得执行时间更短;
- 后端(Backend):负责将优化后的中间结果翻译成目标机器可以执行的机器指令。
其中根据需要,优化部分可以没有。