通用操作码

注意

此文档尚未完全涵盖向量。许多标量/整数/浮点运算也可以处理向量。

常量

G_IMPLICIT_DEF

未定义的值。

%0:_(s32) = G_IMPLICIT_DEF

G_CONSTANT

整数常量。

%0:_(s32) = G_CONSTANT i32 1

G_FCONSTANT

浮点常量。

%0:_(s32) = G_FCONSTANT float 1.0

G_FRAME_INDEX

栈帧中对象的地址。

%1:_(p0) = G_FRAME_INDEX %stack.0.ptr0

G_GLOBAL_VALUE

全局值的地址。

%0(p0) = G_GLOBAL_VALUE @var_local

G_PTRAUTH_GLOBAL_VALUE

全局值的带符号地址。操作数:要签名的地址(指针),密钥(32 位立即数),用于地址区分的地址(如果不需要则为零)和额外的区分符(64 位立即数)。

%0:_(p0) = G_PTRAUTH_GLOBAL_VALUE %1:_(p0), s32, %2:_(p0), s64

G_BLOCK_ADDR

基本块的地址。

%0:_(p0) = G_BLOCK_ADDR blockaddress(@test_blockaddress, %ir-block.block)

G_CONSTANT_POOL

常量池中对象的地址。

%0:_(p0) = G_CONSTANT_POOL %const.0

整数扩展和截断

G_ANYEXT

扩展操作的底层标量类型,高位未指定。

%1:_(s32) = G_ANYEXT %0:_(s16)

G_SEXT

符号扩展操作的底层标量类型,将符号位复制到新创建的空间中。

%1:_(s32) = G_SEXT %0:_(s16)

G_SEXT_INREG

从任意位位置符号扩展值,将符号位复制到其上方的所有位。这等效于带有适当移位量的 shl + ashr 对。$sz 是一个立即数(MachineOperand::isImm() 返回 true),允许目标具有某些合法的位宽,而其他位宽则被降低。如果目标具有比组成移位更便宜的符号扩展指令,则此操作码特别有用,因为优化器能够做出有关是否更好地保留 G_SEXT_INREG 或将其降低并优化各个移位的决策。

%1:_(s32) = G_SEXT_INREG %0:_(s32), 16

G_ZEXT

零扩展操作的底层标量类型,在新创建的空间中放置零位。

%1:_(s32) = G_ZEXT %0:_(s16)

G_TRUNC

截断操作的底层标量类型。这对于标量类型等效于 G_EXTRACT,但对向量按元素起作用。

%1:_(s16) = G_TRUNC %0:_(s32)

类型转换

G_INTTOPTR

将整数转换为指针。

%1:_(p0) = G_INTTOPTR %0:_(s32)

G_PTRTOINT

将指针转换为整数。

%1:_(s32) = G_PTRTOINT %0:_(p0)

G_BITCAST

将值重新解释为新的类型。这通常在不更改任何位的情况下完成,但由于LLVM-IR Bitcast 指令定义中的细微差别,情况并非总是如此。允许在大小相同但地址空间不同的指针之间进行位转换。

%1:_(s64) = G_BITCAST %0:_(<2 x s32>)

G_ADDRSPACE_CAST

将指向某个地址空间的指针转换为指向另一个地址空间的指针。

%1:_(p1) = G_ADDRSPACE_CAST %0:_(p0)

注意

‘addrspacecast .. to’ 指令 没有提及如果转换根本无效(即如果地址空间是不相交的)会发生什么。

标量运算

G_EXTRACT

提取指定大小的寄存器,从索引给定的块开始。在选择寄存器组后,这几乎肯定会被映射到子寄存器 COPY。

%3:_(s32) = G_EXTRACT %2:_(s64), 32

G_INSERT

在指定的位索引处将较小的寄存器插入较大的寄存器中。

%2:_(s64) = G_INSERT %0:(_s64), %1:_(s32), 0

G_MERGE_VALUES

将多个相同大小的寄存器连接成一个更宽的寄存器。输入操作数始终从最低位到最高位排序

%0:(s32) = G_MERGE_VALUES %bits_0_7:(s8), %bits_8_15:(s8),
                          %bits_16_23:(s8), %bits_24_31:(s8)

