iOS开发初窥LLVM语法

·  阅读 701

LLVM是什么?

LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,开源代码在GitHub

iOS中,不管是Swift还是OC,他们的中间表示IR都是LLVM,都是构建在LLVM架构上的,标准的三段式设计

如何生成LLVM文件

因为Swift还是OC的编译工具是不一样的,所以两者生成LLVM文件的命令也是不一样的。

  • OC的编译工具是clang
clang -S -emit-llvm 文件名
复制代码
  • Swift用的是swiftc
swiftc -emit-ir 文件名
复制代码

为了方便,你可以写脚本放在Xcode里,方便快速生成LLVM,脚本方式参考我生成SIL文件的文章,脚本代码替换成:

  • OC
clang -S -emit-llvm CLLVM/main.c > ./main.ll && open main.ll
复制代码
  • Swift
swiftc -emit-ir SwiftSIL/main.swift | xcrun swift-demangle > ./main.ll && open main.ll
复制代码

生成第一个LLVM文件

Xcode下生成一个用CmacOS项目,贴上如下代码:

struct munger_struct{
    int f1;
    int f2;
};
void munge(struct munger_struct *P){
    P[0].f1 = P[1].f1 + P[2].f2;
}
struct munger_struct* array[3];
复制代码

然后生成LLVM文件:

; 声明结构体类型
%struct.munger_struct = type { i32, i32 }
; 声明数组类型及大小
@array = common global [3 x %struct.munger_struct*] zeroinitializer, align 16
; Function Attrs: noinline nounwind optnone ssp uwtable
define void @munge(%struct.munger_struct* %0) #0 {
  ; 开辟空间存放传进来的struct.munger_struct*指针,%2放的是开辟这块空间的地址,相当于2级指针了
  %2 = alloca %struct.munger_struct*, align 8
  store %struct.munger_struct* %0, %struct.munger_struct** %2, align 8
  ; 从%2中取出结构体的数组指针
  %3 = load %struct.munger_struct*, %struct.munger_struct** %2, align 8
  ; 从数组指针中取出下标为1的结构体munger_struct的指针
  %4 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %3, i64 1
  ; 从结构体中取出第一个元素的指针
  %5 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %4, i32 0, i32 0
  ; 将元素指针的值取出来
  %6 = load i32, i32* %5, align 4
  ; 重复上面操作
  %7 = load %struct.munger_struct*, %struct.munger_struct** %2, align 8
  %8 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %7, i64 2
  %9 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %8, i32 0, i32 1
  %10 = load i32, i32* %9, align 4
  %11 = add nsw i32 %6, %10
  %12 = load %struct.munger_struct*, %struct.munger_struct** %2, align 8
  %13 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %12, i64 0
  %14 = getelementptr inbounds %struct.munger_struct, %struct.munger_struct* %13, i32 0, i32 0
  store i32 %11, i32* %14, align 4
  ret void
}
复制代码

LLVM语法解析

  • i:表示的是整形Interger,后面会跟一个数字,表示是多少位的整形。
  • align:对齐大小
  • Type*:表示是Type类型的指针
  • alloca:开辟堆空间
  • load:从指针指向的地址取值出来
  • store:赋值操作
  • getelementptr:从数组或者结构体中取元素指针出来,看下语法:
<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <id x>}*
<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
复制代码

inbounds后面跟的是步长类型,可以省略inbounds,然后接着跟的是基地址的指针,最后跟的是需要偏移的数量。数组的getelementptr比较好理解,但是结构体的getelementptr最后面跟着两个偏移值,第一个偏移值指的是相对于自己本身的偏移量,第二个指的是结构体内部元素的下标。

  • %T = type {<type list>}:这种表示的是结构体
  • [<elementnumber> x <elementtype>]:这个表示的是数组,elementnumber表示的是元素数量,elementtype表示的是数组元素类型。

结语

其实耐下心来看,发现语法比想象中的简单。

读懂了LLVM文件,就能深入探究iOS的底层实现了

分类:
iOS
标签: