本文基于LLVM 12官方文档的
LLVM Language Reference Manual
。以学习笔记为主。所以本文会摘录一些常见/常用的指令。对于一些更加深层次的指令属性/特性,待我对LLVM有更深的理解再单独写文章记录。
LLVM IR可以理解为LLVM平台的汇编语言,所以官方也是以语言参考手册(Language Reference Manual)的形式给出LLVM IR的文档说明。既然是汇编语言,那么就和传统的CUP类似,有特定的汇编指令集。但是它又与传统的特定平台相关的指令集(x86, ARM, RISC-V等)不一样,它定位为平台无关的汇编语言。也就是说,LLVM IR是一种相对于CUP指令集高级,但是又是一种低级的代码中间表示(比抽象语法树等高级表示更加低级)。
基本特点
为:SSA
。在指令集层面,以SSA的形式表示。指令集中存在Phi指令。类型安全
。它在指令集层面,是有类型的,也就是说指令是需要有特定的类型作为约束的。低级操作
。指令集相对比较底层。灵活
。能够很利索地表示“所有”的高级语言。也叫Universal IR。LLVM编译各个阶段通用的IR
。表示形式
:存在形式
内存
。基于LLVM开发需要用到许多的类。include "llvm/IR/XX"
磁盘(二进制)
。一般以.bc
的形式存在。方便JIT编译器快速加载。磁盘(文本)
。可读的汇编语言表示,一般以.ll
的形式存在,本文研究的就是这种形式的LLVM IR
特点
良好的格式
%x = add i32 1, %x在语法上符合,但是它并不是SSA形式的,所以会验证格式错误
LLVM IR中有两种标识符类型
:
全局标识
。以@开头的
局部标识
。以%开头
LLVM IR中有3种标识符格式
:
有名字的值(Named Values)
无名字的值(Unnamed Values)
常量(后文再讲)
官方文档罗列得非常多,这里暂时只列出几个我觉得比较重要的
。
LLVM程序是由若干个Module组成。而Module中由如下三个部分组成:
函数
全局变量
符号表项
其中函数/全局变量都可以认为是全局值(即全局函数和全局变量);
而全局值能够用一个指向内存位置的指针标识。其中
全局值都有链接类型
。链接,即它们可能会结合LLVM Linker,合并每个Module的函数/全局变量定义。并resolve前向声明,合并符号表项
。
全局值(全局变量/函数)会有一种如下的链接类型(这里只简单了解2中类型):
private
internal
LLVM支持如下几种调用模式。(这里只简单了解,后续深入再添加)
ccc
fastcc
coldcc
cc 10
cc 11
webkit_jscc
anyregcc
preserve_mostcc
preserve_allcc
cxx_fast_tlscc
swiftcc
tailcc
cfguard_checkcc
cc
更多可能会在未来添加
…
类型系统是LLVM IR中最重要的特性之一。它带来好处:
有类型让许多优化能够直接在IR上进行(一般的三地址码不行,它没类型)
强类型使得LLVM IR更加可读
void就是通常编程语言中的void,空。
函数类型,它的表示成:
()
例如:
i32 (i32)
: 它表示的函数为:i32类型作为函数入参,i32类型作为函数返回值float (i16, i32 *) *
: 它表示指向函数的指针,此函数以i16,i32指针类型作为入参类型,float作为返回值类型。i32 (i8*, ...)
: 变长参数的函数,固定长度的入参为i8类型,返回值为i32类型{i32, i32} (i32)
: i32,i32为结构体的前两个字段类型,此结构体类型作为函数的返回值类型,函数入参为i32类型。First Class表示一大类的类型。包括:
Single Value
Label
Token
Metadata
Aggregate
Integer(整数)
Float-Point(浮点)
x86_mmx
Pointer(指针类型)
*
Vector(向量)
Fiexed-length vector(定长向量)
: < <# elements> x >
Scalable vector(变长向量)
: < vscale x <# elements> x >
Label
Token
Metadata
Aggregate(聚合类型)
Array(数组)
:
Structure(结构体)
%T1 = type { \ }
{ i32, i32, i32}
: 4字节三元组{ float, i32 (i32) * }
: 第一个元素float,第二个元素函数指针,函数为4字节入参,返回4字节类型%T2 = type <{ \ }>
<{ i8, i32 }>
: 只知道5字节大小Opaque Structure
指令集分为如下几大类,这里会挑一些指令列举,随着后续对指令更加深入,再对指令的具体内容进行补充。
ret
br
Test:
%cond = icmp eq i32 %a, %b
br i1 %cond, label %IfEqual, label %IfUnequal
IfEqual:
ret i32 1
IfUnequal:
ret i32 0
switch
; Emulate a conditional br instruction
%Val = zext i1 %value to i32
switch i32 %Val, label %truedest [ i32 0, label %falsedest ]
; Emulate an unconditional br instruction
switch i32 0, label %dest [ ]
; Implement a jump table:
switch i32 %val, label %otherwise [ i32 0, label %onzero
i32 1, label %onone
i32 2, label %ontwo ]
indirectbr
invoke
%retval = invoke i32 @Test(i32 15) to label %Continue
unwind label %TestCleanup ; i32:retval set
%retval = invoke coldcc i32 %Testfnptr(i32 15) to label %Continue
unwind label %TestCleanup ; i32:retval set`
callbr
resume
catchswitch
catchret
cleanupret
unreachable
fneg
add
fadd
sub
> >, > ; yields ty:result
= sub nuw > >, > ; yields ty:result
= sub nsw > >, > ; yields ty:result
= sub nuw nsw > >, > ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- nuw
- No Unsigned Wrap
- nsw
- No Signed Wrap
- 如果无符号/有符号溢出,nuw/nsw设置,得到poison value
-
fsub
= fsub [fast-math flags]* , ; yields ty:result
- 约束
- 作用相等类型的浮点/浮点向量上
-
mul
= mul , ; yields ty:result
= mul nuw , ; yields ty:result
= mul nsw , ; yields ty:result
= mul nuw nsw , ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- nuw
- No Unsigned Wrap
- nsw
- No Signed Wrap
- 如果无符号/有符号溢出,nuw/nsw设置,得到poison value
-
fmul
= fmul [fast-math flags]* , ; yields ty:result
- 约束
- 作用相等类型的浮点/浮点向量上
-
udiv
= udiv , ; yields ty:result
= udiv exact , ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- 无符号整数相除
- 除0行为未知
- 使用exact时,如果((%op1 udiv exact %op2) mul %op2) != %op1, 则udiv exact得到的结果为poison value
-
sdiv
= sdiv , ; yields ty:result
= sdiv exact , ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- 有符号整数相除
- 除0行为未知
- 溢出行为未知
- 如果exact使用,结果被舍入,则结果为poison value
-
fdiv
= fdiv [fast-math flags]* , ; yields ty:result
- 约束
- 作用相等类型的浮点/浮点向量上
-
urem
= urem , ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- 无符号整数除法的余数
- op2为0,行为未知
- 例如
= urem i32 4, %var ; yields i32:result = 4 % %var
-
srem
= srem , ; yields ty:result
- 约束
- 作用到整数或者整数向量,两个操作数都必须是相同类型
- 有符号整数除法的余数
- 结果要么为0,要么与op1有相同的符号
- op2为0,行为未知
- 溢出行为未知
-
frem
= frem [fast-math flags]* , ; yields ty:result
- 约束
- 作用相等类型的浮点/浮点向量上
- 与被除数相同符号
5.4 二元位运算
-
shl
= shl , ; yields ty:result
= shl nuw , ; yields ty:result
= shl nsw , ; yields ty:result
= shl nuw nsw , ; yields ty:result
- 说明
- op1左移特定数量的位(op2为无符号)
- 结果为(op1 * 2^(op2) ) % 2^n
-
lshr
= lshr , ; yields ty:result
= lshr exact , ; yields ty:result
- 说明
- 逻辑右移
-
ashr
= ashr , ; yields ty:result
= ashr exact , ; yields ty:result
- 说明
- 算数右移
-
and
= and , ; yields ty:result
-
or
= or , ; yields ty:result
-
xor
= xor , ; yields ty:result
5.5 向量操作
-
extractelement
= extractelement > , ; yields
= extractelement > , ; yields
- 例子
= extractelement <4 x i32> %vec, i32 0 ; yields i32
-
insertelement
= insertelement > , , ; yields >
= insertelement > , , ; yields >
- 例子
= insertelement <4 x i32> %vec, i32 1, i32 0 ; yields <4 x i32>
-
shufflevector
= shufflevector > , > , ; yields >
= shufflevector > , > v2, ; yields >
- 例子
= shufflevector <4 x i32> %v1, <4 x i32> %v2, <4 x i32> ; yields <4 x i32>
= shufflevector <4 x i32> %v1, <4 x i32> undef, <4 x i32> ; yields <4 x i32> - Identity shuffle.
= shufflevector <8 x i32> %v1, <8 x i32> undef, <4 x i32> ; yields <4 x i32>
= shufflevector <4 x i32> %v1, <4 x i32> %v2, <8 x i32> ; yields <8 x i32>
5.6 访问/操作内存
-
alloca
= alloca [inalloca] [, ] [, align ] [, addrspace()] ; yields type addrspace(num)*:result
- 说明
- 在当前正执行函数的栈帧分配内存,当函数返回到调用点时自动释放此内存。对象分配内存空间会根据datalayout的指示来
- 例子
- %ptr = alloca i32 ; yields i32*:ptr
- %ptr = alloca i32, i32 4 ; yields i32*:ptr
- %ptr = alloca i32, i32 4, align 1024 ; yields i32*:ptr
- %ptr = alloca i32, align 1024 ; yields i32*:ptr
-
load
= load [volatile] , * [, align ][, !nontemporal !][, !invariant.load !][, !invariant.group !][, !nonnull !][, !dereferenceable !][, !dereferenceable_or_null !][, !align !][, !noundef !]
= load atomic [volatile] , * [syncscope("")] , align [, !invariant.group !]
! = !{ i32 1 }
! = !{}
! = !{ i64 }
! = !{ i64 }
- 说明
- 指定从哪个内存地址加载;指定的类型必须是已知first class类型(不包含opaque structural type)
- 例子
- %ptr = alloca i32 ; yields i32*:ptr
- store i32 3, i32* %ptr ; yields void
- %val = load i32, i32* %ptr ; yields i32:val = i32 3
-
store
- store [volatile]
, * [, align ][, !nontemporal !][, !invariant.group !] ; yields void
- store atomic [volatile]
, * [syncscope("")] , align [, !invariant.group !] ; yields void
! = !{ i32 1 }
! = !{}
- 说明
- 被用来写内存
- 类型必须是的first-class类型
- 例子
- %ptr = alloca i32 ; yields i32*:ptr
- store i32 3, i32* %ptr ; yields void
- %val = load i32, i32* %ptr ; yields i32:val = i32 3
-
fence
- 用于实现happens-before
-
cmpxchg
- 用于自动修改内存(比较,然后修改)。
-
atomicrmw
- 用于自动修改内存
-
getelementptr
= getelementptr , * {, [inrange] }*
= getelementptr inbounds , * {, [inrange] }*
= getelementptr , , [inrange]
- 说明
- 获取aggregate数据结构的子元素的地址;它只执行地址计算不访问内存
- 例子
struct RT {
char A;
int B[10][20];
char C;
};
struct ST {
int X;
double Y;
struct RT Z;
};
int *foo(struct ST *s) {
return &s[1].Z.B[5][13];
}
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }
define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
entry:
%arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
ret i32* %arrayidx
}
5.6 强转操作
trunc ... to
zext ... to
= zext to ; yields ty2
- 约束
- 两个ty必须是整数,或者相同数量的整数向量
- value的位数要比ty2类型的位数小
- 例子
- %X = zext i32 257 to i64 ; yields i64:257
- %Y = zext i1 true to i32 ; yields i32:1
- %Z = zext <2 x i16>
to <2 x i32> ; yields
sext ... to
fptrunc ... to
fpext ... to
fptoui ... to
fptosi ... to
uitofp ... to
sitofp ... to
ptrtoint ... to
inttoptr ... to
bitcast ... to
addrspacecast ... to
5.7 其它操作
-
icmp
= icmp , ; yields i1 or :result
- eq: equal
- ne: not equal
- ugt: unsigned greater than
- uge: unsigned greater or equal
- ult: unsigned less than
- ule: unsigned less or equal
- sgt: signed greater than
- sge: signed greater or equal
- slt: signed less than
- sle: signed less or equal
- 说明
- 返回布尔值或者布尔值的向量
- op1和op2必须是integer/pointer或者interger vector
- 例子
= icmp eq i32 4, 5 ; yields: result=false
= icmp ne float* %X, %X ; yields: result=false
= icmp ult i16 4, 5 ; yields: result=true
= icmp sgt i16 4, 5 ; yields: result=false
= icmp ule i16 -4, 5 ; yields: result=false
= icmp sge i16 4, 5 ; yields: result=false
-
fcmp
= fcmp [fast-math flags]* , ; yields i1 or :result
- false: no comparison, always returns false
- oeq: ordered and equal
- ogt: ordered and greater than
- oge: ordered and greater than or equal
- olt: ordered and less than
- ole: ordered and less than or equal
- one: ordered and not equal
- ord: ordered (no nans)
- ueq: unordered or equal
- ugt: unordered or greater than
- uge: unordered or greater than or equal
- ult: unordered or less than
- ule: unordered or less than or equal
- une: unordered or not equal
- uno: unordered (either nans)
- true: no comparison, always returns true
- 例子
= fcmp oeq float 4.0, 5.0 ; yields: result=false
= fcmp one float 4.0, 5.0 ; yields: result=true
= fcmp olt float 4.0, 5.0 ; yields: result=true
= fcmp ueq double 1.0, 2.0 ; yields: result=false
-
phi
= phi [fast-math-flags] [ , ], …
- 说明
- 每个val对应一个label;
- 每个label对应一个basic block,此bb到phi节点的incoming value就是val
-
select
-
freeze
-
call
-
va_arg
-
landingpad
-
catchpad
-
cleanuppad
你可能感兴趣的:(Clang/LLVM,编译原理)