G_UNMERGE_VALUES

提取指定大小的多个寄存器,从索引给定的块开始。在选择寄存器组后,这几乎肯定会被映射到子寄存器 COPY。输出操作数始终从最低位到最高位排序

%bits_0_7:(s8), %bits_8_15:(s8),
    %bits_16_23:(s8), %bits_24_31:(s8) = G_UNMERGE_VALUES %0:(s32)

G_BSWAP

反转标量中字节的顺序。

%1:_(s32) = G_BSWAP %0:_(s32)

G_BITREVERSE

反转标量中位的顺序。

%1:_(s32) = G_BITREVERSE %0:_(s32)

G_SBFX, G_UBFX

从寄存器中提取一系列位。

源操作数是如下所示的寄存器

  • 提取的最低有效位

  • 提取的宽度

最低有效位 (lsb) 和宽度操作数的范围为

0 <= lsb < lsb + width <= source bitwidth, where all values are unsigned

G_SBFX 对结果进行符号扩展,而 G_UBFX 对结果进行零扩展。

; Extract 5 bits starting at bit 1 from %x and store them in %a.
; Sign-extend the result.
;
; Example:
; %x = 0...0000[10110]1 ---> %a = 1...111111[10110]
%lsb_one = G_CONSTANT i32 1
%width_five = G_CONSTANT i32 5
%a:_(s32) = G_SBFX %x, %lsb_one, %width_five

; Extract 3 bits starting at bit 2 from %x and store them in %b. Zero-extend
; the result.
;
; Example:
; %x = 1...11111[100]11 ---> %b = 0...00000[100]
%lsb_two = G_CONSTANT i32 2
%width_three = G_CONSTANT i32 3
%b:_(s32) = G_UBFX %x, %lsb_two, %width_three

整数运算

G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR, G_SDIV, G_UDIV, G_SREM, G_UREM

这些都对标量执行各自的整数算术运算。

%dst:_(s32) = G_ADD %src0:_(s32), %src1:_(s32)

以上示例将 %src1 加到 %src0 并将结果存储在 %dst 中。

G_SDIVREM, G_UDIVREM

执行整数除法和余数运算,从而产生两个结果。

%div:_(s32), %rem:_(s32) = G_SDIVREM %0:_(s32), %1:_(s32)

G_SADDSAT, G_UADDSAT, G_SSUBSAT, G_USUBSAT, G_SSHLSAT, G_USHLSAT

带饱和的带符号和无符号加法、减法和左移。

%2:_(s32) = G_SADDSAT %0:_(s32), %1:_(s32)

G_SHL, G_LSHR, G_ASHR

将标量的位左移或右移,插入零(对于 G_ASHR 则为符号位)。

G_ROTR, G_ROTL

向右 (G_ROTR) 或向左 (G_ROTL) 旋转位。

G_ICMP

执行整数比较,产生非零(真)或零(假)。真值是 1、~0U 还是其他一些非零值取决于目标。

G_SCMP

执行带符号的三路整数比较,生成 -1(小于)、0(等于)或 1(大于)。

%5:_(s32) = G_SCMP %6, %2

G_UCMP

执行无符号的三路整数比较,生成 -1(小于)、0(等于)或 1(大于)。

%7:_(s32) = G_UCMP %2, %6

G_SELECT

根据零/非零值在两个值之间进行选择。

%5:_(s32) = G_SELECT %4(s1), %6, %2

G_PTR_ADD

将标量偏移量(以可寻址单元为单位)添加到指针。可寻址单元通常为字节,但不同目标之间可能有所不同。

%1:_(p0) = G_PTR_ADD %0:_(p0), %1:_(s32)

注意

目前没有树内目标使用此功能,其可寻址单元不等于 8 位。

G_PTRMASK

将指针的任意位掩码清零。掩码类型必须是整数,并且所有操作数的向量元素数量必须匹配。这对应于 i_intr_llvm_ptrmask

%2:_(p0) = G_PTRMASK %0, %1

G_SMIN, G_SMAX, G_UMIN, G_UMAX

获取两个值的最小值/最大值。

%5:_(s32) = G_SMIN %6, %2

G_ABS

获取带符号整数的绝对值。最小负值的绝对值(例如 8 位值 0x80)定义为其自身。

