Clang/LLVM编译OC代码

2017/03/04

Chris Lattner 就是那个跳槽去特斯拉的Swift之父,他很久以前写了一个用于 开发一个编译器的工具套件叫 Low Level Virtual Machine 也就是常常看见的 LLVMClang 就是 LLVM 着一套工具中的一个。LLVM可以用于常规编译器,JIT编译器,汇编器,调试器,静态分析工具等一系列跟编程语言相关的工作。

Clang 的编译速度惊人,比 GCC 快3倍,LLVM 也提供一种代码编写良好的中间表示 IR,可以作为多种语言的后端,提供语言无关的优化同时还能够方便的针对多种 CPU 的代码生成。

编译OC流程

下面这部分简单的代码将会如何被编译成可执行的呢?

#import <Foundation/Foundation.h>
#define DEFINEEight 8

int main(){
    @autoreleasepool {
        int eight = DEFINEEight;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = eight + six;
        NSLog(@"%@ rank %d", site, rank);
    }
    return 0;
}

1.预处理

处理以#开头的的预编译指令 #include#define#if#else

clang -E main.m

# 1 "/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 185 "/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 2 "main.m" 2

int main(){
    @autoreleasepool {
        int eight = 8;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = eight + six;
        NSLog(@"%@ rank %d", site, rank);
    }
    return 0;
}

2.词法分析

代码通过 Clang Static Analyzer 被识别为 关键词 标识符 字面量( int,string)特殊符号(+,=),他们可以被叫为记号 Token,他们将会在生成语法树被使用到。

clang -fmodules -E -Xclang -dump-tokens main.m

annot_module_include '#import <Fo'      Loc=<main.m:2:1>
int 'int'    [StartOfLine]  Loc=<main.m:5:1>
identifier 'main'    [LeadingSpace] Loc=<main.m:5:5>
l_paren '('     Loc=<main.m:5:9>
r_paren ')'     Loc=<main.m:5:10>
l_brace '{'  [LeadingSpace] Loc=<main.m:5:12>
at '@'   [StartOfLine] [LeadingSpace]   Loc=<main.m:6:5>
identifier 'autoreleasepool'        Loc=<main.m:6:6>
l_brace '{'  [LeadingSpace] Loc=<main.m:6:22>
identifier 'NSString'    [StartOfLine] [LeadingSpace]   Loc=<main.m:7:9>
star '*'     [LeadingSpace] Loc=<main.m:7:18>
identifier 'a'      Loc=<main.m:7:19>
equal '='    [LeadingSpace] Loc=<main.m:7:21>
at '@'   [LeadingSpace] Loc=<main.m:7:23>
string_literal '"aaa"'      Loc=<main.m:7:24>
semi ';'        Loc=<main.m:7:29>
identifier 'NSLog'   [StartOfLine] [LeadingSpace]   Loc=<main.m:8:9>
l_paren '('     Loc=<main.m:8:14>
at '@'      Loc=<main.m:8:15>
string_literal '"hi %@"'        Loc=<main.m:8:16>
comma ','       Loc=<main.m:8:23>
identifier 'a'      Loc=<main.m:8:24>
r_paren ')'     Loc=<main.m:8:25>
semi ';'        Loc=<main.m:8:26>
r_brace '}'  [StartOfLine] [LeadingSpace]   Loc=<main.m:9:5>
return 'return'  [StartOfLine] [LeadingSpace]   Loc=<main.m:10:5>
numeric_constant '0'     [LeadingSpace] Loc=<main.m:10:12>
semi ';'        Loc=<main.m:10:13>
r_brace '}'  [StartOfLine]  Loc=<main.m:11:1>
eof ''      Loc=<main.m:11:2>

每一行一个token 从左到右看,后面的 StartOfLine LeadingSpace Loc 都是位置信息

3.语法分析

生成以 表达式 为节点的 语法树,最小的表达式可以是 字面亮 也就是上面的Token ,按照特定的语法规则,将它们组织起来。生成语法树 Abstract Syntax Tree (AST)

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

