编译器做些什么?
本文主要探讨一下编译器主要做些什么,以及如何有效的利用编译器。
简单的说,编译器有两个职责:把 Objective-C 代码转化成低级代码,以及对代码做分析,确保代码中没有任何明显的错误。
现在,Xcode 的默认编译器是 clang。本文中我们提到的编译器都表示 clang。clang 的功能是首先对 Objective-C 代码做分析检查,然后将其转换为低级的类汇编代码:LLVM Intermediate Representation(LLVM 中间表达码)。接着 LLVM 会执行相关指令将 LLVM IR 编译成目标平台上的本地字节码,这个过程的完成方式可以是即时编译 (Just-in-time),或在编译的时候完成。
LLVM 指令的一个好处就是可以在支持 LLVM 的任意平台上生成和运行 LLVM 指令。例如,你写的一个 iOS app, 它可以自动的运行在两个完全不同的架构(Inter 和 ARM)上,LLVM 会根据不同的平台将 IR 码转换为对应的本地字节码。
LLVM 的优点主要得益于它的三层式架构 – 第一层支持多种语言作为输入(例如 C, ObjectiveC, C++ 和 Haskell),第二层是一个共享式的优化器(对 LLVM IR 做优化处理),第三层是许多不同的目标平台(例如 Intel, ARM 和 PowerPC)。在这三层式的架构中,如果你想要添加一门语言到 LLVM 中,那么可以把重要精力集中到第一层上,如果想要增加另外一个目标平台,那么你没必要过多的考虑输入语言。在书 The Architecture of Open Source Applications 中 LLVM 的创建者 (Chris Lattner) 写了一章很棒的内容:关于 LLVM 架构。
在编译一个源文件时,编译器的处理过程分为几个阶段。要想查看编译 hello.m 源文件需要几个不同的阶段,我们可以让通过 clang 命令观察:
% clang -ccc-print-phases hello.m
0: input, "hello.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, assembler
3: assembler, {2}, object
4: linker, {3}, image
5: bind-arch, "x86_64", {4}, image
本文我们将重点关注第一阶段和第二阶段。在文章 Mach-O Executables 中,Daniel 会对第三阶段和第四阶段进行阐述。
预处理
每当编源译文件的时候,编译器首先做的是一些预处理工作。比如预处理器会处理源文件中的宏定义,将代码中的宏用其对应定义的具体内容进行替换。
例如,如果在源文件中出现下述代码:
1
| #import <Foundation/Foundation.h>;
|
预处理器对这行代码的处理是用 Foundation.h 文件中的内容去替换这行代码,如果 Foundation.h 中也使用了类似的宏引入,则会按照同样的处理方式用各个宏对应的真正代码进行逐级替代。
这也就是为什么人们主张头文件最好尽量少的去引入其他的类或库,因为引入的东西越多,编译器需要做的处理就越多。例如,在头文件中用:
Read on →