%1:_(s32) = G_ABS %0

G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_SMULO, G_UMULO

执行请求的算术运算,除了正常的计算结果外,还会生成进位输出。

%3:_(s32), %4:_(s1) = G_UADDO %0, %1

G_UADDE, G_SADDE, G_USUBE, G_SSUBE

执行请求的算术运算,除了正常的输入外,还会使用进位输入。除了正常的计算结果外,还会生成进位输出。

%4:_(s32), %5:_(s1) = G_UADDE %0, %1, %3:_(s1)

G_UMULH, G_SMULH

将两个数字乘以输入位宽的两倍(无符号或有符号),并返回结果的高半部分。

%3:_(s32) = G_UMULH %0, %1

G_CTLZ, G_CTTZ, G_CTPOP

计算前导零、尾随零或设置位的数量。

%2:_(s33) = G_CTLZ_ZERO_UNDEF %1
%2:_(s33) = G_CTTZ_ZERO_UNDEF %1
%2:_(s33) = G_CTPOP %1

G_CTLZ_ZERO_UNDEF, G_CTTZ_ZERO_UNDEF

计算前导零或尾随零。如果值为零,则结果未定义。

%2:_(s33) = G_CTLZ_ZERO_UNDEF %1
%2:_(s33) = G_CTTZ_ZERO_UNDEF %1

浮点运算

G_FCMP

执行浮点比较,生成非零(真)或零(假)。真值是 1、~0U 还是其他非零值,取决于目标。

G_FNEG

浮点取反。

G_FPEXT

将浮点值转换为更大的类型。

G_FPTRUNC

将浮点值转换为更窄的类型。

G_FPTOSI, G_FPTOUI, G_SITOFP, G_UITOFP

在整数和浮点之间转换。

G_FABS

获取浮点值的绝对值。

G_FCOPYSIGN

复制第一个操作数的值,并将符号位替换为第二个操作数的符号位。

G_FCANONICALIZE

参见 ‘llvm.canonicalize.*’ 内在函数

G_IS_FPCLASS

测试第一个操作数(必须是浮点标量或向量)是否具有第二个操作数指定的浮点类。返回非零(真)或零(假)。真值是 1、~0U 还是其他非零值,取决于目标。如果第一个操作数是向量,则返回值是相同长度的向量。

G_FMINNUM

对两个值执行浮点最小值运算。

如果单个输入是 NaN(信号或静默),则返回非 NaN 输入。

(FMINNUM 0.0, -0.0) 的返回值可以是 0.0 或 -0.0。

G_FMAXNUM

对两个值执行浮点最大值运算。

如果单个输入是 NaN(信号或静默),则返回非 NaN 输入。

(FMAXNUM 0.0, -0.0) 的返回值可以是 0.0 或 -0.0。

G_FMINNUM_IEEE

对两个值执行浮点最小值运算,遵循 IEEE-754 定义。这在处理信号 NaN 时与 FMINNUM 不同。

如果一个输入是信号 NaN,则返回静默 NaN。这与 IEEE-754 2008 年的 minnum/maxnum 对信号 NaN 的处理方式相匹配(与 2019 年不同)。

这些将 -0 视为小于 +0,这与 IEEE-754 2019 年的 minimumNumber/maximumNumber 的行为相匹配(在 2008 年未指定)。

G_FMAXNUM_IEEE

对两个值执行浮点最大值运算,遵循 IEEE-754 定义。这在处理信号 NaN 时与 FMAXNUM 不同。

如果一个输入是信号 NaN,则返回静默 NaN。这与 IEEE-754 2008 年的 minnum/maxnum 对信号 NaN 的处理方式相匹配(与 2019 年不同)。

这些将 -0 视为小于 +0,这与 IEEE-754 2019 年的 minimumNumber/maximumNumber 的行为相匹配(在 2008 年未指定)。

G_FMINIMUM

NaN 传播最小值,还将 -0.0 视为小于 0.0。虽然 FMINNUM_IEEE 遵循 IEEE 754-2008 语义,但 FMINIMUM 遵循 IEEE 754-2019 语义。

G_FMAXIMUM

