LLVM IR格式的基本介绍

LLVM IR以两种格式存储在磁盘上:

1.位码(.bc文件)

2.汇编文本(.ll文件)

    以sum.c源代码为例

int sum(int a, int b){
  return a+b;
}

使用Clang生成位码,命令如下:

$ clang sum.c -emit-llvm -c -o sum.bc

使用Clang生成汇编文本,命令如下:

$ clang sum.c -emit-llvm -S -c -o sum.ll
$ clang -emit-llvm sum.c -S -o sum.ll

还可以汇编上述的LLVM IR汇编文本,创建相应的位码,命令如下:

$ llvm-as sum.ll -o sum.bc

相反,要从位码转换为IR汇编文本,可以使用反汇编程序:

$ llvm-dis sum.bc -o sum.ll

观察LLVM IR汇编码文件sum.ll:

; ModuleID = 'sum.c'
source_filename = "sum.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @sum(i32 %0, i32 %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  store i32 %0, i32* %3, align 4
  store i32 %1, i32* %4, align 4
  %5 = load i32, i32* %3, align 4
  %6 = load i32, i32* %4, align 4
  %7 = add nsw i32 %5, %6
  ret i32 %7
}

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 10.0.0-4ubuntu1 "}

       整个LLVM文件的内容(无论是汇编码还是位码)被定义为一个LLVM模块。模块是LLVM IR顶层数据结构。每个模块包含一系列函数,每个函数由包含一系列指令的一系列基本块组成。模块还包含用于支持该模型的外围实体,如全局变量、目标数据布局、外部函数原型以及数据结构声明。

       LLVM局部变量与汇编语言中的寄存器类似,可以用任何以%符号开头的名称命名。因此,

%7 = add nsw i32 %5, %6

这一指令将执行两个局部变量%5和%6的加法,并将这个结果置于新的局部变量%32中。用户可以自由地给这些值命名。

       通过这个简短的例子,可以看到LLVM如何表达其基本属性:

  • 它使用静态单赋值(SSA)形式。该形式下每个变量都不会被重新赋值,每个变量只有唯一一条定义它的赋值语句。每次使用一个变量都可以立即回溯到负责其定义的唯一指令。使用SSA形式导致“使用定义链”(use-def链,既可以达到使用处的所有定义/赋值语句的集合)的生成变得非常简单。这个简化操作具有巨大的价值。UD链是经典优化(如常量传播和冗余表达式消除)的前提条件,如果LLVM没有使用SSA形式,则需要单独的数据流分析来计算UD链。
  • 代码被组织成三地址指令。数据处理指令有两个源操作数,并将结果放在不同的目标操作数中。
  • 有无穷多的寄存器。它对局部变量的最大数量没有最大限制。

target datalayout构造包含有关目标机器(target host)中描述的目标三元组(target tripple)的字节顺序和类型大小等信息。有些优化需要知道目标的特定数据布局才能完成正确的代码转化.

其中关于target datalayout的参数基本含义如下:

  • target datalayout是通过符号“-”进行分隔的规格列表;
  • E/e代表大小端;
  • m:<mangling>:名称粉碎的类型:

e:ELF mangling;

l:GOFF mangling;

m:Mips mangling;

o:Mach-O mangling;

x:Windows x86 COFF mangling;

w:Windows COFF mangling;

a:XCOFF mangling;

  • p[n]:<size>:<abi>[:<pref>][:<idx>]:n为地址空间编号,size为指针大小,abi为ABI中的指针大小,pref为地址空间n的对齐(默认等于abi),idx为地址计算的index的大小(默认等于size)。
  • S<size>:栈自然对齐的的bit数。
  • P<address space>:默认为0,表示冯诺依曼架构,数据和程序放置到同一个地址空间。
  • A<address space>:alloca分配的地址空间,默认为0。
  • G<address space>:全局变量放置的地址空间,默认为0。
  • i<size>:<abi>[:<pref>]:size为整型的对齐,abi为ABI中的对齐,pref默认等于abi。
  • v<size>:<abi>[:<pref>]:size为矢量的对齐,abi为ABI中的对齐,pref默认等于abi。
  • f<size>:<abi>[:<pref>]size为浮点的对齐,abi为ABI中的对齐,pref默认等于abi。
  • a:<abi>[:<pref>]size为聚合类型的对齐,abi为ABI中的对齐,pref默认等于abi。

上述例子中:

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

我们可以得知它具有小端字节序,采用ELF mangling进行名称粉碎,地址空间270/271的指针小为32bit,地址空间272的指针为大小为64bit,整形64bit对齐,浮点80bit对齐,自然整型为8/16/32/64bit,栈自然对齐为128bit。目标机器是装有linux-gnu的x86_64处理器。

       此外,函数声明严格遵循相应的C语法

define i32 @sum(i32 %a, i32 %b)   #0   {

此函数返回i32类型的值,并具有两个i32参数:%a和%b。本地标识符始终需要%前缀,而全局标识符使用@。LLVM支持多种类型,但最重要的类型如下:

  • iN形式的任意大小的整数,常见的例子是i32、i64和i128。
  • 浮点类型,如32位单精度浮点数float和64位双精度浮点数double。
  • 向量类型的格式位<<elements>x<elementtype>>。包含四个i32元素的向量被写为<4 x i32>.

函数声明中的#0记号映射到一组函数属性,这与C/C++函数和方法中使用的属性非常类似。

alloca指令在当前函数的堆栈帧中保留一定的空间。空间的大小由元素类型的大小决定,它遵循特定的对齐方式,例如:

  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  store i32 %0, i32* %3, align 4
  store i32 %1, i32* %4, align 4

该指令分配一个按4字节对齐的4字节堆栈元素。指向该堆栈元素的指针被存储在本地变量% 3中。

alloca指令通常用于表示本地变量。%0和%1参数通过store指令存储在堆栈地址%3和%4中。这些值通过load指令从相同的内存地址加载回来,并在%7 = add nsw i32 %5, %6加法中使用。最后返回加法结果%7。nsw标识指定此加法操作具有 “no signed wrap",这表示已知指令是无溢出的,从而允许进行一些优化。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值