LLVM 学习(二) -LLVM IR 语法理论学习

LLVM 学习(二) -LLVM IR 语法理论学习

@create 2022-04-16 12:05:47

@sizaif

img

LLVM IR 语法

https://llvm.org/docs/LangRef.html

LLVM IR中的标识符有两种基本类型:全局标识符(以@开头)和局部标识符(以%开头)

1、命名值以字母序列开头,例如:%foo, @DivisionByZero, %a.really.long.identifier. 正则表达式为: ‘[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*‘.

2、非命名值以数字开头,例如: %12, @2, %44.

全局标识符,包括全局变量、全局常量和函数

局部标识符,即局部变量。LLVM IR中的局部变量有两种分类方案:

按照是否命名分类:

  • 命名局部变量:顾名思义,比如%tmp
  • 未命名局部变量:以带前缀的无符号数字值表示,比如%1%2,按顺序编号,函数参数、未命名基本块都会增加计数

按照分配方式分类:

  • 寄存器分配的局部变量:此类局部变量多采用%1 = some value形式进行分配,一般是接受指令返回结果的局部变量 ,
  • 栈分配的局部变量:使用alloca指令在栈帧上分配的局部变量,比如%2 = alloca i32%2也是个指针,访问或存储时必须使用loadstore指令
define i32 @main() {
    %1 = alloca i32, align 4
    %tmp = alloca i32, align 4
    store i32 1, i32* %1, align 4
    store i64 2, i32* %tmp, align 8
    %2 = add nsw i32 %1, %tmp
    %result = add nsw i32 %1, %2
}

其中%1是栈分配的未命名局部变量,%tmp是栈分配的命名局部变量,%2是寄存器分配的未命名局部变量,%result是寄存器分配的命名局部变量

模块结构

LLVM程序由模块组成,每个模块由函数、全局变量和符号表条目组成。

; Declare the string constant as a global constant.
; 全局变量
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"

; External declaration of the puts function
; 函数的外部声明
declare i32 @puts(i8* nocapture) nounwind

; Definition of main function
; 主函数
define i32 @main() {   ; i32()*
  ; Convert [13 x i8]* to i8*...
  %cast210 = getelementptr [13 x i8], [13 x i8]* @.str, i64 0, i64 0

  ; Call puts function to write out the string to stdout.
  call i32 @puts(i8* %cast210)
  ret i32 0
}

; Named metadata
; 元组数据
!0 = !{i32 42, null, !"string"}
!foo = !{!0}
  • Module:LLVM程序由模块组成,每个输入程序都对应一个模块,模块包括函数、全局变量和符号表。多个模块可以被LLVM 链接器(linker)组合在一起。可以将LLVM中的Module类比为C程序中的源文件。一个C源文件中包含函数和全局变量定义、外部函数和外部函数声明,一个Module中包含的内容也基本上如此,只不过C源文件中是源码来表示,Module中是用IR来表示。
  • Function:被Module所包含,LLVM的Function包含函数名、函数的返回值和参数类型,Function内部则包含BasicBlock。
  • BasicBlock:与编译技术中常见的基本块(basic block)的概念是一致的,BasicBlock必须有一个进入标签和一条结束指令。结束指令一般是br跳转指令或者ret返回执行。
  • Instruction:就是上面提到过的“指令”,LLVM IR的最基本的单位,Instruction被包含在BasicBlock中,一个BasicBlock可以有多条Instruction。

链接类型(Linkage Types)

全局的值(全局变量和函数)都由指向某个特定位置的指针表示,并且有一个链接类型:

  • private

    private类型的全局变量只能被当前模块内的对象直接访问,私有

  • internal

    与private类似,不过它的值会作为局部变量出现在目标文件中。类似C语言中static的概念。

  • available_externally

    该类型的变量不会被输出到对应模块的目标文件中。对于链接者来说,该类型相当于一个外部声明。这种类型只允许在定义时使用,而不能在声明时使用。

  • linkonce

    链接时,这种类型的变量会和其他同名变量merge。该类型的变量如果没被使用则可以被丢弃。这种类型的函数不允许优化器将其内联到调用者的函数体中,这个它有可能被重写。如果想允许内联或者其他优化,请使用linkonce_odr类型

  • weak

    linkonce类似,但即使不被使用,也不能丢弃。类似C语言中的weak类型的变量(多个变量拥有相同的名字不会造成冲突,只要其中一个是weak类型的。链接时,weak类型的定义被忽略而使用正常的定义,如果没有正常的定义,则使用weak的定义)。

  • common

    与weak类似,用于C语言中的临时定义,比如全局作用域中的`int x;”。函数和别名没有common类型

  • appending

    只用于数组类型的指针。两个这种类型的变量链接时,对应的全局数组被连接在一起。

    只能用于LLVM.GLOBAL_CTORS等变量

  • extern_weak

    这种类型在链接之前是weak的,如果不被链接,这个变量是null而不是undefined reference。

  • linkonce_odr, weak_odr。ODR(one definition rule)

    只有等效的变量才能被merge。这个链接类型就表示只能跟等效的变量合并。

  • external

    如果没有指定上面的任意类型,那么就是external的,

函数声明只能使用 externalextern_weak

调用约定(Calling Conventions)

LLVM 支持调用约定如下

  • ccc” - C调用约定

    如果没有指定其他类型,那么就默认是这种类型,

    这个类型支持可变参数的函数调用并且允许声明的原型和函数声明的实现之间有一些不匹配。

  • fastcc” - 快速调用约定

    使生成的目标代码尽可能快,可以使用任意tricks。不支持可变参数,并且原型和实现要严格匹配。

  • coldcc” - 冷调用

    假定这种调用不经常发生。不支持可变参数,并且原型和实现要严格匹配

  • cc 10” - GHC调用

    Glasgow Haskell Compiler (GHC).专用,

  • cc 11” - HiPE 调用约定

  • webkit_jscc” - WebKit 的 JavaScript 调用约定

  • anyregcc” - 代码修补的动态调用约定

  • preserve_mostcc” - PreserveMost 调用约定

  • preserve_allcc” - PreserveAll 调用约定

  • cxx_fast_tlscc” - 访问函数的 CXX_FAST_TLS 调用约定

  • tailcc” - 尾可调用调用约定

  • swiftcc” - 此调用约定用于 Swift 语言。

  • swifttailcc

  • cfguard_checkcc” - Windows Control Flow Guard(检查机制)

  • cc <n>” - 编号约定

可见格式(Visibility Styles)

所有的全局变量和函数都有以下可见性样式之一

  • default” - 默认格式

    对于使用ELF(Executable and Linking Format)格式的目标文件,这种可见性样式意味着声明对其他模块可见;在共享库中,意味着声明的实体可以被重写;在Darwin,声明对其他模块可见。

  • hidden” - Hidden style

    具有这种可见性样式的声明引用相同的对象,如果它们在相同的共享对象中的话。通常,具有这种可见性样式的符号不会出现在动态符号表中,所有其他模块不能直接引用它。

  • protected” - Protected style

    对于ELF,这种样式表明符号会放置在动态符号表中,但是在定义这个符号的模块中的引用会绑定到局部变量,也就是说这个符号不能被其他模块重写。

具有内部(internal)私有(private)链接的符号必须具有默认(default)可见性。

DLL存储类(DLL Storage Classes)

所有全局变量,函数和别名可以有以下DLL存储类之一

  • dllimport

    dllimport”使编译器通过一个全局指针引用函数或变量,该指针是由导出符号的DLL设置的。在Microsoft Windows目标上,指针名称由imp和函数或变量名称组合而成。

  • dllexport

    dllexport使编译器提供一个指向DLL中的指针的全局指针,这样它就可以被dllimport属性引用。 在Microsoft Windows目标上,指针名称由imp和函数或变量名称组合而成。因为这个存储类是用来定义dll接口的,所以编译器、汇编器和链接器知道它是外部引用的,所以必须避免删除该符号。

全局变量(Global Variables)

  • 全局变量定义在编译时间分配的内存区域而非运行时
  • 全局变量定义必须初始化
  • 其他翻译单元中的全局变量也可以声明,在这种情况下它们没有初始化器。
  • 全局变量可以选择指定链接类型
  • 变量可以定义为全局常量,这表明变量的内容永远不会被修改,但需要运行时初始化的变量不能标记为常量,因为变量有存储。

语法

@<GlobalVarName> = [Linkage] [PreemptionSpecifier] [Visibility]
                   [DLLStorageClass] [ThreadLocal]
                   [(unnamed_addr|local_unnamed_addr)] [AddrSpace]
                   [ExternallyInitialized]
                   <global | constant> <Type> [<InitializerConstant>]
                   [, section "name"] [, partition "name"]
                   [, comdat [($name)]] [, align <Alignment>]
                   (, !name !N)*

函数(Functions)

LLVM函数定义由define关键字组成

an optional linkage type, an optional runtime preemption specifier, an optional visibility style, an optional DLL storage class, an optional calling convention, an optional unnamed_addr attribute, a return type, an optional parameter attribute for the return type, a function name, a (possibly empty) argument list (each with optional parameter attributes), optional function attributes, an optional address space, an optional section, an optional alignment, an optional comdat, an optional garbage collector name, an optional prefix, an optional prologue, an optional personality, an optional list of attached metadata, an opening curly brace, a list of basic blocks, and a closing curly brace.

LLVM函数声明由declare关键字组成

an optional linkage type, an optional visibility style, an optional DLL storage class, an optional calling convention, an optional unnamed_addr or local_unnamed_addr attribute, an optional address space, a return type, an optional parameter attribute for the return type, a function name, a possibly empty list of arguments, an optional alignment, an optional garbage collector name, an optional prefix, and an optional prologue.

  • 如果给出了Unnamed_addr属性,则已知地址不是很大,并且可以合并两个相同的功能。
  • 如果给出了local_unnamed_addr属性,则已知地址在模块中不显着。
  • 如果未给出显式地址空间,则将默认为DataLayOut字符串的程序地址空间默认。

语法

define [linkage] [PreemptionSpecifier] [visibility] [DLLStorageClass]
       [cconv] [ret attrs]
       <ResultType> @<FunctionName> ([argument list])
       [(unnamed_addr|local_unnamed_addr)] [AddrSpace] [fn Attrs]
       [section "name"] [partition "name"] [comdat [($name)]] [align N]
       [gc] [prefix Constant] [prologue Constant] [personality Constant]
       (!name !N)* { ... }

参数列表(argument list)是一个逗号分隔的参数序列,其中每个参数的形式如下

<type> [parameter Attrs] [name]

Metadata(Metadata)

LLVM IR允许元数据附加到程序中的指令和全局对象,该程序可以将关于代码的额外信息传达给优化器和代码生成器。

元数据没有类型,也不是值。如果从调用指令中引用,则使用元数据类型。

所有元数据在语法中都由感叹号(!)标识.

Named Metadata(Named Metadata)

命名元数据是元数据的集合,元数据节点(Metadata nodes,但不是元数据字符串)是已命名元数据的唯一有效操作数

命名元数据表示为带有元数据前缀的字符串。元数据名称的规则与标识符相同,但不允许使用带引号的名称。"\xx"类型转义仍然有效,它允许任何字符成为名称的一部分。

语法

; Some unnamed metadata nodes, which are referenced by the named metadata.
!0 = !{!"zero"}
!1 = !{!"one"}
!2 = !{!"two"}
; A named metadata.
!name = !{!0, !1, !2}

控制指令含义(Terminator Instructions)

ret 返回

返回

Syntax:

ret <type> <value>       ; Return a value from a non-void function
ret void                 ; Return from void function

Semantics:

当执行‘ret’ 指令时, 控制流返回到调用函数的上下文。

  • 如果调用者是“call”指令,则在调用后的指令处继续执行。
  • 如果调用者是“invoke”指令,则在“正常”目标块的开头继续执行。
  • 如果指令返回一个值,该值应设置调用或调用指令的返回值。

Example:

ret i32 5                       ; Return an integer value of 5
ret void                        ; Return from a void function
ret { i32, i8 } { i32 4, i8 2 } ; Return a struct of values 4 and 2
br 分支

二元有条件分支和无条件分支。

Syntax:

br i1 <cond>, label <iftrue>, label <iffalse>
br label <dest>          ; Unconditional branch

Semantics:

在执行条件br指令时,会评估i1参数。 如果值为 true,则控制流向 ``iftrue' label 参数。 如果condfalse,则控制流向iffalse,label`参数。

如果condpoisonundef,则该指令具有未定义的行为。

Example:

Test:
  %cond = icmp eq i32 %a, %b
  br i1 %cond, label %IfEqual, label %IfUnequal
IfEqual:
  ret i32 1
IfUnequal:
  ret i32 0
switch 多条件分支

多条件分支

Syntax:

switch <intty> <value>, label <defaultdest> [ <intty> <val>, label <dest> ... ]

Arguments:

switch”指令使用三个参数:一个整数比较值“value”,一个默认的“label”目的地,以及一个由成对的比较值常量和“label”组成的数组。 该表不允许包含重复的常量条目。

Semantics:

switch 指令指定了一个值和目标表。 当执行’switch’指令时,将在该表中搜索给定值。

  • 如果找到该值,则将控制流转移到相应的目的地; 否则,控制流将转移到默认目的地。
  • 如果“value”是“poison”或“undef”,则该指令具有未定义的行为。

Example:

; 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 转移到指定地址

转移到地址参数中指定的块

Syntax:

indirectbr <somety>* <address>, [ label <dest1>, label <dest2>, ... ]

Overview:

indirectbr”指令实现了到当前函数内标签的间接分支,其地址由“address”指定。

地址必须从 blockaddress 常量派生。

Arguments:

地址”参数是要跳转到的标签的地址。 其余参数表示地址可能指向的全部可能目的地。 允许在目标列表中多次出现块,但这并不是特别有用。

此目标列表是必需的,以便数据流分析能够准确了解 CFG。

Semantics:

  • 控制转移到地址参数中指定的块。 所有可能的目标块必须列在标签列表中,否则该指令具有未定义的行为。 这意味着跳转到其他函数中定义的标签也具有未定义的行为
  • 如果addresspoisonundef, 则该指令具有未定义的行为。

Implementation:

这通常通过一个寄存器跳转来实现。

Example:

indirectbr i8* %Addr, [ label %bb1, label %bb2, label %bb3 ]
invoke 普通/带异常调用

普通/带异常调用

Syntax:

<result> = invoke [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
              [operand bundles] to label <normal label> unwind label <exception label>

Overview:

如果被调用函数返回“ret”指令,控制流将返回“normal”标签。 如果被调用者(或任何间接被调用者)通过“resume”指令或其他异常处理机制返回,则控制被中断并继续在 动态最近的“exception”标签。

Arguments:

该指令需要几个参数:

  1. 可选的cconv标记指示调用应该使用哪个调用约定。如果未指定,则调用默认使用 C 调用约定。
  2. 返回值的可选参数属性列表。只有zeroext,signextinreg属性在这里是有效的。
  3. 可选的addrspace属性可以用来表示被调用函数的地址空间。如果未指定,将使用 datalayout string 中的程序地址空间。
  4. ty:调用指令本身的类型,也是返回值的类型。不返回值的函数被标记为“void”。
  5. fnty:应该是被调用函数的签名。参数类型必须与此签名所暗示的类型相匹配。如果函数不是可变参数,则可以省略此类型。
  6. fnptrval:一个 LLVM 值,包含一个指向要调用的函数的指针。在大多数情况下,这是一个直接的函数调用,但间接的invoke也是尽可能的,调用一个指向函数值的任意指针。
  7. function args:类型与函数签名参数类型和参数属性匹配的参数列表。所有参数必须是 first class 类型。如果函数签名表明函数接受可变数量的参数,则可以指定额外的参数。
  8. normal label:被调用函数执行ret指令时到达的标签。
  9. exception label:被调用者通过resume指令或其他异常处理机制返回时到达的标签。
  10. 可选的函数属性列表。
  11. 可选的操作数包列表。

Semantics:

在大多数情况下,该指令旨在作为标准的call指令运行。 主要区别在于它建立了与标签的关联,运行时库使用该标签来展开堆栈。

Example:

%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 调用分支

Syntax:

<result> = callbr [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
              [operand bundles] to label <fallthrough label> [indirect labels]

Overview:

callbr’指令使控制转移到指定的函数. 可以控制流传输到‘fallthrough’ 标签或indirect’标签之一。

该指令仅用于实现 gcc 样式内联汇编的goto特性。 任何其他用法都是 IR 验证程序中的错误。

Arguments:

该指令需要几个参数:

  1. 可选的cconv标记指示调用应该使用哪个调用约定。如果未指定,则调用默认使用 C 调用约定。

  2. 返回值的可选参数属性列表。只有zeroext,signextinreg属性在这里是有效的。

  3. 可选的addrspace属性可以用来表示被调用函数的地址空间。如果未指定,将使用 datalayout string 中的程序地址空间。

  4. ty:调用指令本身的类型,也是返回值的类型。不返回值的函数被标记为“void”。

  5. fnty:应该是被调用函数的签名。参数类型必须与此签名所暗示的类型相匹配。如果函数不是可变参数,则可以省略此类型。

  6. fnptrval:一个 LLVM 值,包含一个指向要调用的函数的指针。在大多数情况下,这是一个直接的函数调用,但间接的invoke也是尽可能的,调用一个指向函数值的任意指针。

  7. function args:类型与函数签名参数类型和参数属性匹配的参数列表。所有参数必须是 first class 类型。如果函数签名表明函数接受可变数量的参数,则可以指定额外的参数。

  8. fallthrough label’: 内联汇编执行退出底部时到达的标签。

  9. indirect labels’: 当被调用者将控制权转移到fallthrough label以外的位置时到达的标签。 这些的块地址常量也应该在function args列表中。

  10. 可选的函数属性列表。

  11. 可选的操作数包列表。

Semantics:

在大多数情况下,该指令旨在作为标准的“调用”指令运行。 主要区别在于它建立了与附加标签的关联,以定义控制流在调用之后的去向。

callbr指令的输出值只对fallthrough’块有效,对任何‘indirect’块无效。

这样做的唯一用途是实现 gcc 内联汇编的“goto”功能,其中可以提供额外的标签作为内联汇编跳转到的位置。

Example:

; "asm goto" without output constraints.
callbr void asm "", "r,X"(i32 %x, i8 *blockaddress(@foo, %indirect))
            to label %fallthrough [label %indirect]

; "asm goto" with output constraints.
<result> = callbr i32 asm "", "=r,r,X"(i32 %x, i8 *blockaddress(@foo, %indirect))
            to label %fallthrough [label %indirect]
resume 抛出异常

抛出异常

Syntax:

resume <type> <value>

Overview:

resume指令是没有后继的终止指令。

Arguments:

resume”指令需要一个参数,该参数必须与同一函数中任何“landingpad”指令的结果类型相同。

Semantics:

resume指令恢复一个现有(进行中)异常的传播,该异常的展开被 landingpad 指令中断。

Example:

resume { i8*, i32 } %exn
catchswitch 异常处理

LLVM异常处理

Syntax:

<resultval> = catchswitch within <parent> [ label <handler1>, label <handler2>, ... ] unwind to caller
<resultval> = catchswitch within <parent> [ label <handler1>, label <handler2>, ... ] unwind label <default>

Overview:

LLVM 的异常处理系统 使用 ‘catchswitch’ 指令来描述可能由 EH 特性执行的捕获处理程序集 例程

Arguments:

parent 参数是包含 catchswitch 指令的 funclet 的标记。 如果 catchswitch 不在 funclet 内,则此操作数可能是标记 none

default 参数是另一个以 cleanuppadcatchswitch 指令开头的基本块的标签。 如 异常处理文档 中所述,此展开目标必须是与“parent”链接相关的合法目标。

handlers 是一个后继块的非空列表,每个后继块都以 catchpad 指令开头。

Semantics:

执行此指令将控制权转移到handlers中的后继者之一, 如果合适,或通过展开标签(如果存在)继续展开。

catchswitch 既是终止符又是 “pad” 指令,这意味着它必须是基本块中的第一条非 phi 指令和最后一条指令。 因此,它必须是块中唯一的非 phi 指令。

Example:

dispatch1:
  %cs1 = catchswitch within none [label %handler0, label %handler1] unwind to caller
dispatch2:
  %cs2 = catchswitch within %parenthandler [label %handler0] unwind label %cleanup
catchret 处理返回

Syntax:

catchret from <token> to label <normal>

Overview:

catchret指令是具有单个后继的终止指令。

Arguments:

The first argument to a ‘catchret’ indicates which catchpad it exits. It must be a catchpad. The second argument to a ‘catchret’ specifies where control will transfer to next.

catchret’ 的第一个参数指示它退出哪个 catchpad。 它必须是 catchpad。 ‘catchret’ 的第二个参数指定控制将转移到下一个位置。

Semantics:

catchret’ 指令结束一个现有的(正在进行的)异常,其展开被 catchpad 指令中断。 personality function 有机会执行任意代码,例如销毁活动异常。 然后控制转移到 normal.

token 参数必须是由 catchpad 指令生成的标记。 如果指定的 catchpad 不是最近输入的尚未退出的 funclet pad(如 EH 文档 中所述) , catchret 的行为是未定义的。

Example:

catchret from %catch to label %continue
cleanupret

Syntax:

cleanupret from <value> unwind label <continue>
cleanupret from <value> unwind to caller

Overview

cleanupret指令是具有可选后继的终止指令。

Arguments:

cleanupret 指令需要一个参数,指示它退出哪个 cleanuppad,并且必须是 cleanuppad。 如果指定的 cleanuppad 不是最近进入的尚未退出的 funclet pad(如 EH 文档 中所述) , cleanupret 的行为是未定义的。

The ‘cleanupret’ instruction also has an optional successor, continue, which must be the label of another basic block beginning with either a cleanuppad or catchswitch instruction. This unwind destination must be a legal target with respect to the parent links, as described in the exception handling documentation.

cleanupret指令还有一个可选的后继指令continue,它必须是另一个以cleanuppadcatchswitch指令开头的基本块的标签。 如 异常处理文档 中所述,此展开目标必须是与parent链接相关的合法目标。

Semantics:

cleanupret指令向 personality function 指示一个 [cleanuppad](https://llvm.org/docs/LangRef.html #i-cleanuppad) 将控制权转移到已结束。 它将控制转移到continue或退出函数。

Example:

cleanupret from %cleanup unwind to caller
cleanupret from %cleanup unwind label %continue
unreachable 不可到达

不可到达

Syntax:

unreachable

Overview:

unreachable指令没有定义的语义。 该指令用于通知优化器代码的特定部分不可访问。 这可以用来表示无法到达无返回函数之后的代码,以及其他事实。

一元操作 (Unary Operations)

fneg 求逆

求逆

Syntax:

<result> = fneg [fast-math flags]* <ty> <op1>   ; yields ty:result

Arguments:

fneg指令的参数必须是 floating-point 或 [vector](https://llvm.org/ docs/LangRef.html#t-vector) 的浮点值。

  • <ty> <op1>是操作数类型,必须是浮点类型或者浮点向量。

Semantics:

产生的值是操作数的副本,其符号位被翻转。 该指令还可以采用任意数量的 fast-math flags,它们是启用其他不安全浮点优化的优化提示:

Example:

<result> = fneg float %val          ; yields float:result = -%var

二进制操作指令(Binary Operations)

运算\类型无符号整数带符号整数浮点数
+addfadd
-subfsub
*mulfmul
/udivsdivfdiv
%uremsremfrem
add 加

nuwnsw 分别代表No Unsigned WrapNo Signed Wrap。 如果存在 nuwnsw关键字,则如果分别发生无符号或有符号溢出,则 add 的结果值是 poison value (错误值)

  • <ty> <op1>, <op2>,两个操作数的类型相同且必须是整型类型或者整型向量。
#Syntax
<result> = add <ty> <op1>, <op2>          ; yields ty:result
<result> = add nuw <ty> <op1>, <op2>      ; yields ty:result
<result> = add nsw <ty> <op1>, <op2>      ; yields ty:result
<result> = add nuw nsw <ty> <op1>, <op2>  ; yields ty:result
#example
<result> = add i32 4, %var          ; yields i32:result = 4 + %var
fadd 浮点加
#Syntax
<result> = fadd [fast-math flags]* <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = fadd float 4.0, %var          ; yields float:result = 4.0 + %var
sub 减
#Syntax
<result> = sub <ty> <op1>, <op2>          ; yields ty:result
<result> = sub nuw <ty> <op1>, <op2>      ; yields ty:result
<result> = sub nsw <ty> <op1>, <op2>      ; yields ty:result
<result> = sub nuw nsw <ty> <op1>, <op2>  ; yields ty:result
#example
<result> = sub i32 4, %var          ; yields i32:result = 4 - %var
<result> = sub i32 0, %val          ; yields i32:result = -%var
fsub 浮点减
#Syntax
<result> = fsub [fast-math flags]* <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = fsub float 4.0, %var           ; yields float:result = 4.0 - %var
<result> = fsub float -0.0, %val          ; yields float:result = -%var
mul 乘
#Syntax
<result> = mul <ty> <op1>, <op2>          ; yields ty:result
<result> = mul nuw <ty> <op1>, <op2>      ; yields ty:result
<result> = mul nsw <ty> <op1>, <op2>      ; yields ty:result
<result> = mul nuw nsw <ty> <op1>, <op2>  ; yields ty:result
#example
<result> = mul i32 4, %var          ; yields i32:result = 4 * %var
fmul 浮点乘
#Syntax
<result> = fmul [fast-math flags]* <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = fmul float 4.0, %var          ; yields float:result = 4.0 * %var
udiv 无符号整数除
#Syntax
<result> = udiv <ty> <op1>, <op2>         ; yields ty:result
<result> = udiv exact <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = udiv i32 4, %var          ; yields i32:result = 4 / %var
sdiv 带符号整数除
#Syntax
<result> = sdiv <ty> <op1>, <op2>         ; yields ty:result
<result> = sdiv exact <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = sdiv i32 4, %var          ; yields i32:result = 4 / %var
fdiv 浮点除
#Syntax
<result> = fdiv [fast-math flags]* <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = fdiv float 4.0, %var          ; yields float:result = 4.0 / %var
urem 无符号整数求余
#Syntax
<result> = urem <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = urem i32 4, %var          ; yields i32:result = 4 % %var
srem 带符号整数求余
#Syntax
<result> = srem <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = srem i32 4, %var          ; yields i32:result = 4 % %var
frem 浮点数求余
#Syntax
<result> = frem [fast-math flags]* <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = frem float 4.0, %var          ; yields float:result = 4.0 % %var

位二进制操作指令 (Bitwise Binary Operations)

shl 逻辑左移

返回向左移位指定的结果

o p 1 ∗ 2 o p 2 m o d ( 2 n ) op1 * 2^{op2} mod (2^n) op12op2mod(2n), n为result的宽度

  • <ty> <op1>, <op2>,两个操作数的类型相同且必须是整型类型或者整型向量,<op2>作为无符号值来处理。如果<op1>是向量,<op2>将会按照<op1>的元素逐个移动
  • 如果存在nuw关键字,则如果移位任何非零位,则移位将产生一个poison value(是错误操作的结果)。
  • 如果nsw关键字存在,则如果移位与生成的符号位不一致的任何位,则移位将产生一个poison value(是错误操作的结果)。
#Syntax
<result> = shl <ty> <op1>, <op2>           ; yields ty:result
<result> = shl nuw <ty> <op1>, <op2>       ; yields ty:result
<result> = shl nsw <ty> <op1>, <op2>       ; yields ty:result
<result> = shl nuw nsw <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = shl i32 4, %var   ; yields i32: 4 << %var
<result> = shl i32 4, 2      ; yields i32: 16
<result> = shl i32 1, 10     ; yields i32: 1024
<result> = shl i32 1, 32     ; undefined
<result> = shl <2 x i32> < i32 1, i32 1>, < i32 1, i32 2>   ; yields: result=<2 x i32> < i32 2, i32 4>
lshr 逻辑右移

返回向右移指定零填充位的结果

lshr指令的两个参数必须是相同的整数或整数类型的向量。op2被视为无符号值。

的最高有效位在移位后将被零位填充。

  • <ty> <op1>, <op2>,两个操作数的类型相同且必须是整型类型或者整型向量,作为无符号值来处理。如果是向量,将会按照的元素逐个移动。
  • 如果exact关键字存在,如果移出的任何位都是非零的,那么LSHR的结果值就返回一个poison value(是错误操作的结果)。
#Syntax
<result> = lshr <ty> <op1>, <op2>         ; yields ty:result
<result> = lshr exact <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = lshr i32 4, 1   ; yields i32:result = 2
<result> = lshr i32 4, 2   ; yields i32:result = 1
<result> = lshr i8  4, 3   ; yields i8:result = 0
<result> = lshr i8 -2, 1   ; yields i8:result = 0x7F
<result> = lshr i32 1, 32  ; undefined
<result> = lshr <2 x i32> < i32 -2, i32 4>, < i32 1, i32 2>   ; yields: result=<2 x i32> < i32 0x7FFFFFFF, i32 1>
ashr 算数右移

ashr指令(算术右移)通过符号扩展返回向右移指定位数。

的最高有效位将被op1的符号位填充。

  • <ty> <op1>, <op2>,两个操作数的类型相同且必须是整型类型或者整型向量,作为无符号值来处理。如果是向量,将会按照的元素逐个移动。
  • 如果exact关键字存在,如果移出的任何位都是非零,ashr的结果值返回一个poison value(是错误操作的结果)。
#Syntax
<result> = ashr <ty> <op1>, <op2>         ; yields ty:result
<result> = ashr exact <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = ashr i32 4, 1   ; yields i32:result = 2
<result> = ashr i32 4, 2   ; yields i32:result = 1
<result> = ashr i8  4, 3   ; yields i8:result = 0
<result> = ashr i8 -2, 1   ; yields i8:result = -1
<result> = ashr i32 1, 32  ; undefined
<result> = ashr <2 x i32> < i32 -2, i32 4>, < i32 1, i32 3>   ; yields: result=<2 x i32> < i32 -1, i32 0>
and 与
In0In1Out
000
010
100
111
#Syntax
<result> = and <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = and i32 4, %var         ; yields i32:result = 4 & %var
<result> = and i32 15, 40          ; yields i32:result = 8
<result> = and i32 4, 8            ; yields i32:result = 0
or 或
In0In1Out
000
011
101
111
  • , ,两个操作数的类型相同且必须是整型类型或者整型向量。
#Syntax
<result> = or <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = or i32 4, %var         ; yields i32:result = 4 | %var
<result> = or i32 15, 40          ; yields i32:result = 47
<result> = or i32 4, 8            ; yields i32:result = 12
xor 异或
In0In1Out
000
011
101
110
  • , ,两个操作数的类型相同且必须是整型类型或者整型向量。
#Syntax
<result> = xor <ty> <op1>, <op2>   ; yields ty:result
#example
<result> = xor i32 4, %var         ; yields i32:result = 4 ^ %var
<result> = xor i32 15, 40          ; yields i32:result = 39
<result> = xor i32 4, 8            ; yields i32:result = 12
<result> = xor i32 %V, -1          ; yields i32:result = ~%V

向量运算(Vector Operations)

extractelement

extractelement指令从指定索引处的向量中提取单个矢量元素。

#Syntax:
<result> = extractelement <n x <ty>> <val>, <ty2> <idx>  ; yields <ty>
<result> = extractelement <vscale x n x <ty>> <val>, <ty2> <idx> ; yields <ty>

Arguments:

提取指令的第一个操作数是 vector 类型的值。第二个操作数是指示从何处提取元素的位置的索引。索引可以是任何整数类型的变量。

  • <ty2> <idx>索引值大于等于零,小于向量的长度。

Semantics:

结果是与 val 的元素类型相同类型的矢量。 它的值是 val idx 位置的值。

  • 如果idx超过了固定长度向量的val的长度,结果将是一个poison value(是错误操作的结果)。
  • 对于可伸缩的向量,如果idx的值超过了向量的运行时长度,则结果是一个poison value(是错误操作的结果)。
# Example:
<result> = extractelement <4 x i32> %vec, i32 0    ; yields i32
insertelement

insertelement指令将矢量元素插入到指定索引处的向量中。

Syntax:
<result> = insertelement <n x <ty>> <val>, <ty> <elt>, <ty2> <idx>    ; yields <n x <ty>>
<result> = insertelement <vscale x n x <ty>> <val>, <ty> <elt>, <ty2> <idx> ; yields <vscale x n x <ty>>

Arguments:

insertelement指令的第一个操作数是 vector 类型的值。 第二个操作数是一个矢量值,其类型必须等于第一个操作数的元素类型。 第三个操作数是指示插入值的位置的索引。 索引可以是任何整数类型的变量。

Semantics:

  • 结果是一个与 val 类型相同的向量。 它的元素值是val的值,除了位置 idx,它在该位置获得值 elt。 如果 idx 超过固定长度向量的 val 长度,则结果是poison value.
  • 对于可伸缩向量,如果idx的值超过了向量的运行时长度,则结果是poison value.
# Example:
<result> = insertelement <4 x i32> %vec, i32 1, i32 0    ; yields <4 x i32>
shufflevector

shufflevector指令从两个输入向量构造元素的排列,返回一个与输入具有相同元素类型且长度与shuffle mask相同的向量。

# Syntax:
<result> = shufflevector <n x <ty>> <v1>, <n x <ty>> <v2>, <m x i32> <mask>    ; yields <m x <ty>>
<result> = shufflevector <vscale x n x <ty>> <v1>, <vscale x n x <ty>> v2, <vscale x m x i32> <mask>  ; yields <vscale x m x <ty>>

Arguments:

指令的结果是一个向量,其长度与shuffle mask相同,其元素类型与前两个操作数的元素类型相同。

  • shufflevector指令的前两个操作数是相同类型的向量。 第三个参数是一个shuffle mask向量常量,其元素类型为i32。 掩码向量元素必须是常量整数或undef值。
  • <n x <ty>> <v1>, <n x <ty>> <v2>两个输入向量,如果输入向量中未定义的元素被选中,则新向量对应的元素也是未定义。
  • <m x i32> <mask>调整掩码(shuffle mask),如果调整掩码是未定义的,则新向量就是未定义的。

Semantics:

两个输入向量的元素在两个向量中从左到右编号。 对于结果向量的每个元素,随机掩码从输入向量之一中选择一个元素以复制到结果中。 掩码中的非负元素表示连接输入向量对的索引。

If the shuffle mask is undefined, the result vector is undefined. If the shuffle mask selects an undefined element from one of the input vectors, the resulting element is undefined. An undefined element in the mask vector specifies that the resulting element is undefined. An undefined element in the mask vector prevents a poisoned vector element from propagating.

对于可伸缩矢量, the only valid mask values at present are zeroinitializer and undef, since we cannot write all indices as literals for a vector with a length unknown at compile time.

Example:

<result> = shufflevector <4 x i32> %v1, <4 x i32> %v2,
                        <4 x i32> <i32 0, i32 4, i32 1, i32 5>  ; yields <4 x i32>
<result> = shufflevector <4 x i32> %v1, <4 x i32> undef,
                        <4 x i32> <i32 0, i32 1, i32 2, i32 3>  ; yields <4 x i32> - Identity shuffle.
<result> = shufflevector <8 x i32> %v1, <8 x i32> undef,
                        <4 x i32> <i32 0, i32 1, i32 2, i32 3>  ; yields <4 x i32>
<result> = shufflevector <4 x i32> %v1, <4 x i32> %v2,
                        <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7 >  ; yields <8 x i32>

聚合操作(Aggregate Operations)

extractvalue

extractvalue指令从 aggregate 值中提取成员字段的值。

#Syntax:
<result> = extractvalue <aggregate type> <val>, <idx>{, <idx>}*

Arguments:

  • <aggregate type> <val>聚合类型,数组或结构体。
  • <idx>{, <idx>}*常量索引值,提取指定索引处的元素。

extractvalue’指令的第一个操作数是 struct 或 [array](https://llvm.org/ docs/LangRef.html#t-array) 类型。 其他操作数是常量索引,用于指定以与getelementptr指令中的索引类似的方式提取哪个值。

getelementptr 索引的主要区别是:

  • 由于被索引的值不是指针,所以第一个索引被省略并假定为零。
  • 必须至少指定一个索引。
  • 不仅结构索引而且数组索引也必须在范围内。

Semantics:

结果是索引操作数指定的聚合中位置处的值。

#Example:
<result> = extractvalue {i32, float} %agg, 0    ; yields i32
insertvalue

insertvalue’指令将一个值插入到 aggregate 值的成员字段中。

#Syntax:
<result> = insertvalue <aggregate type> <val>, <ty> <elt>, <idx>{, <idx>}*    ; yields <aggregate type>

Arguments:

  • <aggregate type> <val>聚合类型,数组或结构体。
  • <ty> <elt>要插入的数据,
  • <idx>{, <idx>}*常量索引值,提取指定索引处的元素。

insertvalue’指令的第一个操作数是 struct 或 [array](https://llvm.org/ docs/LangRef.html#t-array) 类型。 第二个操作数是要插入的一等值。 以下操作数是常量索引,指示插入值的位置,其方式与extractvalue指令中的索引类似。 要插入的值必须与索引标识的值具有相同的类型。

Semantics:

The result is an aggregate of the same type as val. Its value is that of val except that the value at the position specified by the indices is that of elt.

结果是与 val 类型相同的聚合。 它的值是val的值,只是索引指定位置的值是elt的值。

#Example:
%agg1 = insertvalue {i32, float} undef, i32 1, 0              ; yields {i32 1, float undef}
%agg2 = insertvalue {i32, float} %agg1, float %val, 1         ; yields {i32 1, float %val}
%agg3 = insertvalue {i32, {float}} undef, float %val, 1, 0    ; yields {i32 undef, {float %val}}

内存访问和寻址操作(Memory Access and Addressing Operations)

alloca

alloca指令在当前执行函数的栈帧上分配内存

  • <type>指令返回的指针类型,可以是任何类型。
  • <ty> <NumElements>要分配的内存数量,分配的内存大小就是该数量*单个类型空间,如果没有指定,默认情况下该值为1。
  • align <alignment>内存对齐值。如果指定了对齐值,对齐值不能大于1 << 29,且保证分配的值结果至少与该边界对齐;如果没有指定或者对齐值为零,目标可以选择在任何与类型兼容的范围内对齐分配。
#Syntax
<result> = alloca [inalloca] <type> [, <ty> <NumElements>] [, align <alignment>] [, addrspace(<num>)]     ; yields type addrspace(num)*:result
#Example
%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指令用于从内存中读取。

  • <result>是一个变量,变量的值是从内存中加载出来的值。
  • load <ty>加载类型,也就是前面变量的类型。
  • <ty>* <pointer>是一个指针,指向要加载的内存,指针的类型必须是不包含不透明结构(opaque structural type)的first class type类型。
#Syntax:
<result> = load [volatile] <ty>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<nontemp_node>][, !invariant.load !<empty_node>][, !invariant.group !<empty_node>][, !nonnull !<empty_node>][, !dereferenceable !<deref_bytes_node>][, !dereferenceable_or_null !<deref_bytes_node>][, !align !<align_node>][, !noundef !<empty_node>]
<result> = load atomic [volatile] <ty>, <ty>* <pointer> [syncscope("<target-scope>")] <ordering>, align <alignment> [, !invariant.group !<empty_node>]
!<nontemp_node> = !{ i32 1 }
!<empty_node> = !{}
!<deref_bytes_node> = !{ i64 <dereferenceable_bytes> }
!<align_node> = !{ i64 <value_alignment> }

#Example
%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 指令用于写入内存。

存储指令有两个参数:要存储的值和存储它的地址。

  • <ty> <value>要存入的值,值的类型必须是已知长度的first class type。
  • <ty>* <pointer>是一个指针,指向一块存值的地址。
#Syntax:
store [volatile] <ty> <value>, <ty>* <pointer>[, align <alignment>][, !nontemporal !<nontemp_node>][, !invariant.group !<empty_node>]        ; yields void
store atomic [volatile] <ty> <value>, <ty>* <pointer> [syncscope("<target-scope>")] <ordering>, align <alignment> [, !invariant.group !<empty_node>] ; yields void
!<nontemp_node> = !{ i32 1 }
!<empty_node> = !{}

# Example
%ptr = alloca i32                               ; yields i32*:ptr
store i32 3, i32* %ptr                          ; yields void
%val = load i32, i32* %ptr                      ; yields i32:val = i32 3
fence

fence指令带有一个排序参数,它定义了与它们添加的边同步的内容。 只能给它们acquire, release, acq_rel, seq_cst排序。

#Syntax
fence [syncscope("<target-scope>")] <ordering>  ; yields void
#Exmaple
fence acquire                                        ; yields void
fence syncscope("singlethread") seq_cst              ; yields void
fence syncscope("agent") seq_cst                     ; yields void
cmpxchg

cmpxchg指令用于原子地修改内存。 它在内存中加载一个值并将其与给定值进行比较。 如果它们相等,它会尝试将新值存储到内存中。

#Syntax:
cmpxchg [weak] [volatile] <ty>* <pointer>, <ty> <cmp>, <ty> <new> [syncscope("<target-scope>")] <success ordering> <failure ordering>[, align <alignment>] ; yields  { ty, i1 }

#Example
entry:
  %orig = load atomic i32, i32* %ptr unordered, align 4                      ; yields i32
  br label %loop

loop:
  %cmp = phi i32 [ %orig, %entry ], [%value_loaded, %loop]
  %squared = mul i32 %cmp, %cmp
  %val_success = cmpxchg i32* %ptr, i32 %cmp, i32 %squared acq_rel monotonic ; yields  { i32, i1 }
  %value_loaded = extractvalue { i32, i1 } %val_success, 0
  %success = extractvalue { i32, i1 } %val_success, 1
  br i1 %success, label %done, label %loop

done:
  ...
atomicrmw

atomicrmw指令用于原子地修改内存。

#Syntax:
atomicrmw [volatile] <operation> <ty>* <pointer>, <ty> <value> [syncscope("<target-scope>")] <ordering>[, align <alignment>]  ; yields ty

atomicrmw指令有三个参数:要应用的操作、要修改其值的地址、操作的参数。

  • xchg: *ptr = val
  • add: *ptr = *ptr + val
  • sub: *ptr = *ptr - val
  • and: *ptr = *ptr & val
  • nand: *ptr = ~(*ptr & val)
  • or: *ptr = *ptr | val
  • xor: *ptr = *ptr ^ val
  • max: *ptr = *ptr > val ? *ptr : val (using a signed comparison)
  • min: *ptr = *ptr < val ? *ptr : val (using a signed comparison)
  • umax: *ptr = *ptr > val ? *ptr : val (using an unsigned comparison)
  • umin: *ptr = *ptr < val ? *ptr : val (using an unsigned comparison)
  • fadd: *ptr = *ptr + val (using floating point arithmetic)
  • fsub: *ptr = *ptr - val (using floating point arithmetic)
#Example:
%old = atomicrmw add i32* %ptr, i32 1 acquire                        ; yields i32
getelementptr

getelementptr指令用于获取 aggregate 数据结构的子元素的地址。 它只执行地址计算,不访问内存。 该指令还可用于计算此类地址的向量。


<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
<result> = getelementptr <ty>, <ptr vector> <ptrval>, [inrange] <vector index type> <idx>
  • 第一个<ty>是第一个索引<idx>使用的基本类型
  • 第二个<ty>*表示其后的基地址ptrval的类型。
  • <ty> <idx>是第一组索引的类型和值, 可以出现多次,其后出现就是第二组、第三组等等索引的类型和值。
#Example:
; yields [12 x i8]*:aptr
%aptr = getelementptr {i32, [12 x i8]}, {i32, [12 x i8]}* %saptr, i64 0, i32 1
; yields i8*:vptr
%vptr = getelementptr {i32, <2 x i8>}, {i32, <2 x i8>}* %svptr, i64 0, i32 1, i32 1
; yields i8*:eptr
%eptr = getelementptr [12 x i8], [12 x i8]* %aptr, i64 0, i32 1
; yields i32*:iptr
%iptr = getelementptr [10 x i32], [10 x i32]* @arr, i16 0, i16 0

其他操作(Other Operations)

icmp 整数比较

icmp 指令根据其两个整数、整数向量、指针或指针向量操作数的比较返回一个布尔值或一个布尔值向量。

#Syntax:
<result> = icmp <cond> <ty> <op1>, <op2>   ; yields i1 or <N x i1>:result

Arguments:

“icmp”指令需要三个操作数。 第一个操作数是指示要执行的比较类型的关键字:

  1. eq: equal
  2. ne: not equal
  3. ugt: unsigned greater than
  4. uge: unsigned greater or equal
  5. ult: unsigned less than
  6. ule: unsigned less or equal
  7. sgt: signed greater than
  8. sge: signed greater or equal
  9. slt: signed less than
  10. sle: signed less or equal

The remaining two arguments must be integer or pointer or integer vector typed. They must also be identical types.

  1. eq: yields true if the operands are equal, false otherwise. No sign interpretation is necessary or performed.
  2. ne: yields true if the operands are unequal, false otherwise. No sign interpretation is necessary or performed.
  3. ugt: interprets the operands as unsigned values and yields true if op1 is greater than op2.
  4. uge: interprets the operands as unsigned values and yields true if op1 is greater than or equal to op2.
  5. ult: interprets the operands as unsigned values and yields true if op1 is less than op2.
  6. ule: interprets the operands as unsigned values and yields true if op1 is less than or equal to op2.
  7. sgt: interprets the operands as signed values and yields true if op1 is greater than op2.
  8. sge: interprets the operands as signed values and yields true if op1 is greater than or equal to op2.
  9. slt: interprets the operands as signed values and yields true if op1 is less than op2.
  10. sle: interprets the operands as signed values and yields true if op1 is less than or equal to op2.
#Example
<result> = icmp eq i32 4, 5          ; yields: result=false
<result> = icmp ne float* %X, %X     ; yields: result=false
<result> = icmp ult i16  4, 5        ; yields: result=true
<result> = icmp sgt i16  4, 5        ; yields: result=false
<result> = icmp ule i16 -4, 5        ; yields: result=false
<result> = icmp sge i16  4, 5        ; yields: result=false
fcmp 浮点数比较
  • fcmp指令根据其操作数的比较返回一个布尔值或布尔值向量。

  • 如果操作数是浮点标量,则结果类型为布尔值 (i1)。

  • 如果操作数是浮点向量,则结果类型是布尔向量,其元素数量与被比较的操作数相同。

#Syntax:
<result> = fcmp [fast-math flags]* <cond> <ty> <op1>, <op2>     ; yields i1 or <N x i1>:result

Arguments:

fcmp’指令需要三个操作数。 第一个操作数是指示要执行的比较类型的关键字:

  1. false:不比较,总是返回false

  2. oeq:有序且相等

  3. ogt:有序且大于

  4. oge:有序且大于等于

  5. olt:有序且小于

  6. ole:有序且小于或等于

  7. one:有序且不相等

  8. ord:有序(非NASN) yields true if both operands are not a QNAN.

  9. ueq:无序或相等

  10. ugt:无序或大于

  11. uge:无序或大于等于

  12. ult:无序或小于

  13. ule:无序或小于等于

  14. une:无序或不相等

  15. uno:无序(都是nans) yields true if either operand is a QNAN.

  16. true:不比较,总是返回true

Ordered 表示两个操作数都不是 QNAN,而 unordered 表示任何一个操作数都可能是 QNAN。

每个 val1val2 参数必须是 floating-point 类型或 [vector](https://llvm .org/docs/LangRef.html#t-vector) 的浮点类型。 它们必须具有相同的类型。

Semantics:

The ‘fcmp’ instruction compares op1 and op2 according to the condition code given as cond. If the operands are vectors, then the vectors are compared element by element. Each comparison performed always yields an i1 result, as follows:

  1. false: always yields false, regardless of operands.
  2. oeq: yields true if both operands are not a QNAN and op1 is equal to op2.
  3. ogt: yields true if both operands are not a QNAN and op1 is greater than op2.
  4. oge: yields true if both operands are not a QNAN and op1 is greater than or equal to op2.
  5. olt: yields true if both operands are not a QNAN and op1 is less than op2.
  6. ole: yields true if both operands are not a QNAN and op1 is less than or equal to op2.
  7. one: yields true if both operands are not a QNAN and op1 is not equal to op2.
  8. ord: yields true if both operands are not a QNAN.
  9. ueq: yields true if either operand is a QNAN or op1 is equal to op2.
  10. ugt: yields true if either operand is a QNAN or op1 is greater than op2.
  11. uge: yields true if either operand is a QNAN or op1 is greater than or equal to op2.
  12. ult: yields true if either operand is a QNAN or op1 is less than op2.
  13. ule: yields true if either operand is a QNAN or op1 is less than or equal to op2.
  14. une: yields true if either operand is a QNAN or op1 is not equal to op2.
  15. uno: yields true if either operand is a QNAN.
  16. true: always yields true, regardless of operands.

fcmp 指令还可以选择采用任意数量的 fast-math 标志,它们是启用其他不安全浮点优化的优化提示。

任何一组快速数学标志在“fcmp”指令上都是合法的,但唯一对其语义有任何影响的标志是那些允许对输入参数的值进行假设的标志; 即nnanninfreassoc。 有关详细信息,请参阅 Fast-Math 标志

# Example:
<result> = fcmp oeq float 4.0, 5.0    ; yields: result=false
<result> = fcmp one float 4.0, 5.0    ; yields: result=true
<result> = fcmp olt float 4.0, 5.0    ; yields: result=true
<result> = fcmp ueq double 1.0, 2.0   ; yields: result=false
phi

phi 指令用于实现 SSA 图中表示函数的 φ 节点。

在运行时,phi指令在逻辑上采用与在当前块之前执行的前驱基本块相对应的对指定的值。

# Syntax:
<result> = phi [fast-math-flags] <ty> [ <val0>, <label0>], ...

Arguments:

传入值的类型由第一个类型字段指定。 在此之后,phi指令将一对列表作为参数,其中一对用于当前块的每个前驱基本块。 只有 first class 类型的值可以用作 PHI 节点的值参数。 只有标签可以用作标签参数。

在基本块的开始和 PHI 指令之间不能有非 phi 指令:即 PHI 指令必须在基本块中的第一个。

就 SSA 形式而言,每个传入值的使用都被认为发生在从相应的前驱块到当前块的边上(但在同一边上定义了“invoke”指令的返回值之后。

# Example:
Loop:       ; Infinite loop that counts from 0 on up...
  %indvar = phi i32 [ 0, %LoopHeader ], [ %nextindvar, %Loop ]
  %nextindvar = add i32 %indvar, 1
  br label %Loop
select 条件值选择

select指令用于根据条件选择一个值,无需 IR 级分支。

  • 如果条件是 i1 并且计算结果为 1,则指令返回第一个值参数; 否则,它返回第二个值参数。

  • 如果条件是 i1 的向量,则值参数必须是相同大小的向量,并且选择是逐个元素完成的。

  • 如果条件是 i1 并且值参数是相同大小的向量,则选择整个向量。

#Syntax
<result> = select [fast-math flags] selty <cond>, <ty> <val1>, <ty> <val2>             ; yields ty selty is either i1 or {<N x i1>}

#Example:
%X = select i1 true, i8 17, i8 42          ; yields i8:17
freeze

freeze 指令用于停止 undef 和 [poison](https://llvm.org/docs/LangRef. html#poisonvalues) 值。

#Syntax:
<result> = freeze ty <val>    ; yields ty:result

Semantics:

如果参数是 undefpoison,则 freeze’返回一个任意但固定的类型为 ``ty' 的值。 否则,此指令为空操作并返回输入参数。 同一条freeze指令返回的值的所有使用都保证始终遵守相同的值,而不同的freeze`指令可能会产生不同的值。

虽然 undefpoison 指针可以被冻结,但结果是一个不可解引用的指针。 有关详细信息,请参阅 指针别名规则 部分。 如果聚合值或向量被冻结,则操作数按元素冻结。 不考虑聚合的填充,因为如果不将其存储到内存中并以不同的类型加载它,它是不可见的。

# Example:
%w = i32 undef
%x = freeze i32 %w
%y = add i32 %w, %w         ; undef
%z = add i32 %x, %x         ; even number because all uses of %x observe
                            ; the same value
%x2 = freeze i32 %w
%cmp = icmp eq i32 %x, %x2  ; can be true or false

; example with vectors
%v = <2 x i32> <i32 undef, i32 poison>
%a = extractelement <2 x i32> %v, i32 0    ; undef
%b = extractelement <2 x i32> %v, i32 1    ; poison
%add = add i32 %a, %a                      ; undef

%v.fr = freeze <2 x i32> %v                ; element-wise freeze
%d = extractelement <2 x i32> %v.fr, i32 0 ; not undef
%add.f = add i32 %d, %d                    ; even number

; branching on frozen value
%poison = add nsw i1 %k, undef   ; poison
%c = freeze i1 %poison
br i1 %c, label %foo, label %bar ; non-deterministic branch to %foo or %bar
call 简单函数调用

call指令代表一个简单的函数调用。

# Syntax:
<result> = [tail | musttail | notail ] call [fast-math flags] [cconv] [ret attrs] [addrspace(<num>)]
           <ty>|<fnty> <fnptrval>(<function args>) [fn attrs] [ operand bundles ]

Arguments:

https://llvm.org/docs/LangRef.html#id2116

Semantics:

call指令用于使控制流转移到指定函数,其传入参数绑定到指定值。 在被调用函数中执行ret指令时,控制流继续执行函数调用之后的指令,并且函数的返回值绑定到结果参数。

# Example:
%retval = call i32 @test(i32 %argc)
call i32 (i8*, ...)* @printf(i8* %msg, i32 12, i8 42)        ; yields i32
%X = tail call i32 @foo()                                    ; yields i32
%Y = tail call fastcc i32 @foo()  ; yields i32
call void %foo(i8 signext 97)

%struct.A = type { i32, i8 }
%r = call %struct.A @foo()                        ; yields { i32, i8 }
%gr = extractvalue %struct.A %r, 0                ; yields i32
%gr1 = extractvalue %struct.A %r, 1               ; yields i8
%Z = call void @foo() noreturn                    ; indicates that %foo never returns normally
%ZZ = call zeroext i32 @bar()                     ; Return value is %zero extended
va_arg 可变参数

va_arg指令用于访问通过函数调用的variable argumen区域传递的参数。 它用于在 C 中实现 va_arg 宏。

# Syntax:
<resultval> = va_arg <va_list*> <arglist>, <argty>

Example:

See the variable argument processing section.

landingpad

LLVM 的异常处理系统 使用landingpad指令来指定一个基本块是一个着陆场——一个异常着陆的地方, 并且对应于在 try/catch 序列的 catch 部分中找到的代码。 它定义了 personality 函数 在重新进入该函数时提供的值。 resultval 的类型为 resultty

# Syntax:
<resultval> = landingpad <resultty> <clause>+
<resultval> = landingpad <resultty> cleanup <clause>*

<clause> := catch <type> <value>
<clause> := filter <array constant type> <array constant>
# Example:
;; A landing pad which can catch an integer.
%res = landingpad { i8*, i32 }
         catch i8** @_ZTIi
;; A landing pad that is a cleanup.
%res = landingpad { i8*, i32 }
         cleanup
;; A landing pad which can catch an integer and can only throw a double.
%res = landingpad { i8*, i32 }
         catch i8** @_ZTIi
         filter [1 x i8**] [i8** @_ZTId]
catchpad

LLVM 的异常处理系统 使用catchpad指令来指定一个基本块开始一个 catch 处理程序- 个性例程试图转移控制以捕获异常的一种。

#Syntax:
<resultval> = catchpad within <catchswitch> [<args>*]
#Example:
dispatch:
  %cs = catchswitch within none [label %handler0] unwind to caller
  ;; A catch block which can catch an integer.
handler0:
  %tok = catchpad within %cs [i8** @_ZTIi]
cleanuppad

LLVM 的异常处理系统使用cleanuppad指令来指定基本块是清理块- 个性例程试图转移控制以运行清理操作的一种。

# Syntax:
<resultval> = cleanuppad within <parent> [<args>*]
# Example:
%tok = cleanuppad within %cs []

Poison Values

A poison value 错误操作的结果。.

为了促进推测执行,许多指令在提供非法操作数时不会立即调用未定义行为,而是返回poison value

字符串 poison 可以在任何需要常量的地方使用,并且带有 nsw 标志的 add 等操作可以产生poison value。

Most instructions return ‘poison’ when one of their arguments is ‘poison’. A notable exception is the select instruction. Propagation of poison can be stopped with the freeze instruction.

It is correct to replace a poison value with an undef value or any value of the type.

This means that immediate undefined behavior occurs if a poison value is used as an instruction operand that has any values that trigger undefined behavior. Notably this includes (but is not limited to):

  • The pointer operand of a load, store or any other pointer dereferencing instruction (independent of address space).
  • The divisor operand of a udiv, sdiv, urem or srem instruction.
  • The condition operand of a br instruction.
  • The callee operand of a call or invoke instruction.
  • The parameter operand of a call or invoke instruction, when the function or invoking call site has a noundef attribute in the corresponding position.
  • The operand of a ret instruction if the function or invoking call site has a noundef attribute in the return value position.

Here are some examples:

entry:
  %poison = sub nuw i32 0, 1           ; Results in a poison value.
  %poison2 = sub i32 poison, 1         ; Also results in a poison value.
  %still_poison = and i32 %poison, 0   ; 0, but also poison.
  %poison_yet_again = getelementptr i32, i32* @h, i32 %still_poison
  store i32 0, i32* %poison_yet_again  ; Undefined behavior due to
                                       ; store to poison.

  store i32 %poison, i32* @g           ; Poison value stored to memory.
  %poison3 = load i32, i32* @g         ; Poison value loaded back from memory.

  %narrowaddr = bitcast i32* @g to i16*
  %wideaddr = bitcast i32* @g to i64*
  %poison4 = load i16, i16* %narrowaddr ; Returns a poison value.
  %poison5 = load i64, i64* %wideaddr   ; Returns a poison value.

  %cmp = icmp slt i32 %poison, 0       ; Returns a poison value.
  br i1 %cmp, label %end, label %end   ; undefined behavior

end:
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值