NaN 传播最大值,还将 -0.0 视为小于 0.0。虽然 FMAXNUM_IEEE 遵循 IEEE 754-2008 语义,但 FMAXIMUM 遵循 IEEE 754-2019 语义。

G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FREM

执行指定的浮点算术运算。

G_FMA

执行融合乘加运算(即没有中间舍入步骤)。

G_FMAD

执行非融合乘加运算(即有中间舍入步骤)。

G_FPOW

将第一个操作数提升到第二个操作数的幂。

G_FEXP, G_FEXP2

计算以 e 为底或以 2 为底的值的指数。

G_FLOG, G_FLOG2, G_FLOG10

分别计算以 e 为底、以 2 为底或以 10 为底的对数。

G_FCEIL, G_FSQRT, G_FFLOOR, G_FRINT, G_FNEARBYINT

这些对应于同名的标准 C 函数。

G_FCOS, G_FSIN, G_FTAN, G_FACOS, G_FASIN, G_FATAN, G_FCOSH, G_FSINH, G_FTANH

这些对应于同名的标准 C 三角函数。

G_INTRINSIC_TRUNC

返回舍入到不大于操作数的最近整数的操作数。

G_INTRINSIC_ROUND

返回舍入到最近整数的操作数。

G_LROUND, G_LLROUND

返回舍入到远离零的最近整数的源操作数。

有关行为的详细信息,请参阅 LLVM LangRef 中的“llvm.lround.*'”条目。

%rounded_32:_(s32) = G_LROUND %round_me:_(s64)
%rounded_64:_(s64) = G_LLROUND %round_me:_(s64)

向量特定操作

G_VSCALE

将运行时 vscale 的值乘以源操作数中的值放入目标寄存器。这在确定向量中实际运行时元素数量时很有用。

%0:_(s32) = G_VSCALE 4

G_INSERT_SUBVECTOR

将第二个源向量插入第一个源向量。索引操作数表示在第一个源向量中应插入第二个源向量的起始索引。

索引必须是第二个源向量的最小向量长度的常数倍数。如果向量是可扩展的,则索引首先乘以运行时缩放因子。插入源向量中的索引必须是该向量的有效索引。如果无法静态确定此条件,但在运行时为假,则结果向量未定义。

%2:_(<vscale x 4 x i64>) = G_INSERT_SUBVECTOR %0:_(<vscale x 4 x i64>), %1:_(<vscale x 2 x i64>), 0

G_EXTRACT_SUBVECTOR

从源向量中提取目标类型的向量。索引操作数表示从源向量中提取子向量的起始索引。

索引必须是源向量的最小向量长度的常数倍数。如果源向量是可扩展向量,则索引首先乘以运行时缩放因子。从源向量中提取的索引必须是该向量的有效索引。如果无法静态确定此条件,但在运行时为假,则结果向量未定义。

%3:_(<vscale x 4 x i64>) = G_EXTRACT_SUBVECTOR %2:_(<vscale x 8 x i64>), 2

G_CONCAT_VECTORS

连接两个向量以形成更长的向量。

G_BUILD_VECTOR, G_BUILD_VECTOR_TRUNC

从多个标量寄存器创建向量。不执行隐式转换(即结果元素类型必须与所有源操作数相同)。

_TRUNC 版本将较大的操作数类型截断以适合目标向量元素类型。

G_INSERT_VECTOR_ELT

将元素插入向量。

G_EXTRACT_VECTOR_ELT

从向量中提取元素。

G_SHUFFLE_VECTOR

连接两个向量,并根据掩码操作数对元素进行重新排序。掩码操作数应为与 IR shufflevector 指令对应的掩码完全匹配的 IR 常量。

G_SPLAT_VECTOR

创建一个所有元素都来自源操作数的标量的向量。

操作数的类型必须等于或大于向量元素类型。如果操作数大于向量元素类型,则标量将隐式截断为向量元素类型。

G_VECTOR_COMPRESS

给定一个输入向量、一个掩码向量和一个直通向量,连续地将所有选定的(即,其中 mask[i] = true)输入通道放置到输出向量中。输出中的所有剩余通道都取自直通向量,它可能是未定义的。

向量归约操作

这些操作表示水平向量归约,产生一个标量结果。

G_VECREDUCE_SEQ_FADD, G_VECREDUCE_SEQ_FMUL