TranslationUnitDecl 0x7fa80f018ad0 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x7fa80f018fc8 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x7fa80f018d20 '__int128'
|-TypedefDecl 0x7fa80f019028 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x7fa80f018d40 'unsigned __int128'
|-TypedefDecl 0x7fa80f0190b8 <<invalid sloc>> <invalid sloc> implicit SEL 'SEL *'
| `-PointerType 0x7fa80f019080 'SEL *'
|   `-BuiltinType 0x7fa80f018f30 'SEL'
|-TypedefDecl 0x7fa80f019198 <<invalid sloc>> <invalid sloc> implicit id 'id'
| `-ObjCObjectPointerType 0x7fa80f019140 'id' imported
|   `-ObjCObjectType 0x7fa80f019110 'id' imported
|-TypedefDecl 0x7fa80f019278 <<invalid sloc>> <invalid sloc> implicit Class 'Class'
| `-ObjCObjectPointerType 0x7fa80f019220 'Class'
|   `-ObjCObjectType 0x7fa80f0191f0 'Class'
|-ObjCInterfaceDecl 0x7fa80f0192c8 <<invalid sloc>> <invalid sloc> implicit Protocol
|-TypedefDecl 0x7fa80f019618 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0x7fa80f019430 'struct __NSConstantString_tag'
|   `-Record 0x7fa80f019390 '__NSConstantString_tag'
|-TypedefDecl 0x7fa80f0196a8 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x7fa80f019670 'char *'
|   `-BuiltinType 0x7fa80f018b60 'char'
|-TypedefDecl 0x7fa80f047978 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'struct __va_list_tag [1]'
| `-ConstantArrayType 0x7fa80f047920 'struct __va_list_tag [1]' 1 
|   `-RecordType 0x7fa80f0197a0 'struct __va_list_tag'
|     `-Record 0x7fa80f0196f8 '__va_list_tag'
|-ImportDecl 0x7fa80f0486b0 <main.m:2:1> col:1 implicit Foundation
|-FunctionDecl 0x7fa80f048738 <line:5:1, line:11:1> line:5:5 main 'int ()'
| `-CompoundStmt 0x7fa80f393998 <col:12, line:11:1>
|   |-ObjCAutoreleasePoolStmt 0x7fa80f393950 <line:6:5, line:9:5>
|   | `-CompoundStmt 0x7fa80f393928 <line:6:22, line:9:5>
|   |   |-DeclStmt 0x7fa80f3a3b38 <line:7:9, col:29>
|   |   | `-VarDecl 0x7fa80f3a3580 <col:9, col:24> col:19 used a 'NSString *' cinit
|   |   |   `-ObjCStringLiteral 0x7fa80f3a3648 <col:23, col:24> 'NSString *'
|   |   |     `-StringLiteral 0x7fa80f3a3618 <col:24> 'char [4]' lvalue "aaa"
|   |   `-CallExpr 0x7fa80f3938c0 <line:8:9, col:25> 'void'
|   |     |-ImplicitCastExpr 0x7fa80f3938a8 <col:9> 'void (*)(id, ...)' <FunctionToPointerDecay>
|   |     | `-DeclRefExpr 0x7fa80f3a3b50 <col:9> 'void (id, ...)' Function 0x7fa80f3a3670 'NSLog' 'void (id, ...)'
|   |     |-ImplicitCastExpr 0x7fa80f3938f8 <col:15, col:16> 'id':'id' <BitCast>
|   |     | `-ObjCStringLiteral 0x7fa80f393800 <col:15, col:16> 'NSString *'
|   |     |   `-StringLiteral 0x7fa80f3a3bb8 <col:16> 'char [6]' lvalue "hi %@"
|   |     `-ImplicitCastExpr 0x7fa80f393910 <col:24> 'NSString *' <LValueToRValue>
|   |       `-DeclRefExpr 0x7fa80f393820 <col:24> 'NSString *' lvalue Var 0x7fa80f3a3580 'a' 'NSString *'
|   `-ReturnStmt 0x7fa80f393980 <line:10:5, col:12>
|     `-IntegerLiteral 0x7fa80f393960 <col:12> 'int' 0
`-<undeserialized declarations>

TranslationUnitDecl 是根节点,表示一个源文件。Decl 表示一个声明,Expr 表示表达式,Literal 表示字面量是特殊的 Expr,Stmt 表示语句。

4.中间语言生成

将语法树翻译成 LLVM IR 中间代码,做为 LLVM Backend 输入的桥接语言。这样做的好处在前言里也提到了,方便 LLVM Backend 给多语言做相同的优化,做到语言无关。

  • 各种类,方法,成员变量等的结构体的生成,并将其放到对应的Mach-O的section中。
  • Non-Fragile ABI 合成 OBJC_IVAR_$_ 偏移值常量。
  • ObjCMessageExpr 翻译成相应版本的 objc_msgSend,super 翻译成 objc_msgSendSuper。
  • strong,weak,copy,atomic 合成 @property 自动实现 setter 和 getter。 @synthesize 的处理。
  • 生成 block_layout 数据结构
  • __block 和 __weak
  • _block_invoke
  • ARC 处理,插入 objc_storeStrong 和 objc_storeWeak 等 ARC 代码。 ObjCAutoreleasePoolStmt 转 objc_autorealeasePoolPush / Pop。自动添加 [super dealloc]。给每个 ivar 的类合成 .cxx_destructor 方法自动释放类的成员变量。

  • 深入剖析 iOS 编译 Clang LLVM
  • 基于clang插件的一种iOS包大小瘦身方案

Post Directory