SEQ 变体按顺序执行归约。第一个操作数是初始标量累加器值,第二个操作数是要归约的向量。

G_VECREDUCE_FADD, G_VECREDUCE_FMUL

这些归约是宽松的变体,可以以任何顺序归约元素。

G_VECREDUCE_FMAX, G_VECREDUCE_FMIN, G_VECREDUCE_FMAXIMUM, G_VECREDUCE_FMINIMUM

FMIN/FMAX/FMINIMUM/FMAXIMUM 节点可以有标志,用于 NaN/NoNaN 变体。

整数/按位归约

  • G_VECREDUCE_ADD

  • G_VECREDUCE_MUL

  • G_VECREDUCE_AND

  • G_VECREDUCE_OR

  • G_VECREDUCE_XOR

  • G_VECREDUCE_SMAX

  • G_VECREDUCE_SMIN

  • G_VECREDUCE_UMAX

  • G_VECREDUCE_UMIN

整数归约的结果类型可能大于向量元素类型。但是,归约使用向量元素类型执行,最高位的值未指定。

内存操作

G_LOAD, G_SEXTLOAD, G_ZEXTLOAD

通用加载。除了显式操作数外,还期望一个 MachineMemOperand。如果结果大小大于内存大小,则高位分别为未定义、符号扩展或零扩展。

如果结果是向量类型,则只有 G_LOAD 有效。如果结果大于内存大小,则高元素未定义(即,这不是每个元素的向量 anyextload)。

与 SelectionDAG 不同,原子加载使用与常规加载相同的操作码表示。G_LOAD、G_SEXTLOAD 和 G_ZEXTLOAD 都可能具有原子内存操作数。

G_INDEXED_LOAD

通用索引加载。将 GEP 与加载结合起来。$newaddr 设置为 $base + $offset。如果 $am 为 0(后索引),则从 $base 加载值;如果 $am 为 1(前索引),则从 $newaddr 加载值。

G_INDEXED_SEXTLOAD

与 G_INDEXED_LOAD 相同,只是执行的加载是符号扩展,与 G_SEXTLOAD 相同。

G_INDEXED_ZEXTLOAD

与 G_INDEXED_LOAD 相同,只是执行的加载是零扩展,与 G_ZEXTLOAD 相同。

G_STORE

通用存储。除了显式操作数外,还期望一个 MachineMemOperand。如果存储的值大小大于内存大小,则高位将隐式截断。如果这是一个向量存储,则高元素将被丢弃(即,这不会像每个通道的向量一样工作,截断存储)。

G_INDEXED_STORE

将存储与 GEP 结合起来。有关索引行为,请参阅 G_INDEXED_LOAD 的说明。

G_ATOMIC_CMPXCHG_WITH_SUCCESS

具有内部成功检查的通用原子 cmpxchg。除了显式操作数外,还期望一个 MachineMemOperand。

G_ATOMIC_CMPXCHG

通用原子 cmpxchg。除了显式操作数外,还期望一个 MachineMemOperand。

G_ATOMICRMW_XCHG, G_ATOMICRMW_ADD, G_ATOMICRMW_SUB, G_ATOMICRMW_AND, G_ATOMICRMW_NAND, G_ATOMICRMW_OR, G_ATOMICRMW_XOR, G_ATOMICRMW_MAX, G_ATOMICRMW_MIN, G_ATOMICRMW_UMAX, G_ATOMICRMW_UMIN, G_ATOMICRMW_FADD, G_ATOMICRMW_FSUB, G_ATOMICRMW_FMAX, G_ATOMICRMW_FMIN

通用 atomicrmw。除了显式操作数外,还期望一个 MachineMemOperand。

G_FENCE

通用 fence。第一个操作数是内存排序。第二个操作数是同步范围。

有关更多详细信息,请参阅 LLVM LangRef 中的“fence'”指令条目。

G_MEMCPY

通用 memcpy。除了显式操作数外,还期望两个分别覆盖存储和加载的 MachineMemOperands。

G_MEMCPY_INLINE

通用内联 memcpy。与 G_MEMCPY 类似,但保证此版本不会作为对外部函数的调用进行降低。当前,大小操作数需要计算为常量(而不是立即数),尽管当 llvm.memcpy.inline 被教导支持动态大小后,这预计会发生变化。

G_MEMMOVE

通用 memmove。类似于 G_MEMCPY,但允许源内存范围和目标内存范围重叠。

G_MEMSET

通用 memset。除了显式操作数外,还期望一个 MachineMemOperand。

G_BZERO

通用 bzero。除了显式操作数外,还期望一个 MachineMemOperand。

控制流

G_PHI

实现表示函数的 SSA 图中的 φ 节点。

%dst(s8) = G_PHI %src1(s8), %bb.<id1>, %src2(s8), %bb.<id2>

G_BR

无条件分支

G_BR %bb.<id>

G_BRCOND

条件分支

G_BRCOND %condition, %basicblock.<id>

G_BRINDIRECT

间接分支

G_BRINDIRECT %src(p0)

G_BRJT

跳转到跳转表项的间接分支

G_BRJT %ptr(p0), %jti, %idx(s64)

G_JUMP_TABLE

生成一个指向由源操作数指定的跳转表的地址的指针。源操作数是跳转表索引。G_JUMP_TABLE 可以与 G_BRJT 一起使用,以支持使用 GlobalISel 的跳转表代码生成。

%dst:_(p0) = G_JUMP_TABLE %jump-table.0

上面的示例生成一个指向源跳转表索引的指针。

G_INVOKE_REGION_START

一个标记指令,充当可能抛出异常的代码区域的伪终止符。作为一个终止符,它可以防止在诸如合法化之类的过程中在其后插入代码。这是必要的,因为对异常抛出例程的调用不会返回,因此在抛出后,必须位于可执行路径上的任何代码都不能放置。

G_INTRINSIC, G_INTRINSIC_CONVERGENT

调用没有副作用的内联函数。

_CONVERGENT 变体对应于标记为convergent的 LLVM IR 内联函数。

注意

与 SelectionDAG 不同,没有 _VOID 变体。这两种都允许具有零个、一个或多个结果。

G_INTRINSIC_W_SIDE_EFFECTS, G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS

调用被认为具有未知副作用的内联函数,因此不能跨其他具有副作用的指令重新排序。

_CONVERGENT 变体对应于标记为convergent的 LLVM IR 内联函数。

注意

与 SelectionDAG 不同,没有 _VOID 变体。这两种都允许具有零个、一个或多个结果。

G_TRAP, G_DEBUGTRAP, G_UBSANTRAP

表示llvm.trapllvm.debugtrapllvm.ubsantrap,它们生成目标相关的陷阱指令。

G_TRAP
G_DEBUGTRAP
G_UBSANTRAP 12

可变参数

G_VASTART

注意

我在撰写本文时没有找到此指令的文档。

G_VAARG

注意

我在撰写本文时没有找到此指令的文档。

其他操作

G_DYN_STACKALLOC

动态地将栈指针重新对齐到指定的大小和对齐方式。对齐值为01表示没有特定对齐方式。

%8:_(p0) = G_DYN_STACKALLOC %7(s64), 32

优化提示

这些指令不对应于任何目标指令。它们充当各种合并的提示。

G_ASSERT_SEXT, G_ASSERT_ZEXT

这表示寄存器的内容先前已从较小的类型扩展而来。

较小的类型使用立即数操作数表示。对于标量,这是整个较小类型的宽度。对于向量,这是较小元素类型的宽度。

%x_was_zexted:_(s32) = G_ASSERT_ZEXT %x(s32), 16
%y_was_zexted:_(<2 x s32>) = G_ASSERT_ZEXT %y(<2 x s32>), 16

%z_was_sexted:_(s32) = G_ASSERT_SEXT %z(s32), 8

G_ASSERT_SEXT 和 G_ASSERT_ZEXT 就像复制一样,尽管有一些限制。

源寄存器和目标寄存器必须

  • 是虚拟的

  • 属于同一个寄存器类

  • 属于同一个寄存器组

始终可以安全地

  • 查看源寄存器

  • 用源寄存器替换目标寄存器

其他

G_CONSTANT_FOLD_BARRIER

此操作用作不透明屏障,以防止常量折叠。组合和其他转换不应该查看此屏障。这些操作没有其他语义,如果目标选择,可以安全地消除。