LLVM 语言参考手册¶
摘要¶
本文档是 LLVM 汇编语言的参考手册。LLVM 是一种基于静态单赋值 (SSA) 的表示,它提供类型安全、底层操作、灵活性以及清晰地表示“所有”高级语言的能力。它是 LLVM 编译策略所有阶段中使用的通用代码表示。
简介¶
LLVM 代码表示旨在以三种不同的形式使用:作为内存中的编译器 IR,作为磁盘上的位代码表示(适用于即时编译器快速加载),以及作为人类可读的汇编语言表示。这使得 LLVM 能够为高效的编译器转换和分析提供强大的中间表示,同时为调试和可视化转换提供自然的方法。LLVM 的三种不同形式都是等效的。本文档描述了人类可读的表示法和符号。
LLVM 表示旨在轻量级和底层,同时具有表达性、类型化和可扩展性。它的目标是成为一种“通用 IR”,通过处于足够低的级别,高级思想可以清晰地映射到它(类似于微处理器如何成为“通用 IR”,允许许多源语言映射到它们)。通过提供类型信息,LLVM 可以用作优化的目标:例如,通过指针分析,可以证明 C 自动变量永远不会在当前函数之外访问,从而允许将其提升为简单的 SSA 值而不是内存位置。
良好形式¶
重要的是要注意,本文档描述了“良好形式”的 LLVM 汇编语言。解析器接受的内容与被认为是“良好形式”的内容之间存在差异。例如,以下指令在语法上是可以的,但不是良好形式的
%x = add i32 1, %x
因为 %x
的定义不支配其所有用途。LLVM 基础设施提供了一个验证 pass,可用于验证 LLVM 模块是否是良好形式的。此 pass 在解析输入汇编后由解析器自动运行,并在优化器输出位代码之前由优化器自动运行。验证器 pass 指出的违规行为表明转换 pass 或解析器的输入中存在错误。
语法¶
标识符¶
LLVM 标识符有两种基本类型:全局和局部。全局标识符(函数、全局变量)以 '@'
字符开头。局部标识符(寄存器名称、类型)以 '%'
字符开头。此外,标识符有三种不同的格式,用于不同的目的
命名值表示为带有前缀的字符串。例如,
%foo
、@DivisionByZero
、%a.really.long.identifier
。实际使用的正则表达式是 ‘[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*
’。名称中需要其他字符的标识符可以用引号引起来。特殊字符可以使用"\xx"
转义,其中xx
是字符的十六进制 ASCII 代码。通过这种方式,任何字符都可以在名称值中使用,甚至包括引号本身。"\01"
前缀可以用于全局值以抑制名称修饰。未命名值表示为带有前缀的无符号数值。例如,
%12
、@2
、%44
。常量,在下面的 常量 部分中描述。
LLVM 要求值以前缀开头有两个原因:编译器不需要担心与保留字的名称冲突,并且保留字集将来可能会扩展而不会受到惩罚。此外,未命名的标识符允许编译器快速提出一个临时变量,而无需避免符号表冲突。
LLVM 中的保留字与其他语言中的保留字非常相似。不同的操作码('add
'、'bitcast
'、'ret
' 等...)、原始类型名称('void
'、'i32
' 等...)和其他关键字。这些保留字不会与变量名冲突,因为它们都没有以前缀字符('%'
或 '@'
)开头。
这是一个 LLVM 代码示例,用于将整数变量 ‘%X
’ 乘以 8
简单的方法
%result = mul i32 %X, 8
强度缩减后
%result = shl i32 %X, 3
困难的方法
%0 = add i32 %X, %X ; yields i32:%0
%1 = add i32 %0, %0 /* yields i32:%1 */
%result = add i32 %1, %1
最后一种将 %X
乘以 8 的方法说明了 LLVM 的几个重要的词法特征
注释以 ‘
;
’ 分隔,并一直持续到行尾。或者,注释可以以/*
开头,以*/
结尾。当计算结果未分配给命名值时,将创建未命名的临时变量。
默认情况下,未命名的临时变量按顺序编号(使用每个函数的递增计数器,从 0 开始)。但是,当显式指定临时数字时,允许跳过数字。
请注意,基本块和未命名的函数参数包含在此编号中。例如,如果入口基本块未给定标签名称,并且所有函数参数都已命名,则它将获得数字 0。
它还显示了我们在本文档中遵循的约定。在演示指令时,我们将在指令后跟一个注释,该注释定义了生成值的类型和名称。
字符串常量¶
LLVM 程序中的字符串由 "
字符分隔。在字符串中,除了 \
字符(表示转义的开始)和第一个 "
字符(表示字符串的结束)之外,所有字节都按字面意思处理。
有两种转义。
\\
表示单个\
字符。\
后跟两个十六进制字符(0-9、a-f 或 A-F)表示具有给定值的字节(例如,x00 表示空字节)。
要表示 "
字符,请使用 \22
。(\"
将以尾随 \
结束字符串。)
换行符不会终止字符串常量;字符串可以跨越多行。
字符串常量的解释(例如,它们的字符编码)取决于上下文。
高级结构¶
模块结构¶
LLVM 程序由 Module
组成,每个模块都是输入程序的翻译单元。每个模块由函数、全局变量和符号表条目组成。模块可以使用 LLVM 链接器组合在一起,该链接器合并函数(和全局变量)定义,解析前向声明,并合并符号表条目。这是一个 “hello world” 模块的示例
; 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(ptr captures(none)) nounwind
; Definition of main function
define i32 @main() {
; Call puts function to write out the string to stdout.
call i32 @puts(ptr @.str)
ret i32 0
}
; Named metadata
!0 = !{i32 42, null, !"string"}
!foo = !{!0}
此示例由一个名为 “.str
” 的 全局变量、 “puts
” 函数的外部声明、 “main
” 的 函数定义 和名为 “foo
” 的 命名元数据 组成。
通常,模块由全局值列表组成(其中函数和全局变量都是全局值)。全局值由指向内存位置的指针表示(在本例中,是指向字符数组的指针和指向函数的指针),并具有以下 链接类型之一。
链接类型¶
所有全局变量和函数都具有以下链接类型之一
private
具有 “
private
” 链接的全局值只能由当前模块中的对象直接访问。特别是,将代码链接到具有 private 全局值的模块可能会导致 private 根据需要重命名,以避免冲突。由于符号是模块私有的,因此可以更新所有引用。这不会显示在对象文件中的任何符号表中。internal
类似于 private,但该值在对象文件中显示为本地符号(ELF 的情况下为
STB_LOCAL
)。这对应于 C 中 ‘static
’ 关键字的概念。available_externally
具有 “
available_externally
” 链接的全局变量永远不会被发射到与 LLVM 模块对应的对象文件中。从链接器的角度来看,available_externally
全局变量等同于外部声明。它们的存在是为了允许内联和其他优化在已知全局变量定义的情况下进行,该定义已知在模块外部的某个地方。具有available_externally
链接的全局变量可以随意丢弃,并允许内联和其他优化。此链接类型仅允许用于定义,不允许用于声明。linkonce
使用“
linkonce
”链接的全局变量在发生链接时会与同名的其他全局变量合并。这可以用于实现某些形式的内联函数、模板或其他代码,这些代码必须在使用它的每个翻译单元中生成,但其主体可能会在稍后被更明确的定义覆盖。未被引用的linkonce
全局变量可以被丢弃。请注意,linkonce
链接实际上不允许优化器将此函数的主体内联到调用者中,因为它不知道此函数定义是否是程序中的最终定义,或者是否会被更强的定义覆盖。要启用内联和其他优化,请使用“linkonce_odr
”链接。weak
“
weak
”链接与linkonce
链接具有相同的合并语义,但未被引用的具有weak
链接的全局变量可能不会被丢弃。这用于在 C 源代码中声明为 “weak” 的全局变量。common
“
common
” 链接与 “weak
” 链接最相似,但它们用于 C 中的暂定定义,例如全局范围内的 “int X;
”。具有 “common
” 链接的符号以与weak symbols
相同的方式合并,并且如果未被引用,则可能不会被删除。common
符号不能有显式节(section),必须具有零初始化器,并且不能标记为 ‘constant’。函数和别名不能具有 common 链接。
appending
“
appending
” 链接只能应用于指向数组类型的指针的全局变量。当两个具有 appending 链接的全局变量链接在一起时,这两个全局数组将被追加在一起。这是 LLVM 中类型安全的等效方式,相当于系统链接器在链接 .o 文件时将具有相同名称的 “节 (sections)” 追加在一起。不幸的是,这与 .o 文件中的任何特性都不对应,因此它只能用于像
llvm.global_ctors
这样的变量,llvm 对其进行特殊解释。extern_weak
此链接的语义遵循 ELF 对象文件模型:符号在链接之前是弱符号,如果未链接,则符号变为 null 而不是未定义的引用。
linkonce_odr
,weak_odr
odr
后缀表示使用给定名称定义的所有全局变量都是等效的,这符合 C++ “单一定义规则” (“ODR”)。通俗地说,这意味着我们可以内联函数并折叠常量加载。形式上,使用以下定义:当调用
odr
函数时,会非确定性地选择其中一个定义来运行。对于odr
变量,如果值中的任何字节在所有初始化器中都不相等,则该字节是 poison value。对于别名和 ifuncs,应用底层函数或变量的规则。这些链接类型在其他方面与其非
odr
版本相同。external
如果未使用上述任何标识符,则全局变量是外部可见的,这意味着它参与链接,并且可以用于解析外部符号引用。
全局变量或函数的声明具有除 external
或 extern_weak
之外的任何链接类型都是非法的。
调用约定¶
LLVM 函数、调用 和 调用指令 都可以具有为调用指定的可选调用约定。任何一对动态调用者/被调用者的调用约定必须匹配,否则程序的行为是未定义的。LLVM 支持以下调用约定,并且将来可能会添加更多约定
- “
ccc
” - C 调用约定 此调用约定(如果未指定其他调用约定,则为默认约定)与目标 C 调用约定匹配。此调用约定支持可变参数函数调用,并容忍函数声明的原型和已实现的声明之间的一些不匹配(就像普通的 C 一样)。
- “
fastcc
” - 快速调用约定 此调用约定尝试使调用尽可能快(例如,通过在寄存器中传递参数)。此调用约定允许目标使用任何技巧来为目标生成快速代码,而无需符合外部指定的 ABI(应用程序二进制接口)。仅当使用此约定、tailcc、GHC 或 HiPE 约定,尾调用才能被优化。 此调用约定不支持可变参数,并且要求所有被调用者的原型与函数定义的原型完全匹配。
- “
coldcc
” - 冷调用约定 此调用约定尝试在调用不常执行的假设下,使调用者中的代码尽可能高效。因此,这些调用通常会保留所有寄存器,以便调用不会破坏调用者端的任何活跃范围。此调用约定不支持可变参数,并且要求所有被调用者的原型与函数定义的原型完全匹配。此外,内联器不会考虑将此类函数调用进行内联。
- “
ghccc
” - GHC 约定 此调用约定是专门为 Glasgow Haskell Compiler (GHC) 实现的。它将所有内容都通过寄存器传递,并通过禁用被调用者保存寄存器来竭力实现这一点。不应轻率地使用此调用约定,而应仅用于特定情况,例如在实现函数式编程语言时经常使用的寄存器绑定性能技术的替代方案。目前,只有 X86、AArch64 和 RISCV 支持此约定。存在以下限制
在 X86-32 上,仅支持最多 4 个位类型参数。不支持浮点类型。
在 X86-64 上,仅支持最多 10 个位类型参数和 6 个浮点参数。
在 AArch64 上,仅支持最多 4 个 32 位浮点参数、4 个 64 位浮点参数和 10 个位类型参数。
RISCV64 仅支持最多 11 个位类型参数、4 个 32 位浮点参数和 4 个 64 位浮点参数。
此调用约定支持 尾调用优化,但要求调用者和被调用者都使用它。
- “
cc 11
” - HiPE 调用约定 此调用约定是专门为 High-Performance Erlang (HiPE) 编译器实现的,它是 Ericsson 的开源 Erlang/OTP 系统的本机代码编译器。它使用比普通 C 调用约定更多的寄存器来传递参数,并且没有定义被调用者保存的寄存器。此调用约定正确地支持 尾调用优化,但要求调用者和被调用者都使用它。它使用类似于 GHC 约定的寄存器绑定机制,用于将频繁访问的运行时组件绑定到特定的硬件寄存器。目前,只有 X86 支持此约定(包括 32 位和 64 位)。
- “
anyregcc
” - 用于代码修补的动态调用约定 这是一个特殊的约定,支持在调用点位置修补任意代码序列。此约定强制将调用参数放入寄存器,但允许动态分配它们。目前,这只能与对 llvm.experimental.patchpoint 的调用一起使用,因为只有这个 intrinsic 记录了其参数在边表中的位置。请参阅 LLVM 中的堆栈映射和补丁点。
- “
preserve_mostcc
” - PreserveMost 调用约定 此调用约定尝试使调用者中的代码尽可能不具侵入性。此约定在参数和返回值如何传递方面与 C 调用约定完全相同,但它使用一组不同的调用者/被调用者保存的寄存器。这减轻了在调用者中调用之前和之后保存和恢复大型寄存器集的负担。如果参数在被调用者保存的寄存器中传递,则它们将在调用期间由被调用者保留。这不适用于在被调用者保存的寄存器中返回的值。
在 X86-64 上,被调用者保留除 R11 和返回寄存器(如果有)之外的所有通用寄存器。R11 可以用作暂存寄存器。浮点寄存器 (XMM/YMM) 的处理方式与操作系统的 C 调用约定匹配:在大多数平台上,它们不会被保留,需要由调用者保存,但在 Windows 上,xmm6-xmm15 会被保留。
在 AArch64 上,被调用者保留除 X0-X8 和 X16-X18 之外的所有通用寄存器。
此约定背后的想法是支持对具有热路径和冷路径的运行时函数的调用。热路径通常是一小段不使用太多寄存器的代码。冷路径可能需要调用另一个函数,因此只需要保留尚未被调用者保存的调用者保存的寄存器。PreserveMost 调用约定在调用者/被调用者保存的寄存器方面与 cold 调用约定非常相似,但它们用于不同类型的函数调用。coldcc 用于很少执行的函数调用,而 preserve_mostcc 函数调用旨在位于热路径上,并且肯定会被大量执行。此外,preserve_mostcc 不会阻止内联器内联函数调用。
此调用约定将由未来版本的 ObjectiveC 运行时使用,因此目前仍应被视为实验性的。尽管此约定是为了优化对 ObjectiveC 运行时的某些运行时调用而创建的,但它不限于此运行时,将来也可能被其他运行时使用。当前实现仅支持 X86-64,但计划将来支持更多架构。
- “
preserve_allcc
” - PreserveAll 调用约定 此调用约定尝试使调用者中的代码比 PreserveMost 调用约定更不具侵入性。此调用约定在参数和返回值如何传递方面也与 C 调用约定完全相同,但它使用一组不同的调用者/被调用者保存的寄存器。这消除了在调用者中调用之前和之后保存和恢复大型寄存器集的负担。如果参数在被调用者保存的寄存器中传递,则它们将在调用期间由被调用者保留。这不适用于在被调用者保存的寄存器中返回的值。
在 X86-64 上,被调用者保留除 R11 之外的所有通用寄存器。R11 可以用作暂存寄存器。此外,它还保留所有浮点寄存器 (XMM/YMM)。
在 AArch64 上,被调用者保留除 X0-X8 和 X16-X18 之外的所有通用寄存器。此外,它还保留 V8-V31 SIMD - 浮点寄存器的低 128 位。
此约定背后的想法是支持对不需要调用任何其他函数的运行时函数的调用。
此调用约定与 PreserveMost 调用约定一样,将由未来版本的 ObjectiveC 运行时使用,并且目前应被视为实验性的。
- “
preserve_nonecc
” - PreserveNone 调用约定 此调用约定不保留任何通用寄存器。因此,所有通用寄存器都是调用者保存的寄存器。它还使用所有通用寄存器来传递参数。此属性不影响非通用寄存器(例如,浮点寄存器,在 X86 上为 XMM/YMM)。非通用寄存器仍然遵循标准的 c 调用约定。目前,它仅适用于 x86_64 和 AArch64。
- “
cxx_fast_tlscc
” - 用于访问函数的 CXX_FAST_TLS 调用约定 Clang 生成一个访问函数来访问 C++ 样式的 TLS。访问函数通常具有入口块、出口块和首次运行时运行的初始化块。入口块和出口块可以访问一些 TLS IR 变量,每次访问都将降级为平台特定的序列。
此调用约定旨在通过保留尽可能多的寄存器(快速路径上保留的所有寄存器,由入口块和出口块组成)来最大限度地减少调用者中的开销。
此调用约定在参数和返回值如何传递方面与 C 调用约定完全相同,但它使用一组不同的调用者/被调用者保存的寄存器。
鉴于每个平台都有自己的降级序列,因此也有自己的一组保留寄存器,我们不能使用现有的 PreserveMost。
在 X86-64 上,被调用者保留除 RDI 和 RAX 之外的所有通用寄存器。
- “
tailcc
” - 尾调用可调用调用约定 此调用约定确保尾部位置的调用始终会被尾调用优化。此调用约定等效于 fastcc,但增加了一个额外的保证,即在可能的情况下始终会生成尾调用。仅当使用此约定、fastcc、GHC 或 HiPE 约定,尾调用才能被优化。 此调用约定不支持可变参数,并且要求所有被调用者的原型与函数定义的原型完全匹配。
- “
swiftcc
” - 此调用约定用于 Swift 语言。 在 X86-64 上,RCX 和 R8 可用于额外的整数返回值,XMM2 和 XMM3 可用于额外的 FP/向量返回值。
在 iOS 平台上,我们使用 AAPCS-VFP 调用约定。
- “
swifttailcc
” 此调用约定在大多数方面都类似于
swiftcc
,但被调用者也会弹出堆栈的参数区域,以便可以像在tailcc
中那样进行强制尾调用。- “
cfguard_checkcc
” - Windows 控制流保护 (检查机制) 此调用约定用于控制流保护检查函数,可以在间接调用之前插入对此函数的调用,以检查调用目标是否为有效的函数地址。检查函数没有返回值,但如果地址不是有效的目标,它将触发操作系统级别的错误。由检查函数保留的寄存器集以及包含目标地址的寄存器是特定于体系结构的。
在 X86 上,目标地址在 ECX 中传递。
在 ARM 上,目标地址在 R0 中传递。
在 AArch64 上,目标地址在 X15 中传递。
- “
cc <n>
” - 编号约定 任何调用约定都可以通过数字指定,从而允许使用特定于目标的调用约定。特定于目标的调用约定从 64 开始。
可以根据需要添加/定义更多调用约定,以支持 Pascal 约定或任何其他众所周知的目标无关约定。
可见性样式¶
所有全局变量和函数都具有以下可见性样式之一
- “
default
” - 默认样式 在使用 ELF 对象文件格式的目标上,默认可见性意味着声明对其他模块可见,并且在共享库中,意味着声明的实体可能会被覆盖。在 Darwin 上,默认可见性意味着声明对其他模块可见。在 XCOFF 上,默认可见性意味着不会设置显式可见性位,并且符号是否对其他模块可见(即“导出”)主要取决于提供给链接器的导出列表。默认可见性对应于语言中的“外部链接”。
- “
hidden
” - 隐藏样式 如果具有隐藏可见性的对象的两个声明位于同一共享对象中,则它们引用同一对象。通常,隐藏可见性表示符号不会被放入动态符号表,因此没有其他模块(可执行文件或共享库)可以直接引用它。
- “
protected
” - 受保护样式 在 ELF 上,受保护的可见性表示符号将放置在动态符号表中,但是定义模块内的引用将绑定到本地符号。也就是说,该符号不能被另一个模块覆盖。
具有 internal
或 private
链接的符号必须具有 default
可见性。
DLL 存储类¶
所有全局变量、函数和别名都可以具有以下 DLL 存储类之一
dllimport
“
dllimport
” 使编译器通过指向由导出符号的 DLL 设置的指针的全局指针来引用函数或变量。在 Microsoft Windows 目标上,指针名称是通过组合__imp_
和函数或变量名称形成的。dllexport
在 Microsoft Windows 目标上,“
dllexport
” 使编译器在 DLL 中提供指向指针的全局指针,以便可以使用dllimport
属性引用它。指针名称是通过组合__imp_
和函数或变量名称形成的。在 XCOFF 目标上,dllexport
表示符号将对使用“exported”可见性的其他模块可见,因此链接器会将其放置在加载器节符号表中。由于此存储类用于定义 dll 接口,因此编译器、汇编器和链接器知道它是外部引用的,因此必须避免删除该符号。
具有 internal
或 private
链接的符号不能具有 DLL 存储类。
线程局部存储模型¶
变量可以定义为 thread_local
,这意味着它不会在线程之间共享(每个线程将具有该变量的单独副本)。并非所有目标都支持线程局部变量。可选地,可以指定 TLS 模型
localdynamic
对于仅在当前共享库中使用的变量。
initialexec
对于不会动态加载的模块中的变量。
localexec
对于在可执行文件中定义且仅在其中使用的变量。
如果未给出显式模型,则使用“通用动态”模型。
这些模型对应于 ELF TLS 模型;有关可以使用不同模型的具体情况的更多信息,请参阅 ELF 处理线程局部存储。如果指定模型不受支持,或者可以选择更好的模型,则目标可以选择不同的 TLS 模型。
模型也可以在别名中指定,但随后它仅控制别名如何被访问。它不会对 aliasee 产生任何影响。
对于没有 ELF TLS 模型链接器支持的平台,可以使用 -femulated-tls 标志来生成 GCC 兼容的模拟 TLS 代码。
运行时抢占说明符¶
全局变量、函数和别名可以具有可选的运行时抢占说明符。如果未显式给出抢占说明符,则假定符号为 dso_preemptable
。
dso_preemptable
表示函数或变量可能在运行时被来自链接单元外部的符号替换。
dso_local
编译器可以假定标记为
dso_local
的函数或变量将解析为同一链接单元内的符号。即使定义不在该编译单元内,也会生成直接访问。
结构类型¶
LLVM IR 允许您指定 “已标识” 和 “字面量” 结构类型。字面量类型在结构上是唯一的,但已标识类型永远不是唯一的。 不透明结构类型 也可用于转发声明尚未可用的类型。
已标识结构规范的示例是
%mytype = type { %mytype*, i32 }
在 LLVM 3.0 版本之前,已标识类型在结构上是唯一的。在最新版本的 LLVM 中,只有字面量类型是唯一的。
非整型指针类型¶
注意:非整型指针类型是一项正在进行中的工作,目前应将其视为实验性的。
LLVM IR 可选地允许前端通过 datalayout 字符串 将某些地址空间中的指针表示为 “非整型”。非整型指针类型表示具有未指定按位表示的指针;也就是说,整数表示可能取决于目标或不稳定(不受固定整数支持)。
inttoptr
和 ptrtoint
指令具有与整型(即普通)指针相同的语义,因为它们在整数和相应的指针类型之间进行转换,但是有一些额外的含义需要注意。由于非整型指针的位表示可能不稳定,因此对同一操作数进行两次相同的转换可能会或可能不会返回相同的值。换句话说,与非整型类型的转换取决于实现定义的方式中的环境状态。
如果前端希望观察转换后的特定值,则生成的 IR 必须以实现定义的方式与底层环境进行栅栏操作。(在实践中,这通常需要 noinline
例程来进行此类操作。)
从优化器的角度来看,非整型类型的 inttoptr
和 ptrtoint
与整型类型上的类似,但有一个关键例外:优化器通常可能不会插入此类转换的新动态出现。如果插入了新的转换,则优化器需要确保 a) 所有可能的值都是有效的,或者 b) 插入了适当的栅栏操作。由于适当的栅栏操作是实现定义的,因此优化器无法执行后者。前者具有挑战性,因为许多常见的预期属性,例如 ptrtoint(v)-ptrtoint(v) == 0
,对于非整型类型不成立。类似的限制适用于可能检查指针位的 intrinsic,例如 llvm.ptrmask。
前端为非整型指针提供的对齐信息(通常使用属性或元数据)对于指针的每种可能的表示形式都必须有效。
全局变量¶
全局变量定义在编译时而不是运行时分配的内存区域。
全局变量定义必须被初始化。
其他翻译单元中的全局变量也可以被声明,在这种情况下,它们没有初始化器。
全局变量可以选择性地指定 链接类型。
全局变量定义或声明都可以具有要放置的显式节,并且可以具有可选的显式对齐方式指定。如果变量声明及其定义的显式或推断的节信息之间存在不匹配,则结果行为是未定义的。
变量可以定义为全局 constant
,这表示变量的内容将永远不会被修改(从而实现更好的优化,允许将全局数据放置在可执行文件的只读节中等)。请注意,需要运行时初始化的变量不能标记为 constant
,因为存在对变量的存储。
LLVM 显式允许将全局变量的声明标记为常量,即使全局变量的最终定义不是常量。此功能可用于稍微更好地优化程序,但要求语言定义保证基于 “constantness” 的优化对于不包含定义的翻译单元是有效的。
作为 SSA 值,全局变量定义在程序中所有基本块范围内(即,它们支配)的指针值。全局变量始终定义指向其 “内容” 类型的指针,因为它们描述了内存区域,并且 LLVM 中的所有 已分配对象 都是通过指针访问的。
全局变量可以标记为 unnamed_addr
,这表示地址不重要,重要的是内容。如果具有相同的初始化器,则标记为这样的常量可以与其他常量合并。请注意,具有重要地址的常量可以与 unnamed_addr
常量合并,结果是地址重要的常量。
如果给定 local_unnamed_addr
属性,则已知地址在模块内不重要。
全局变量可以声明为驻留在特定于目标的编号地址空间中。对于支持地址空间的目标,地址空间可能会影响优化的执行方式和/或用于访问变量的目标指令。默认地址空间为零。地址空间限定符必须位于任何其他属性之前。
LLVM 允许为全局变量指定显式节。如果目标支持,它会将全局变量发出到指定的节。此外,如果目标具有必要的支持,则可以将全局变量放置在 comdat 中。
外部声明可以具有指定的显式节。节信息保留在 LLVM IR 中,以用于利用此信息的目标。将节信息附加到外部声明是一种断言,即其定义位于指定的节中。如果定义位于不同的节中,则行为是未定义的。
LLVM 允许为全局变量指定显式代码模型。如果目标支持,它将以指定的代码模型发出全局变量,从而覆盖用于编译翻译单元的代码模型。允许的值为 “tiny”、“small”、“kernel”、“medium”、“large”。将来可能会扩展此功能,以指定不完全适合特定代码模型的全局数据布局。
默认情况下,全局初始化器通过假设模块内定义的全局变量在其初始值之后,在全局初始化器启动之前未被修改来进行优化。即使对于可能从模块外部访问的变量,包括具有外部链接或出现在 @llvm.used
或 dllexported 变量中的变量,也是如此。可以通过使用 externally_initialized
标记变量来抑制此假设。
可以为全局变量指定显式对齐方式,该对齐方式必须是 2 的幂。如果不存在,或者如果对齐方式设置为零,则全局变量的对齐方式由目标设置为其认为方便的任何值。如果指定了显式对齐方式,则强制全局变量具有完全相同的对齐方式。如果全局变量具有分配的节,则目标和优化器不允许过度对齐全局变量。在这种情况下,额外的对齐方式可能是可观察的:例如,代码可以假设全局变量在其节中密集打包,并尝试将它们作为数组进行迭代,对齐填充会破坏此迭代。对于 TLS 变量,模块标志 MaxTLSAlign
(如果存在)会将对齐方式限制为给定值。优化器不允许对这些变量施加更强的对齐方式。最大对齐方式为 1 << 32
。
对于全局变量声明,以及可能在链接时被替换的定义(linkonce
、weak
、extern_weak
和 common
链接类型),其解析到的定义的分配大小和对齐方式必须大于或等于声明或可替换的定义的大小和对齐方式,否则行为是未定义的。
全局变量还可以具有DLL 存储类、可选的运行时抢占说明符、可选的全局属性以及可选的附加元数据列表。
变量和别名可以具有线程本地存储模型。
全局变量不能是或包含可伸缩向量,因为它们的大小在编译时是未知的。它们在结构体中是允许的,以方便内部函数返回多个值。通常,包含可伸缩向量的结构体不被认为是“定大小的”,不能用于加载、存储、alloca 或 GEP 操作。此规则的唯一例外是包含相同类型的可伸缩向量的结构体(例如,{<vscale x 2 x i32>, <vscale x 2 x i32>}
包含相同的类型,而 {<vscale x 2 x i32>, <vscale x 2 x i64>}
则不包含)。这些类型的结构体(我们可以称之为同构可伸缩向量结构体)被认为是定大小的,可以用于加载、存储、alloca 操作,但不能用于 GEP 操作。
设置了 toc-data
属性的全局变量存储在 XCOFF 的 TOC 中。它们的对齐方式不大于 TOC 条目的对齐方式。优化不应增加它们的对齐方式以缓解 TOC 溢出。
语法
@<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>]
[, code_model "model"]
[, no_sanitize_address] [, no_sanitize_hwaddress]
[, sanitize_address_dyninit] [, sanitize_memtag]
(, !name !N)*
例如,以下代码在一个编号的地址空间中定义了一个带有初始化器、节和对齐方式的全局变量
@G = addrspace(5) constant float 1.0, section "foo", align 4
以下示例仅声明了一个全局变量
@G = external global i32
以下示例使用 large
代码模型定义了一个全局变量
@G = internal global i32 0, code_model "large"
以下示例使用 initialexec
TLS 模型定义了一个线程本地全局变量
@G = thread_local(initialexec) global i32 0, align 4
函数¶
LLVM 函数定义由 “define
” 关键字、可选的链接类型、可选的运行时抢占说明符、可选的可见性样式、可选的DLL 存储类、可选的调用约定、可选的 unnamed_addr
属性、返回类型、返回类型的可选参数属性、函数名称、一个(可能为空的)参数列表(每个参数都有可选的参数属性)、可选的函数属性、可选的地址空间、可选的节、可选的分区、可选的对齐方式、可选的comdat、可选的垃圾回收器名称、可选的前缀数据、可选的序言数据、可选的人格化函数、可选的附加元数据列表、一个左花括号、一个基本块列表和一个右花括号组成。
语法
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)* { ... }
参数列表是以逗号分隔的参数序列,其中每个参数的形式如下
语法
<type> [parameter Attrs] [name]
LLVM 函数声明由 “declare
” 关键字、可选的链接类型、可选的可见性样式、可选的DLL 存储类、可选的调用约定、可选的 unnamed_addr
或 local_unnamed_addr
属性、可选的地址空间、返回类型、返回类型的可选参数属性、函数名称、一个可能为空的参数列表、可选的对齐方式、可选的垃圾回收器名称、可选的前缀数据和可选的序言数据组成。
语法
declare [linkage] [visibility] [DLLStorageClass]
[cconv] [ret attrs]
<ResultType> @<FunctionName> ([argument list])
[(unnamed_addr|local_unnamed_addr)] [align N] [gc]
[prefix Constant] [prologue Constant]
函数定义包含一个基本块列表,形成函数的 CFG(控制流图)。每个基本块可以选择性地以标签开始(为基本块提供符号表条目),包含指令和调试记录列表,并以终止符指令(例如分支或函数返回)结束。如果未提供显式标签名称,则会为块分配一个隐式编号的标签,使用与未命名临时变量相同的计数器中的下一个值(见上文)。例如,如果函数入口块没有显式标签,则会为其分配标签 “%0”,然后该块中的第一个未命名临时变量将是 “%1”,依此类推。如果显式指定了数字标签,则它必须与将隐式使用的数字标签匹配。
函数中的第一个基本块在两个方面是特殊的:它在进入函数时立即执行,并且不允许有前驱基本块(即,不能有任何分支到函数的入口块)。由于该块不能有前驱,因此它也不能有任何 PHI 节点。
LLVM 允许为函数指定显式节。如果目标平台支持,它会将函数发出到指定的节中。此外,函数可以放置在 COMDAT 中。
可以为函数指定显式对齐方式。如果不存在,或者如果对齐方式设置为零,则函数的对齐方式由目标平台设置为它认为方便的任何值。如果指定了显式对齐方式,则强制函数至少具有该对齐方式。所有对齐方式都必须是 2 的幂。
如果给定了 unnamed_addr
属性,则地址已知不重要,并且可以合并两个相同的函数。
如果给定 local_unnamed_addr
属性,则已知地址在模块内不重要。
如果未给出显式地址空间,则它将默认为来自数据布局字符串的程序地址空间。
别名¶
与函数或变量不同,别名不创建任何新数据。它们只是现有位置的新符号和元数据。
别名具有名称和一个别名,该别名可以是全局值或常量表达式。
别名可以具有可选的链接类型、可选的运行时抢占说明符、可选的可见性样式、可选的DLL 存储类和可选的tls 模型。
语法
@<Name> = [Linkage] [PreemptionSpecifier] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias <AliaseeTy>, <AliaseeTy>* @<Aliasee>
[, partition "name"]
链接必须是 private
、internal
、linkonce
、weak
、linkonce_odr
、weak_odr
、external
、available_externally
之一。请注意,某些系统链接器可能无法正确处理删除被别名化的弱符号。
不是 unnamed_addr
的别名保证具有与别名表达式相同的地址。unnamed_addr
的别名仅保证指向相同的内容。
如果给定 local_unnamed_addr
属性,则已知地址在模块内不重要。
由于别名只是第二个名称,因此适用一些限制,其中一些限制只能在生成目标文件时检查
定义别名表达式必须在汇编时可计算。由于它只是一个名称,因此不能使用重定位。
表达式中不能有弱别名,因为中间别名被覆盖的可能性无法在目标文件中表示。
如果别名具有
available_externally
链接,则别名必须是available_externally
全局值;否则,别名可以是表达式,但表达式中的全局值不能是声明,因为这将需要重定位,这是不可能的。如果别名或别名可能在链接时或运行时被模块外部的符号替换,则任何优化都不能用别名替换别名,因为行为可能不同。别名可以用作保证指向当前模块中内容的名称。
IFuncs¶
与别名一样,IFuncs 不创建任何新数据或函数。它们只是一个新的符号,通过调用解析器函数在运行时解析。
在 ELF 平台上,IFuncs 在加载时由动态链接器解析。在 Mach-O 平台上,它们以 .symbol_resolver
函数的形式降低,这些函数在第一次调用时延迟解析被调用者。
语法
@<Name> = [Linkage] [PreemptionSpecifier] [Visibility] ifunc <IFuncTy>, <ResolverTy>* @<Resolver>
[, partition "name"]
Comdats¶
Comdat IR 提供了对目标文件 COMDAT/节组功能的访问,该功能表示相互关联的节。
Comdats 具有一个名称,该名称表示 COMDAT 键,以及一个选择类型,以提供关于链接器如何消除两个不同目标文件中具有相同键的 comdat 的重复数据的输入。comdat 必须作为一个单元包含或省略。允许丢弃整个 comdat,但不允许丢弃子集。
全局对象最多可以是一个 comdat 的成员。别名放置在它们的别名计算到的同一个 COMDAT 中(如果有)。
语法
$<Name> = comdat SelectionKind
对于 nodeduplicate
以外的选择类型,重复 comdat 中只能有一个可以被链接器保留,并且必须丢弃其余 comdat 的成员。支持以下选择类型
any
链接器可以选择任何 COMDAT 键,选择是任意的。
exactmatch
链接器可以选择任何 COMDAT 键,但节必须包含相同的数据。
largest
链接器将选择包含最大 COMDAT 键的节。
nodeduplicate
不执行重复数据消除。
samesize
链接器可以选择任何 COMDAT 键,但节必须包含相同数量的数据。
XCOFF 和 Mach-O 不支持 COMDAT。
COFF 支持所有选择类型。非
nodeduplicate
选择类型需要非本地链接 COMDAT 符号。ELF 支持
any
和nodeduplicate
。WebAssembly 仅支持
any
。
以下是一个 COFF COMDAT 的示例,其中仅当 COMDAT 键的节是最大时才会选择函数
$foo = comdat largest
@foo = global i32 2, comdat($foo)
define void @bar() comdat($foo) {
ret void
}
在 COFF 目标文件中,这将创建一个选择类型为 IMAGE_COMDAT_SELECT_LARGEST
的 COMDAT 节,其中包含 @foo
符号的内容,以及另一个选择类型为 IMAGE_COMDAT_SELECT_ASSOCIATIVE
的 COMDAT 节,该节与第一个 COMDAT 节关联并包含 @bar
符号的内容。
作为语法糖,如果名称与全局名称相同,则可以省略 $name
$foo = comdat any
@foo = global i32 2, comdat
@bar = global i32 3, comdat($foo)
全局对象的属性存在一些限制。当以 COFF 为目标时,它或其别名必须与 COMDAT 组具有相同的名称。此对象的内容和大小可能在链接时用于确定哪些 COMDAT 组根据选择类型被选中。由于对象的名称必须与 COMDAT 组的名称匹配,因此全局对象的链接不能是本地的;如果符号表中发生冲突,则本地符号可能会被重命名。
COMDATS 和节属性的组合使用可能会产生令人惊讶的结果。例如
$foo = comdat any
$bar = comdat any
@g1 = global i32 42, section "sec", comdat($foo)
@g2 = global i32 42, section "sec", comdat($bar)
从目标文件的角度来看,这需要创建两个具有相同名称的节。这是必要的,因为两个全局变量都属于不同的 COMDAT 组,并且 COMDAT 在目标文件级别上由节表示。
请注意,某些 IR 构造(如全局变量和函数)可能会在目标文件中创建 COMDAT,除了任何使用 COMDAT IR 指定的 COMDAT 之外。当代码生成器配置为在单独的节中发出全局变量时(例如,当为 llc 提供 -data-sections 或 -function-sections 时),就会出现这种情况。
命名元数据¶
命名元数据是元数据的集合。元数据节点(但不是元数据字符串)是命名元数据的唯一有效操作数。
命名元数据表示为带有元数据前缀的字符串。元数据名称的规则与标识符的规则相同,但不允许使用带引号的名称。
"\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}
参数属性¶
函数类型的返回类型和每个参数都可以有一组与之关联的参数属性。参数属性用于传达关于函数结果或参数的附加信息。参数属性被认为是函数的一部分,而不是函数类型的一部分,因此具有不同参数属性的函数可以具有相同的函数类型。参数属性可以放置在函数声明/定义和调用点。
参数属性可以是简单的关键字或跟随指定类型的字符串。当需要多个参数属性时,它们用空格分隔。例如
; On function declarations/definitions:
declare i32 @printf(ptr noalias captures(none), ...)
declare i32 @atoi(i8 zeroext)
declare signext i8 @returns_signed_char()
define void @baz(i32 "amdgpu-flat-work-group-size"="1,256" %x)
; On call-sites:
call i32 @atoi(i8 zeroext %x)
call signext i8 @returns_signed_char()
请注意,函数结果的任何属性(nonnull
、signext
)都位于结果类型之前。
参数属性可以大致分为两种:ABI 属性,它影响值如何传递给函数/从函数传递,例如 zeroext
、inreg
、byval
或 sret
。以及优化属性,它提供额外的优化保证,例如 noalias
、nonnull
和 dereferenceable
。
ABI 属性必须在函数声明/定义和调用点都指定,否则行为可能是未定义的。ABI 属性不能安全地删除。优化属性不必在调用点和函数之间匹配:应用它们隐含语义的交集。优化属性也可以自由地删除。
如果函数的整数参数未标记 signext/zeroext/noext,则使用的扩展类型是特定于目标的。某些目标平台依赖于显式指定的扩展类型以确保正确性。
目前,仅定义了以下参数属性
zeroext
这向代码生成器指示,参数或返回值应由调用者(对于参数)或被调用者(对于返回值)零扩展到目标平台的 ABI 所需的范围。
signext
这向代码生成器指示,参数或返回值应由调用者(对于参数)或被调用者(对于返回值)符号扩展到目标平台的 ABI 所需的范围(通常为 32 位)。
noext
这向代码生成器指示,参数或返回值的高位是未定义的,就像寄存器中的结构体一样,因此不需要进行符号扩展或零扩展。这与默认行为相同,实际上仅用于(某些目标平台)验证是否始终存在其中一个属性。
inreg
这指示在为函数调用或返回发出代码时,应以特殊的目标平台相关的方式处理此参数或返回值(通常,通过将其放入寄存器而不是内存中,尽管某些目标平台使用它来区分两种不同的寄存器)。此属性的使用是特定于目标平台的。
byval(<ty>)
这指示指针参数实际上应该按值传递给函数。该属性意味着在调用者和被调用者之间创建了被指向值的隐藏副本,因此被调用者无法修改调用者中的值。此属性仅对 LLVM 指针参数有效。它通常用于按值传递结构体和数组,但也对指向标量的指针有效。副本被认为属于调用者而不是被调用者(例如,
readonly
函数不应写入byval
参数)。这不是返回值的有效属性。byval 类型参数指示内存中的值类型,并且必须与参数的被指向类型相同。
byval 属性还支持使用 align 属性指定对齐方式。它指示要形成的堆栈槽的对齐方式以及调用点指定的指针的已知对齐方式。如果未指定对齐方式,则代码生成器会做出特定于目标平台的假设。
byref(<ty>)
byref
参数属性允许指定参数的被指向内存类型。这类似于byval
,但不意味着在任何地方创建副本,也不意味着参数在堆栈上传递。这意味着指针是可解引用的,直到类型的存储大小。通常不允许引入对
byref
指针的写入。指针可以具有任何地址空间,并且可以是只读的。这不是返回值的有效属性。
byref
参数的对齐方式可以通过将其与align
属性组合来显式指定,类似于byval
。如果未指定对齐方式,则代码生成器会做出特定于目标平台的假设。这旨在表示 ABI 约束,而不是旨在用于优化推断。
preallocated(<ty>)
这指示指针参数实际上应该按值传递给函数,并且指针参数的被指向值在调用指令之前已初始化。此属性仅对 LLVM 指针参数有效。参数必须是由非
musttail
调用上的适当 llvm.call.preallocated.arg 返回的值,或者是musttail
调用中的相应调用方参数,尽管在代码生成期间它被忽略。任何参数中具有
preallocated
属性的非musttail
函数调用必须具有"preallocated"
操作数捆绑包。musttail
函数调用不能具有"preallocated"
操作数捆绑包。preallocated 属性需要类型参数,该类型参数必须与参数的被指向类型相同。
preallocated 属性还支持使用 align 属性指定对齐方式。它指示要形成的堆栈槽的对齐方式以及调用点指定的指针的已知对齐方式。如果未指定对齐方式,则代码生成器会做出特定于目标平台的假设。
inalloca(<ty>)
inalloca
参数属性允许调用者获取传出堆栈参数的地址。inalloca
参数必须是指向alloca
指令生成的堆栈内存的指针。alloca 或参数分配也必须使用 inalloca 关键字标记。只有最后一个参数可以具有inalloca
属性,并且保证该参数在内存中传递。参数分配最多可以被调用使用一次,因为调用可能会解除分配它。
inalloca
属性不能与影响参数存储的其他属性(如inreg
、nest
、sret
或byval
)结合使用。inalloca
属性还会禁用 LLVM 隐式降低大型聚合返回值,这意味着前端作者必须使用sret
指针来降低它们。当到达调用点时,参数分配必须是仍然存在的最新堆栈分配,否则行为是未定义的。可以在参数分配之后和其调用点之前分配额外的堆栈空间,但必须使用 llvm.stackrestore 清除它。
inalloca 属性需要类型参数,该类型参数必须与参数的被指向类型相同。
有关如何使用此属性的更多信息,请参阅 InAlloca 属性的设计和用法。
sret(<ty>)
这指示指针参数指定源程序中作为函数返回值的结构的地址。调用者必须保证此指针有效:被调用者可以假定对结构的加载和存储不会陷入陷阱并且已正确对齐。
sret 类型参数指定内存中的类型,该类型必须与参数的被指向类型相同。
接受
sret
参数的函数必须返回void
。返回值不能是sret
。
elementtype(<ty>)
elementtype
参数属性可用于以与不透明指针兼容的方式指定指针元素类型。
elementtype
属性本身不携带任何特定语义。但是,某些内在函数可能需要此属性存在并为其分配特定的语义。这将在各个内在函数上记录。该属性只能应用于内在函数调用的指针类型参数。它不能应用于非内在函数调用,也不能应用于函数声明中的参数。对于非不透明指针,传递给
elementtype
的类型必须与指针元素类型匹配。
align <n>
或align(<n>)
这指示指针值或指针向量具有指定的对齐方式。如果应用于指针向量,则所有指针(元素)都具有指定的对齐方式。如果指针值不具有指定的对齐方式,则返回或传递毒化值。
align
属性应与noundef
属性结合使用,以确保指针已对齐,否则行为是未定义的。请注意,align 1
对非 byval、非 preallocated 参数没有影响。请注意,当与
byval
或preallocated
属性结合使用时,此属性具有额外的语义,这些语义已在其中记录。
noalias
这指示通过基于参数或返回值的指针值访问的内存位置,在函数执行期间,也不会通过不基于参数或返回值的指针值访问。此保证仅适用于在函数执行期间通过任何方式修改的内存位置。如果存在其他不基于参数或返回值的访问,则行为是未定义的。返回值上的属性也具有额外的语义,如下所述。调用者和被调用者共同承担确保满足这些要求的责任。有关更多详细信息,请参阅 别名分析中 NoAlias 响应的讨论。
请注意,
noalias
的定义与 C99 中函数参数的restrict
的定义有意相似。对于函数返回值,C99 的
restrict
没有实际意义,而 LLVM 的noalias
则有意义。此外,当noalias
属性用于函数返回值时,其语义比用于函数参数时更强。noalias
属性在函数返回值上表明,该函数的作用类似于系统内存分配函数,返回一个指向已分配存储的指针,该存储与调用者可访问的任何其他对象的存储不相交。captures(...)
这些属性限制了被调用者捕获指针的方式。 这不是返回值的有效属性。 此属性仅适用于此参数中传递的指针的特定副本。
captures
的参数是捕获的指针组件的列表,可以是none
,或以下各项的组合address
:指针的整数地址。address_is_null
(address
的子集):地址是否为空。provenance
:函数返回后访问指针进行读取和写入的能力。read_provenance
(provenance
的子集):函数返回后仅访问指针进行读取的能力。
此外,可以指定某些组件仅在某些位置捕获。目前仅支持返回值 (
ret
) 和其他(默认)位置。指针捕获部分 更详细地讨论了这些语义。
如何使用属性的一些示例
captures(none)
:指针未被捕获。captures(address, provenance)
:等同于省略该属性。captures(address)
:地址可能被捕获,但 provenance 不会。captures(address_is_null)
:仅捕获地址是否为空。captures(address, read_provenance)
:地址和 provenance 均被捕获,但仅用于只读访问。captures(ret: address, provenance)
:指针仅通过返回值捕获。captures(address_is_null, ret: address, provenance)
:整个指针通过返回值捕获,此外指针是否为空以其他方式捕获。
nofree
这表示被调用者不释放指针参数。 这不是返回值的有效属性。
nest
这表示可以使用 trampoline intrinsics 移除指针参数。 这不是返回值的有效属性,并且只能应用于一个参数。
returned
这表示函数始终将其参数作为其返回值返回。 这是对优化器和代码生成器的提示,用于在生成调用者时进行值传播、尾调用优化以及在某些情况下省略寄存器保存和恢复;在生成被调用者时,不会检查或强制执行此操作。 参数和函数返回类型必须是 bitcast 指令 的有效操作数。 这不是返回值的有效属性,并且只能应用于一个参数。
nonnull
这表示参数或返回指针不为空。 此属性只能应用于指针类型的参数。 LLVM 不会检查或强制执行此操作;如果参数或返回指针为空,则会返回或传递 poison value。
nonnull
属性应与noundef
属性结合使用,以确保指针不为空,否则行为是未定义的。dereferenceable(<n>)
这表示参数或返回指针是可解引用的。 此属性只能应用于指针类型的参数。 可解引用的指针可以推测性地加载,而不会有陷阱的风险。 已知可解引用的字节数必须在括号中提供。 字节数可以小于被指向类型的大小。
nonnull
属性并不意味着可解引用性(考虑指向数组末尾之后一个元素的指针),但是dereferenceable(<n>)
在addrspace(0)
(这是默认地址空间)中确实意味着nonnull
,除非存在null_pointer_is_valid
函数属性。n
应为正数。 指针应该是明确定义的,否则它是未定义的行为。 这意味着dereferenceable(<n>)
意味着noundef
。 当在 assume 操作数捆绑包中使用时,适用更严格的语义。 有关更多详细信息,请参阅 assume operand bundles。dereferenceable_or_null(<n>)
这表示参数或返回值不会同时是非空和不可解引用的(最多
<n>
字节)。 所有标记为dereferenceable_or_null(<n>)
的非空指针都是dereferenceable(<n>)
。 对于地址空间 0,dereferenceable_or_null(<n>)
意味着指针恰好是dereferenceable(<n>)
或null
之一,而在其他地址空间中,dereferenceable_or_null(<n>)
意味着指针至少是dereferenceable(<n>)
或null
之一(即,它可能同时是null
和dereferenceable(<n>)
)。 此属性只能应用于指针类型的参数。swiftself
这表示参数是 self/context 参数。 这不是返回值的有效属性,并且只能应用于一个参数。
swiftasync
这表示参数是异步上下文参数,并触发创建特定于目标的扩展帧记录以存储此指针。 这不是返回值的有效属性,并且只能应用于一个参数。
swifterror
此属性旨在建模和优化 Swift 错误处理。 它可以应用于指针到指针类型或指针大小的 alloca 的参数。 在调用站点,与
swifterror
参数对应的实际参数必须来自swifterror
alloca 或调用者的swifterror
参数。swifterror
值(参数或 alloca)只能加载和存储,或用作swifterror
参数。 这不是返回值的有效属性,并且只能应用于一个参数。这些约束允许调用约定通过在调用边界处将
swifterror
变量与特定寄存器关联而不是将它们放置在内存中来优化对它们的访问。 由于这确实更改了调用约定,因此对参数使用swifterror
属性的函数与不使用该属性的函数不 ABI 兼容。这些约束还允许 LLVM 假设
swifterror
参数不与函数内可见的任何其他内存别名,并且作为参数传递的swifterror
alloca 不会逃逸。immarg
这表示参数必须是立即值。 这必须是微不足道的立即整数或浮点常数。 Undef 或常量表达式无效。 这仅在 intrinsic 声明中有效,不能应用于调用站点或任意函数。
noundef
此属性适用于参数和返回值。 如果值表示包含任何未定义或 poison 位,则行为是未定义的。 请注意,这不涉及由类型的存储表示引入的填充。
如果启用了内存清理器,则
noundef
将成为 ABI 属性,并且必须在调用站点和函数定义之间匹配。
nofpclass(<test mask>)
此属性适用于具有浮点和浮点向量类型的参数和返回值,以及此类类型的 受支持聚合(与 fast-math flags 的受支持类型匹配)。 测试掩码的格式与 llvm.is.fpclass 的第二个参数相同,并指示不允许该值的哪些浮点值类。 例如,位掩码 3 表示参数不能是 NaN。
如果该值是
nofpclass
测试掩码指示的浮点类,则会传递或返回 poison value。
@llvm.is.fpclass(nofpclass(test_mask) %x, test_mask) => false
@llvm.is.fpclass(nofpclass(test_mask) %x, ~test_mask) => true
nofpclass(all) => poison
在文本 IR 中,支持各种字符串名称以提高可读性,并且可以组合使用。 例如,
nofpclass(nan pinf nzero)
的计算结果为掩码 547。这不依赖于浮点环境。 例如,标记为
nofpclass(zero)
的函数参数表示没有零输入。 如果这应用于标记为 “denormal-fp-math” 的函数中的参数,表示对输入非正规数的零处理,则不意味着该值不能是与 0 相等的非正规值。
名称 |
浮点类 |
位掩码值 |
---|---|---|
nan |
任何 NaN |
3 |
inf |
+/- 无穷大 |
516 |
norm |
+/- 正常 |
264 |
sub |
+/- 次正规 |
144 |
zero |
+/- 0 |
96 |
零 |
all |
1023 |
所有值 |
snan |
1 |
信令 NaN |
qnan |
2 |
安静 NaN |
ninf |
4 |
负无穷大 |
nnorm |
8 |
负正常 |
nsub |
16 |
负次正规 |
nzero |
32 |
负零 |
pzero |
64 |
正零 |
psub |
128 |
正次正规 |
pnorm |
256 |
正正常 |
pinf |
512 |
正无穷大
alignstack(<n>)
这表示后端在调用约定降低期间将此参数分配给堆栈槽时应考虑的对齐方式。 指定对齐方式的强制执行取决于目标,因为特定于目标的调用约定规则可能会覆盖此值。 此属性的目的是携带语言特定的对齐信息,该信息未映射到后端的基本类型(例如,通过语言属性进行的过度对齐规范)。
allocalign
标记有此属性的函数参数是此函数返回的新分配块的对齐方式(以字节为单位)。 返回的值必须具有指定的对齐方式,或者为空指针。 返回值可能比请求的对齐方式更对齐,但不能对齐得更少。 只要返回的指针为空,就允许无效的(例如,非 2 的幂)对齐方式。 此属性只能应用于整数参数。
allocptr
标记有此属性的函数参数是分配器将操作的指针。 对于类似 realloc 的函数,指针在成功后将失效(但可能会返回相同的地址),对于类似 free 的函数,指针将始终失效。
readnone
此属性表示函数不解引用该指针参数,即使它可能读取或写入指针指向的内存(如果通过其他指针访问)。
如果函数从 readnone 指针参数读取或写入,则行为是未定义的。
readonly
此属性表示函数不通过此指针参数写入,即使它可能写入指针指向的内存。
如果函数写入 readonly 指针参数,则行为是未定义的。
writeonly
此属性表示函数可以写入但不读取通过此指针参数(即使它可能从指针指向的内存中读取)。
此属性的理解方式与
memory(write)
属性相同。 也就是说,只要读取在函数外部不可观察,指针仍然可以被读取。 有关精确的语义,请参阅memory
文档。writable
此属性仅在与
dereferenceable(N)
或另一个暗示指针参数的前N
个字节是可解引用的属性结合使用时才有意义。在这种情况下,该属性表示前
N
个字节将在进入函数时被(非原子地)加载和存储回。这意味着可以在进入函数时引入虚假存储,而不会引入陷阱或数据竞争。 这不一定在整个函数中都成立,因为指针可能在函数执行期间逃逸到不同的线程。 另请参阅 原子优化指南
暗示可解引用性的“其他属性”是
dereferenceable_or_null
(如果指针非空)以及sret
、byval
、byref
、inalloca
、preallocated
系列属性。 请注意,并非所有这些组合都很有用,例如,即使没有此属性,也已知byval
参数是可写的。writable
属性不能与readnone
、readonly
或不包含argmem: write
的memory
属性组合使用。initializes((Lo1, Hi1), ...)
此属性表示函数初始化指针参数内存的范围,
[%p+LoN, %p+HiN)
。 内存初始化意味着第一个内存访问是非易失性的、非原子的写入。 写入必须在函数返回之前发生。 如果函数展开,则写入可能不会发生。此属性仅适用于通过此指针参数访问的内存。 允许通过其他指针任意访问同一内存。
writable
或dereferenceable
属性不暗示initializes
属性。initializes
属性不暗示writeonly
,因为initializes
允许在写入后从指针读取。此属性是恒定范围的列表,按升序排列,没有重叠或连续的列表元素。
LoN/HiN
是 64 位整数,并且允许负值,以防参数部分指向分配。在
byval
参数上,initializes
指的是被覆盖的被调用者副本的给定部分。byval
被调用者永远无法初始化传递给byval
参数的原始调用者内存。dead_on_unwind
在较高层面上,此属性表示如果调用展开,指针参数将失效,因为调用者将不依赖于内存的内容。 仅在展开路径上可见的存储可以被省略。
更准确地说,其行为就好像在函数执行期间通过指针写入的任何内存在展开时都被 poison 值覆盖一样。 这包括
writable
属性暗示的隐式写入写入的内存。 允许调用者访问受影响的内存,但所有未先进行存储的加载都将返回 poison。此属性不能应用于返回值。
range(<ty> <a>, <b>)
此属性表示参数或返回值的可能范围。 如果该值不在指定的范围内,则将其转换为 poison。 传递给
range
的参数具有以下属性类型必须与参数或返回值的标量类型匹配。
对
a,b
表示范围[a,b)
。a
和b
都是常量。允许范围环绕。
空范围使用
0,0
表示。
否则,不允许
a
和b
相等。此属性只能应用于具有整数或整数向量类型的参数或返回值。
垃圾回收策略名称¶
每个函数都可以指定一个垃圾回收策略名称,它只是一个字符串
define void @f() gc "name" { ... }
name 的支持值包括 内置于 LLVM 的策略 以及加载的插件提供的任何策略。 指定 GC 策略将导致编译器更改其输出,以支持命名的垃圾回收算法。 请注意,LLVM 本身不包含垃圾回收器,此功能仅限于生成可以与外部提供的回收器互操作的机器代码。
前缀数据¶
前缀数据是与函数关联的数据,代码生成器将在函数入口点之前立即发出该数据。 此功能的目的是允许前端将特定于语言的运行时元数据与特定函数关联,并通过函数指针使其可用,同时仍然允许调用函数指针。
要访问给定函数的数据,程序可以将函数指针位转换为指向常量类型的指针,并解引用索引 -1。 这意味着 IR 符号指向紧挨着前缀数据末尾的位置。 例如,以用单个 i32
注释的函数为例,
define void @f() prefix i32 123 { ... }
前缀数据可以引用为,
%a = getelementptr inbounds i32, ptr @f, i32 -1
%b = load i32, ptr %a
前缀数据的布局方式就好像它是前缀数据类型的全局变量的初始化程序一样。 将放置函数,以使前缀数据的开头对齐。 这意味着,如果前缀数据的大小不是对齐大小的倍数,则函数的入口点将不对齐。 如果需要函数入口点的对齐,则必须将填充添加到前缀数据。
函数可以具有前缀数据但没有主体。 这具有与 available_externally
链接类似的语义,因为优化器可以使用数据,但不会在目标文件中发出数据。
序言数据¶
prologue
属性允许在函数体之前插入任意代码(编码为字节)。 这可以用于启用函数热补丁和 instrumentation。
为了维护普通函数调用的语义,序言数据必须具有特定格式。 具体来说,它必须以字节序列开头,这些字节序列解码为机器指令序列,对于模块的目标有效,这些指令将控制权转移到紧随序言数据之后的点,而不执行任何其他可见操作。 这允许内联器和其他 pass 推理函数定义的语义,而无需推理序言数据。 显然,这使得序言数据的格式高度依赖于目标。
x86 架构的有效序言数据的简单示例是 i8 144
,它编码了 nop
指令
define void @f() prologue i8 144 { ... }
通常,序言数据可以通过编码相对分支指令来形成,该指令跳过元数据,如 x86_64 架构的有效序言数据的此示例所示,其中前两个字节编码 jmp .+10
%0 = type <{ i8, i8, ptr }>
define void @f() prologue %0 <{ i8 235, i8 8, ptr @md}> { ... }
函数可以具有序言数据但没有主体。 这具有与 available_externally
链接类似的语义,因为优化器可以使用数据,但不会在目标文件中发出数据。
人格化函数¶
personality
属性允许函数指定用于异常处理的函数。
属性组¶
属性组是 IR 中对象引用的属性组。 它们对于保持 .ll
文件可读性非常重要,因为许多函数将使用相同的属性集。 在对应于单个 .c
文件的 .ll
文件的退化情况下,单个属性组将捕获用于构建该文件的重要命令行标志。
属性组是模块级对象。 要使用属性组,对象引用属性组的 ID(例如 #37
)。 一个对象可以引用多个属性组。 在这种情况下,来自不同组的属性将被合并。
以下是一个属性组的示例,用于一个应始终内联的函数,其堆栈对齐为 4,并且不应使用 SSE 指令
; Target-independent attributes:
attributes #0 = { alwaysinline alignstack=4 }
; Target-dependent attributes:
attributes #1 = { "no-sse" }
; Function @f has attributes: alwaysinline, alignstack=4, and "no-sse".
define void @f() #0 #1 { ... }
函数属性¶
设置函数属性是为了传达有关函数的附加信息。 函数属性被认为是函数的一部分,而不是函数类型的一部分,因此具有不同函数属性的函数可以具有相同的函数类型。
函数属性是简单的关键字或字符串,它们遵循指定的类型。 如果需要多个属性,则用空格分隔。 例如
define void @f() noinline { ... }
define void @f() alwaysinline { ... }
define void @f() alwaysinline optsize { ... }
define void @f() optsize { ... }
define void @f() "no-sse" { ... }
正无穷大
此属性表示,在发出序言和尾声时,后端应强制对齐堆栈指针。 在括号中指定所需的对齐方式,该对齐方式必须是 2 的幂。
"alloc-family"="FAMILY"
这表示分配器函数所属的“family”。 为了避免冲突,family 名称应与主分配器函数的 mangled 名称匹配,即 malloc/calloc/realloc/free 的 “malloc”,
::operator::new
和::operator::delete
的 “_Znwm”,以及对齐的::operator::new
和::operator::delete
的 “_ZnwmSt11align_val_t”。 可以优化 family 中匹配的 malloc/realloc/free 调用,但不匹配的调用将保持不变。allockind("KIND")
描述分配函数的行为。 KIND 字符串包含逗号分隔的条目,来自以下选项
“alloc”:该函数返回一个新的内存块或 null。
“realloc”:该函数返回一个新的内存块或 null。 如果结果为非 null,则从块的开头到原始分配大小和新分配大小中较小者的内存内容将与
allocptr
参数的内容匹配,并且即使函数返回相同的地址,allocptr
参数也会失效。“free”:该函数释放
allocptr
指定的内存块。 标记为 “free”allockind
的函数必须返回 void。“uninitialized”:任何新分配的内存(来自 “alloc” 函数的新块或来自 “realloc” 函数的扩容)都将是未初始化的。
“zeroed”:任何新分配的内存(来自 “alloc” 函数的新块或来自 “realloc” 函数的扩容)都将被清零。
“aligned”:该函数返回根据
allocalign
参数对齐的内存。
前三个选项是互斥的,其余选项描述了函数行为的更多细节。 其余选项对于 “free” 类型函数无效。
allocsize(<EltSizeParam>[, <NumEltsParam>])
此属性表示带注释的函数将始终返回至少给定数量的字节(或 null)。 它的参数是零索引参数编号;如果提供一个参数,则假定返回的指针处至少有
CallSite.Args[EltSizeParam]
个字节可用。 如果提供两个参数,则假定CallSite.Args[EltSizeParam] * CallSite.Args[NumEltsParam]
个字节可用。 引用的参数必须是整数类型。 不对返回的内存块的内容做任何假设。alwaysinline
此属性表示内联器应尽可能尝试将此函数内联到调用者中,而忽略此调用者的任何活动内联大小阈值。
builtin
这表示在调用站点,被调用函数应被识别为内置函数,即使函数的声明使用了
nobuiltin
属性。 这仅在直接调用声明了nobuiltin
属性的函数的调用站点有效。cold
此属性表示此函数很少被调用。 在计算边权重时,由 cold 函数调用后支配的基本块也被认为是 cold 的;因此,给予低权重。
convergent
此属性表示此函数是收敛的。 当它出现在 call/invoke 上时,convergent 属性表示我们应将该调用视为我们正在调用收敛函数。 这在间接调用上尤其有用;没有它,我们可能会将此类调用视为目标是非收敛的。
有关更多详细信息,请参阅 收敛操作语义。
从不具有此属性的函数调用 llvm.experimental.convergence.entry 是错误的。
disable_sanitizer_instrumentation
当使用 sanitizers instrumentation 代码时,跳过某些函数以确保不对它们应用任何 instrumentation 可能很重要。
此属性并不总是类似于缺少的
sanitize_<name>
属性:根据特定的 sanitizer,可以将代码插入到函数中,而不管sanitize_<name>
属性如何,以防止误报。disable_sanitizer_instrumentation
禁用所有类型的插桩,其优先级高于sanitize_<name>
属性和其他编译器标志。"dontcall-error"
此属性表示当带有此属性的函数的调用未通过优化消除时,应发出错误诊断信息。前端可以提供可选的
srcloc
元数据节点在这些被调用者的调用点上,以附加有关此类调用在源语言中来自何处的信息。可以提供字符串值作为注释。"dontcall-warn"
此属性表示当带有此属性的函数的调用未通过优化消除时,应发出警告诊断信息。前端可以提供可选的
srcloc
元数据节点在这些被调用者的调用点上,以附加有关此类调用在源语言中来自何处的信息。可以提供字符串值作为注释。fn_ret_thunk_extern
此属性告知代码生成器,函数的返回应替换为跳转到外部定义的特定于架构的符号。对于 X86,此符号的标识符为
__x86_return_thunk
。"frame-pointer"
此属性告知代码生成器函数是否应保留帧指针。即使此属性表明可以消除帧指针,代码生成器也可能发出帧指针。允许的字符串值为
"none"
(默认) - 可以消除帧指针,并且其寄存器可以用于其他目的。"reserved"
- 帧指针寄存器必须更新以指向当前函数的有效帧记录,或者不被修改。"non-leaf"
- 如果函数调用其他函数,则应保留帧指针。"all"
- 应保留帧指针。
hot
此属性指示此函数是程序执行的热点。该函数将被更积极地优化,并将被放置在文本段的特殊子段中以提高局部性。
当启用配置文件反馈时,此属性优先于配置文件信息。通过将函数标记为
hot
,用户可以解决训练输入在所有热函数上没有良好覆盖率的情况。inlinehint
此属性指示源代码包含内联此函数是可取的提示(例如 C/C++ 中的 “inline” 关键字)。这只是一个提示;它对内联器没有要求。
jumptable
此属性指示应在代码生成时将该函数添加到跳转指令表,并且对该函数的所有取址引用都应替换为对相应跳转指令表函数指针的引用。请注意,这为原始函数创建了一个新的指针,这意味着依赖于函数指针标识的代码可能会中断。因此,任何使用
jumptable
注释的函数也必须是unnamed_addr
。memory(...)
此属性指定调用点或函数可能产生的内存影响。它允许为可能的内存位置类型(
argmem
、inaccessiblemem
、errnomem
以及默认类型)指定可能的访问类型(none
、read
、write
或readwrite
)。通过示例可以最好地理解它memory(none)
: 不访问任何内存。memory(read)
: 可能读取(但不写入)任何内存。memory(write)
: 可能写入(但不读取)任何内存。memory(readwrite)
: 可能读取或写入任何内存。memory(argmem: read)
: 可能仅读取参数内存。memory(argmem: read, inaccessiblemem: write)
: 可能仅读取参数内存,并且仅写入不可访问的内存。memory(argmem: read, errnomem: write)
: 可能仅读取参数内存,并且仅写入 errno。memory(read, argmem: readwrite)
: 可能读取任何内存(默认模式),并额外写入参数内存。memory(readwrite, argmem: none)
: 可能访问除参数内存之外的任何内存。
支持的访问类型有
readwrite
: 允许对位置进行任何类型的访问。read
: 位置仅被读取。写入位置会立即导致未定义行为。这包括从位置读取然后将相同的值写回的情况。write
: 只有对位置的写入在函数调用外部是可观察的。但是,函数仍然可以在内部读取写入后的位置,因为这是不可观察的。在写入位置之前读取位置会导致产生 poison 值。none
: 在函数外部观察不到对位置的读取或写入。即使使用memory(none)
,读取和写入 allocas 以及读取全局常量始终是有效的,因为这些影响在外部是不可观察的。
支持的内存位置类型有
argmem
: 这指的是基于函数指针参数的访问。inaccessiblemem
: 这指的是对当前模块不可访问的内存的访问(在函数返回之前 - 分配器函数可能返回新可访问的内存,同时仅访问不可访问的内存本身)。不可访问的内存通常用于建模内在函数的控制依赖性。errnomem
: 这指的是对errno
变量的访问。默认访问类型(在没有位置前缀的情况下指定)适用于所有未明确指定的位置,包括当前没有专用位置类型的位置(例如,对全局变量或捕获的指针的访问)。
如果未指定
memory
属性,则意味着memory(readwrite)
(所有内存影响都是可能的)。调用的内存影响可以计算为
CallSiteEffects & (FunctionEffects | OperandBundleEffects)
。因此,调用点注释优先于函数注释或操作数束描述的潜在影响。minsize
此属性建议优化pass和代码生成器pass做出选择,以尽可能保持此函数的代码大小较小,并执行可能牺牲运行时性能以最小化生成的代码大小的优化。此属性与
optdebug
和optnone
属性不兼容。naked
此属性禁用函数的序言/尾声发出。这可能会产生非常特定于系统的后果。
naked
函数的参数不能通过 IR 值引用。"no-inline-line-tables"
当此属性设置为 true 时,内联器在内联代码时会丢弃源位置,而是使用调用点的源位置。在内联到当前函数中的代码上设置的断点在执行内联调用点期间不会触发。如果调试器在内联调用点内停止,它将显示在最外层的内联调用点处停止。
no-jump-tables
当此属性设置为 true 时,从 switch case 降低生成的跳转表和查找表将被禁用。
nobuiltin
这表示调用点的被调用函数未被识别为内置函数。除非调用点使用
builtin
属性,否则 LLVM 将保留原始调用,并且不会根据内置函数的语义将其替换为等效代码。这在调用点以及函数声明和定义中都有效。nocallback
此属性指示函数仅允许通过返回或异常跳转回调用者模块,并且不允许通过调用回调函数、直接的(可能是传递的)外部函数调用、使用
longjmp
或其他方式跳转返回。这是一个编译器提示,在模块级别用于改进数据流分析,在链接期间被丢弃,并且对当前模块中定义的函数没有影响。nodivergencesource
对此函数的调用不是发散的来源。在一致性分析中,发散的来源 是指即使其输入是一致的也会产生发散的指令。没有进一步信息的调用通常被认为是发散的来源;在函数上设置此属性意味着对其的调用不是发散的来源。
noduplicate
此属性指示不能复制对函数的调用。对
noduplicate
函数的调用可以在其父函数中移动,但不能在其父函数中复制。包含
noduplicate
调用的函数仍然可以是内联候选者,前提是该调用不会因内联而复制。这意味着该函数具有内部链接,并且只有一个调用点,因此在内联之后原始调用将变为死代码。nofree
此函数属性指示该函数不会直接或间接地调用内存释放函数(例如
free
)来释放调用之前已存在的内存分配。因此,已知在调用具有
nofree
属性的函数之前可解引用的未捕获指针在调用之后仍然已知是可解引用的。在函数可能将指针传递给另一个线程(然后该线程释放内存)的环境中,捕获条件是必要的。或者,nosync
将确保不会发生此类通信,即使是捕获的指针也不会被函数释放。nofree
函数被明确允许释放它分配的内存,或者(如果不是nosync
)安排另一个线程代表它释放内存。因此,也许令人惊讶的是,nofree
函数可以返回指向先前已释放的 已分配对象 的指针。noimplicitfloat
禁止隐式浮点代码。这会抑制使用浮点代码和浮点寄存器进行名义上不是浮点运算的操作的优化。执行浮点运算或需要访问浮点寄存器的 LLVM 指令仍可能导致生成浮点代码。
还会抑制从标量代码创建 SIMD/向量代码和寄存器的优化,例如向量化或 memcpy/memset 优化。这包括整数向量。IR 中存在的向量指令仍可能导致生成向量代码。
noinline
此属性指示内联器在任何情况下都不应内联此函数。此属性不得与
alwaysinline
属性一起使用。nomerge
此属性指示在优化期间永远不应合并对此函数的调用。例如,它可以防止尾部合并,否则会合并引发异常或终止程序的相同代码序列。尾部合并通常会降低源位置信息的精度,从而使堆栈跟踪在调试时不太有用。此属性使用户可以控制代码大小和调试信息精度之间的权衡。
nonlazybind
此属性禁止函数的惰性符号绑定。如果函数在程序启动期间未被调用,这可能会使对函数的调用更快,但会以额外的程序启动时间为代价。
noprofile
此函数属性阻止基于插桩的性能分析(用于覆盖率或基于配置文件的优化)添加到函数中。如果调用者和被调用者具有此属性的不同值,它也会阻止内联。
skipprofile
此函数属性阻止基于插桩的性能分析(用于覆盖率或基于配置文件的优化)添加到函数中。此属性不限制内联,因此插桩指令可能会最终出现在此函数中。
noredzone
此属性指示代码生成器不应使用红色区域,即使特定于目标的 ABI 通常允许这样做。
indirect-tls-seg-refs
此属性指示代码生成器不应通过段寄存器使用直接 TLS 访问,即使特定于目标的 ABI 通常允许这样做。
noreturn
此函数属性指示该函数永远不会正常返回,因此不会通过返回指令返回。如果函数确实动态返回,则会在运行时产生未定义的行为。带注释的函数仍然可能引发异常,即,不隐含
nounwind
。norecurse
此函数属性指示该函数不会直接或间接地在任何可能的调用路径中调用自身。如果函数确实递归调用,则会在运行时产生未定义的行为。
willreturn
此函数属性指示对此函数的调用要么会表现出未定义的行为,要么会返回并继续在现有调用堆栈中的某个点(包括当前调用)执行。带注释的函数仍然可能引发异常,即,不隐含
nounwind
。如果带注释的函数的调用没有将控制权返回到调用堆栈中的某个点,则行为未定义。nosync
此函数属性指示该函数不通过内存或其他明确定义的方式与另一个线程通信(同步)。在存在强制执行顺序的 atomic 访问(因此不是“unordered”和“monotonic”)、volatile 访问以及 convergent 函数调用的情况下,认为同步是可能的。
请注意,convergent 操作可能涉及不通过内存进行的通信,并且对于内存模型的目的而言,不一定暗示线程之间的顺序。因此,操作可以是 convergent 和 nosync 两者。
如果 nosync 函数确实与另一个线程同步,则行为未定义。
nounwind
此函数属性指示该函数永远不会引发异常。如果函数确实引发异常,则其运行时行为是未定义的。但是,标记为 nounwind 的函数仍然可能陷入陷阱或生成异步异常。LLVM 识别为处理异步异常的异常处理方案(例如 SEH)仍将提供其实现定义的语义。
nosanitize_bounds
此属性指示为此函数禁用边界检查 sanitizer 插桩。
nosanitize_coverage
此属性指示为此函数禁用 SanitizerCoverage 插桩。
null_pointer_is_valid
如果设置了
null_pointer_is_valid
,则地址空间 0 中的null
地址被认为是内存加载和存储的有效地址。任何分析或优化都不应将在此函数中解引用指向null
的指针视为未定义的行为。注意:由于在常量表达式内部查询此属性的限制,将全局变量的地址与null
进行比较仍可能评估为 false。optdebug
此属性建议优化pass和代码生成器pass应做出选择,以尝试保留调试信息,而不会显着降低运行时性能。此属性与
minsize
、optsize
和optnone
属性不兼容。optforfuzzing
此属性指示应针对最大模糊测试信号优化此函数。
optnone
此函数属性指示大多数优化pass将跳过此函数,但过程间优化pass除外。代码生成默认为 “fast” 指令选择器。此属性不能与
alwaysinline
属性一起使用;此属性还与minsize
、optsize
和optdebug
属性不兼容。此属性要求在函数上也指定
noinline
属性,因此该函数永远不会内联到任何调用者中。只有具有alwaysinline
属性的函数才是有资格内联到此函数体中的候选者。optsize
此属性建议优化pass和代码生成器pass做出选择,以保持此函数的代码大小较小,并在其他方面专门执行优化以减小代码大小,只要它们不会显着影响运行时性能。此属性与
optdebug
和optnone
属性不兼容。"patchable-function"
此属性告知代码生成器,为此函数生成的代码需要遵循某些约定,以便运行时函数稍后可以对其进行修补。此属性的确切效果取决于其字符串值,目前只有一个合法的可能性
"prologue-short-redirect"
- 这种样式的可修补函数旨在支持修补函数序言,以便以线程安全的方式将控制权从函数重定向出去。它保证函数的第一个指令足够大,可以容纳短跳转指令,并且具有足够的对齐方式,允许通过原子比较和交换指令完全更改。虽然可以通过插入足够大的 NOP 来满足第一个要求,但 LLVM 可以并且将尝试将现有指令(即无论如何都必须发出的指令)重新用作大于短跳转的可修补指令。"prologue-short-redirect"
目前仅在 x86-64 上受支持。
此属性本身并不暗示对过程间优化的限制。修补可能产生的所有语义效果都必须通过链接类型单独传达。
"probe-stack"
此属性指示函数将在堆栈末尾触发保护区域。它确保对堆栈的访问必须不超过保护区域的大小到堆栈的先前访问。它需要一个必需的字符串值,即将被调用的堆栈探测函数的名称。
如果具有
"probe-stack"
属性的函数内联到具有另一个"probe-stack"
属性的函数中,则结果函数具有调用者的"probe-stack"
属性。如果具有"probe-stack"
属性的函数内联到根本没有"probe-stack"
属性的函数中,则结果函数具有被调用者的"probe-stack"
属性。"stack-probe-size"
此属性控制堆栈探测的行为:
"probe-stack"
属性或 ABI 要求的堆栈探测(如果有)。它定义了保护区域的大小。它确保如果函数可能使用的堆栈空间大于保护区域的大小,则将发出堆栈探测序列。它需要一个必需的整数值,默认值为 4096。如果具有
"stack-probe-size"
属性的函数内联到具有另一个"stack-probe-size"
属性的函数中,则结果函数具有数值较小的"stack-probe-size"
属性。如果具有"stack-probe-size"
属性的函数内联到根本没有"stack-probe-size"
属性的函数中,则结果函数具有被调用者的"stack-probe-size"
属性。"no-stack-arg-probe"
此属性禁用 ABI 要求的堆栈探测(如果有)。
returns_twice
此属性指示此函数可以返回两次。C 语言的
setjmp
就是这样一个函数的例子。编译器在这些函数的调用者中禁用了一些优化(例如尾调用)。safestack
此属性指示为此函数启用了 SafeStack 保护。
如果具有
safestack
属性的函数内联到不具有safestack
属性或具有ssp
、sspstrong
或sspreq
属性的函数中,则结果函数将具有safestack
属性。sanitize_address
此属性指示为此函数启用了 AddressSanitizer 检查(动态地址安全分析)。
sanitize_memory
此属性指示为此函数启用了 MemorySanitizer 检查(动态检测对未初始化内存的访问)。
sanitize_thread
此属性指示为此函数启用了 ThreadSanitizer 检查(动态线程安全分析)。
sanitize_hwaddress
此属性指示为此函数启用了 HWAddressSanitizer 检查(基于标记指针的动态地址安全分析)。
sanitize_memtag
此属性指示为此函数启用了 MemTagSanitizer 检查(基于 Armv8 MTE 的动态地址安全分析)。
sanitize_realtime
此属性指示为此函数启用了 RealtimeSanitizer 检查(实时安全分析 - 无分配、系统调用或异常)。
sanitize_realtime_blocking
此属性指示如果在使用
sanitize_realtime
属性注释的函数的调用期间调用了具有此属性的函数,则 RealtimeSanitizer 应立即报错。此属性与sanitize_realtime
属性不兼容。speculative_load_hardening
此属性指示应为函数体启用 推测加载强化。
推测加载强化是对利用控制流错误推测(特别是分支是否被采纳的错误推测)的信息泄漏攻击的最佳努力缓解措施。通常,启用此类攻击的漏洞被归类为 “Spectre variant #1”。值得注意的是,这不尝试缓解分支目标错误推测,分支目标错误推测被归类为 “Spectre variant #2” 漏洞。
内联时,此属性是粘性的。内联带有此属性的函数将导致调用者获得该属性。这旨在提供一个最大程度保守的模型,在该模型中,使用此属性注释的函数中的代码将始终(即使在内联之后)最终得到强化。
speculatable
此函数属性指示该函数除了计算其结果外没有任何其他影响,并且没有未定义的行为。请注意,
speculatable
不足以得出结论,沿着任何特定的执行路径,对此函数的调用次数在外部是不可观察的。此属性仅对函数和声明有效,对单个调用点无效。如果函数被错误地标记为 speculatable 并且确实表现出未定义的行为,即使调用点是死代码,也可能会观察到未定义的行为。ssp
此属性指示函数应发出堆栈粉碎保护器。它以 “canary”(金丝雀)的形式出现 — 一个随机值在局部变量之前的堆栈上,并在函数返回时进行检查,以查看它是否已被覆盖。使用启发式方法来确定函数是否需要堆栈保护器。使用的启发式方法将为具有以下特征的函数启用保护器:
大于
ssp-buffer-size
(默认值为 8)的字符数组。包含大于
ssp-buffer-size
的字符数组的聚合。对 alloca() 的调用,具有可变大小或大于
ssp-buffer-size
的常量大小。
被标识为需要保护器的变量将排列在堆栈上,使其与堆栈保护器 guard 相邻。
如果具有
ssp
属性的函数内联到调用函数中,则该属性不会传递到调用函数。sspstrong
此属性指示函数应发出堆栈粉碎保护器。此属性会导致在使用强启发式方法来确定函数是否需要堆栈保护器时使用。强启发式方法将为具有以下特征的函数启用保护器:
任何大小和类型的数组
包含任何大小和类型的数组的聚合。
对 alloca() 的调用。
已获取其地址的局部变量。
被标识为需要保护器的变量将排列在堆栈上,使其与堆栈保护器 guard 相邻。具体的布局规则是
大型数组和包含大型数组(
>= ssp-buffer-size
)的结构最靠近堆栈保护器。小型数组和包含小型数组(
< ssp-buffer-size
)的结构是第二靠近保护器。已获取其地址的变量是第三靠近保护器。
这会覆盖
ssp
函数属性。如果一个带有
sspstrong
属性的函数被内联到一个带有ssp
属性的调用函数中,那么调用函数的属性将被升级为sspstrong
。sspreq
此属性表示函数应始终发出堆栈粉碎保护器。这将覆盖
ssp
和sspstrong
函数属性。被标识为需要保护器的变量将排列在堆栈上,使其与堆栈保护器 guard 相邻。具体的布局规则是
大型数组和包含大型数组(
>= ssp-buffer-size
)的结构最靠近堆栈保护器。小型数组和包含小型数组(
< ssp-buffer-size
)的结构是第二靠近保护器。已获取其地址的变量是第三靠近保护器。
如果一个带有
sspreq
属性的函数被内联到一个带有ssp
或sspstrong
属性的调用函数中,那么调用函数的属性将被升级为sspreq
。
strictfp
此属性表示该函数是从需要严格浮点语义的范围调用的。LLVM 不会尝试任何需要假设浮点舍入模式或可能更改浮点状态标志状态的优化,否则调用此函数可能会设置或清除这些标志。LLVM 不会引入任何可能陷入陷阱的新浮点指令。
"denormal-fp-math"
这表示可以为默认浮点环境假定的非正规化(次正规)处理。这是一个逗号分隔的对。元素可以是
"ieee"
、"preserve-sign"
、"positive-zero"
或"dynamic"
之一。第一个条目表示浮点运算结果的刷新模式。第二个条目表示对浮点指令的非正规化输入的处理。为了与旧的 bitcode 兼容,如果省略第二个值,则输入和输出模式都将假定为相同的模式。如果未指定此属性,则默认值为
"ieee,ieee"
。如果输出模式为
"preserve-sign"
或"positive-zero"
,则非正规化输出可能会被标准浮点运算刷新为零。不强制要求刷新为零,但如果非正规化输出被刷新为零,则必须遵守符号模式。并非所有目标都支持所有模式。如果模式为
"dynamic"
,则行为源自浮点环境的动态状态。不应执行依赖于非正规化值行为的转换。虽然这表示函数将要执行的预期浮点模式,但这并不尝试确保模式一致。用户或平台代码应在函数入口之前适当设置浮点模式。
如果输入模式为
"preserve-sign"
或"positive-zero"
,则浮点运算必须将任何输入非正规化值视为零。在某些情况下,如果指令不遵守此模式,则可能需要将输入转换为 0,就像在降低精度的过程中使用@llvm.canonicalize
一样,以确保正确性。"denormal-fp-math-f32"
与
"denormal-fp-math"
相同,但仅控制 32 位浮点类型(或 32 位浮点向量)的行为。如果两者都存在,则此属性将覆盖"denormal-fp-math"
。并非所有目标都支持为每种类型单独设置非正规化模式,并且不会尝试诊断不支持的用法。目前,AMDGPU 和 NVPTX 后端支持此属性。"thunk"
此属性表示该函数将使用尾调用委托给其他函数。thunk 的原型不应用于优化目的。调用者应将 thunk 原型强制转换为与 thunk 目标原型匹配。
uwtable[(sync|async)]
此属性表示目标 ABI 需要为此函数生成展开表条目,即使我们可以证明没有异常通过它。这通常是 ELF x86-64 abi 的情况,但可以为某些编译单元禁用它。可选参数描述要生成哪种类型的展开表:
sync
用于正常展开表,async
用于异步(指令精确)展开表。如果没有参数,则属性uwtable
等效于uwtable(async)
。nocf_check
此属性表示不会对具有属性的实体执行控制流检查。它为特定实体禁用 -fcf-protection=<>,以细粒度地控制硬件控制流保护机制。该标志是目标独立的,目前适用于函数或函数指针。
shadowcallstack
此属性表示为该函数启用了 ShadowCallStack 检查。该检测会检查函数的返回地址在函数序言和尾声之间是否已更改。目前它是 x86_64 特定的。
mustprogress
此属性表示函数必须返回、展开或以可观察的方式与环境交互,例如通过易失性内存访问、I/O 或其他同步。
mustprogress
属性旨在模拟 C++ 标准 [intro.progress] 第一节的要求。因此,如果一个带有mustprogress
属性的函数中的循环不以可观察的方式与环境交互,则可以假定该循环终止,并且可以删除没有副作用的终止循环。如果mustprogress
函数不满足此约定,则行为是未定义的。如果mustprogress
函数调用未标记为mustprogress
的函数,并且该函数永远不返回,则程序是良好定义的,即使没有任何其他可观察的进度。请注意,willreturn
意味着mustprogress
。"warn-stack-size"="<threshold>"
此属性设置一个阈值,一旦帧大小已知,如果帧大小超过指定值,则发出诊断信息。它接受一个必需的整数值,该值应为非负整数,且小于 UINT_MAX。当具有不同值的重复定义链接在一起时,将使用哪个阈值是未指定的。
vscale_range(<min>[, <max>])
此函数属性表示 vscale 是指定范围内的 2 的幂。min 必须是大于 0 的 2 的幂。指定 max 时,max 必须是大于或等于 min 的 2 的幂,或者为 0 以表示无界最大值。语法 vscale_range(<val>) 可用于将 min 和 max 都设置为相同的值。不包含此属性的函数不对 vscale 的值做任何假设。
"nooutline"
此属性表示 outlining 过程不应修改该函数。
调用点属性¶
除了函数属性之外,还支持以下仅调用点属性
vector-function-abi-variant
此属性可以附加到 call 以列出与该函数关联的向量函数。请注意,该属性不能附加到 invoke 或 callbr 指令。该属性由逗号分隔的 mangled 名称列表组成。列表的顺序不暗示偏好(逻辑上是一个集合)。编译器可以自由选择其选择的任何列出的向量函数。
mangled 名称的语法如下:
_ZGV<isa><mask><vlen><parameters>_<scalar_name>[(<vector_redirection>)]
当存在时,该属性通知编译器函数
<scalar_name>
具有相应的向量变体,该变体可用于对向量执行<scalar_name>
的并发调用。向量函数的形状由前缀_ZGV
和<scalar_name>
标记之间的标记描述。向量函数的标准名称为_ZGV<isa><mask><vlen><parameters>_<scalar_name>
。当存在时,可选标记(<vector_redirection>)
通知编译器除了标准名称之外还提供了自定义名称(例如,可以通过在 OpenMP 5.0 中使用declare variant
来提供自定义名称)。变体的声明必须存在于 IR 模块中。向量变体的签名由目标的向量函数 ABI (VFABI) 规范的规则确定。对于 Arm 和 X86,VFABI 可以在 https://github.com/ARM-software/abi-aa 和 https://software.intel.com/content/www/us/en/develop/download/vector-simd-function-abi.html 中找到。对于 X86 和 Arm 目标,标准名称中标记的值是在 VFABI 中定义的那些值。LLVM 具有一个内部
<isa>
标记,可用于为未直接与任何目标 ISA 关联的函数创建标量到向量的映射(例如,TargetLibraryInfo 中存储的一些映射)。<isa>
标记的有效值包括:<isa>:= b | c | d | e -> X86 SSE, AVX, AVX2, AVX512 | n | s -> Armv8 Advanced SIMD, SVE | __LLVM__ -> Internal LLVM Vector ISA
对于当前支持的所有目标(x86、Arm 和内部 LLVM),其余标记可以具有以下值:
<mask>:= M | N -> mask | no mask <vlen>:= number -> number of lanes | x -> VLA (Vector Length Agnostic) <parameters>:= v -> vector | l | l <number> -> linear | R | R <number> -> linear with ref modifier | L | L <number> -> linear with val modifier | U | U <number> -> linear with uval modifier | ls <pos> -> runtime linear | Rs <pos> -> runtime linear with ref modifier | Ls <pos> -> runtime linear with val modifier | Us <pos> -> runtime linear with uval modifier | u -> uniform <scalar_name>:= name of the scalar function <vector_redirection>:= optional, custom name of the vector function
preallocated(<ty>)
此属性是调用
llvm.call.preallocated.arg
所必需的,并且不能用于任何其他调用。有关更多详细信息,请参阅 llvm.call.preallocated.arg。
全局属性¶
可以设置属性以传达有关全局变量的附加信息。与 函数属性 不同,全局变量上的属性被分组到一个 属性组 中。
no_sanitize_address
此属性表示全局变量不应应用 AddressSanitizer 检测,因为它已使用 __attribute__((no_sanitize(“address”)))、__attribute__((disable_sanitizer_instrumentation)) 注释,或包含在 -fsanitize-ignorelist 文件中。
no_sanitize_hwaddress
此属性表示全局变量不应应用 HWAddressSanitizer 检测,因为它已使用 __attribute__((no_sanitize(“hwaddress”)))、__attribute__((disable_sanitizer_instrumentation)) 注释,或包含在 -fsanitize-ignorelist 文件中。
sanitize_memtag
此属性表示全局变量应应用 AArch64 内存标签 (MTE) 检测。此属性会导致抑制某些优化,例如 GlobalMerge,并确保在汇编中发出额外的指令,并在目标文件中放置额外的元数据,以便链接器可以确保访问受到 MTE 的保护。当提供 -fsanitize=memtag-globals 时,只要全局变量未标记为 __attribute__((no_sanitize(“memtag”)))、__attribute__((disable_sanitizer_instrumentation)) 或包含在 -fsanitize-ignorelist 文件中,clang 就会添加此属性。当无法标记全局变量时(例如,它是 TLS 变量),AArch64 Globals Tagging 过程可能会删除此属性。
sanitize_address_dyninit
此属性表示当使用 AddressSanitizer 检测时,应检查全局变量是否存在 ODR 违规。此属性应用于根据 C++ 规则动态初始化的全局变量。
操作数束¶
操作数束是标记的 SSA 值集或元数据字符串集,可以与某些 LLVM 指令(目前仅限 call
和 invoke
)关联。在某种程度上,它们类似于元数据,但删除它们是不正确的,并且会更改程序语义。
语法
operand bundle set ::= '[' operand bundle (, operand bundle )* ']'
operand bundle ::= tag '(' [ bundle operand ] (, bundle operand )* ')'
bundle operand ::= SSA value | metadata string
tag ::= string constant
操作数束不是函数签名的一部分,并且给定的函数可能会从多个位置调用,并带有不同类型的操作数束。这反映了一个事实,即操作数束在概念上是 call
(或 invoke
)的一部分,而不是被调度的被调用者的一部分。
操作数束是一种通用机制,旨在支持托管语言的运行时自省类功能。虽然操作数束的确切语义取决于束标签,但操作数束的存在对程序语义的影响程度存在某些限制。这些限制被描述为“未知”操作数束的语义。只要操作数束的行为可以在这些限制范围内描述,LLVM 就不需要对操作数束有特殊的了解,以避免错误编译包含它的程序。
未知操作数束的束操作数在控制权转移到被调用者或被调用对象之前以未知方式逃逸。
带有操作数束的调用和 invoke 在入口和出口处对堆具有未知的读/写效果(即使调用目标指定了
memory
属性),除非它们被调用点特定的属性覆盖。调用点的操作数束不能更改被调用函数的实现。过程间优化像往常一样工作,只要它们考虑到前两个属性即可。
下面描述了更具体类型的操作数束。
反优化操作数束¶
反优化操作数束以 "deopt"
操作数束标签为特征。这些操作数束表示附加它们的调用点的备用“安全”延续,并且可以由合适的运行时使用,以在指定的调用点反优化编译的帧。一个调用点最多可以附加一个 "deopt"
操作数束。反优化的确切细节超出了语言参考的范围,但它通常涉及将编译的帧重写为一组解释的帧。
从编译器的角度来看,反优化操作数束使它们附加到的调用点至少是 readonly
的。它们读取其所有指针类型操作数(即使它们没有以其他方式逃逸)和整个可见堆。反优化操作数束不会捕获其操作数,除非在反优化期间,在这种情况下,控制权将不会返回到编译的帧。
内联器知道如何内联通过具有反优化操作数束的调用。就像内联通过正常的调用点涉及组合正常和异常延续一样,内联通过具有反优化操作数束的调用点需要适当地组合“安全”的反优化延续。内联器通过将父级的反优化延续添加到内联主体中的每个反优化延续来做到这一点。例如,在以下示例中将 @f
内联到 @g
中
define void @f() {
call void @x() ;; no deopt state
call void @y() [ "deopt"(i32 10) ]
call void @y() [ "deopt"(i32 10), "unknown"(ptr null) ]
ret void
}
define void @g() {
call void @f() [ "deopt"(i32 20) ]
ret void
}
将导致
define void @g() {
call void @x() ;; still no deopt state
call void @y() [ "deopt"(i32 20, i32 10) ]
call void @y() [ "deopt"(i32 20, i32 10), "unknown"(ptr null) ]
ret void
}
前端的责任是以一种语法方式构造或编码反优化状态,即将调用者的反优化状态添加到被调用者的反优化状态在语义上等同于在被调用者的反优化延续之后组合调用者的反优化延续。
Funclet 操作数束¶
Funclet 操作数束以 "funclet"
操作数束标签为特征。这些操作数束指示调用点位于特定的 funclet 内。一个调用点最多可以附加一个 "funclet"
操作数束,并且它必须只有一个束操作数。
如果任何 funclet EH pad 已“进入”但未“退出”(根据 EH 文档中的描述),则执行 call
或 invoke
是未定义的行为,其中
没有
"funclet"
束且不是对 nounwind intrinsic 的call
,或者具有
"funclet"
束,其操作数不是最近进入的尚未退出的 funclet EH pad。
类似地,如果没有已进入但尚未退出的 funclet EH pad,则执行带有 "funclet"
束的 call
或 invoke
是未定义的行为。
GC 转换操作数束¶
GC 转换操作数束以 "gc-transition"
操作数束标签为特征。这些操作数束将调用标记为从具有一种 GC 策略的函数到具有不同 GC 策略的函数的转换。如果协调 GC 策略之间的转换需要在调用点生成额外的代码,则这些束可能包含生成的代码所需的任何值。有关更多详细信息,请参阅 GC 转换。
该束包含需要传递给 GC 转换代码的任意值列表。它们将被降低精度并作为操作数传递给选择 DAG 中的适当 GC_TRANSITION 节点。假定这些参数必须在被调用者的执行之前和之后(但不一定在执行期间)可用。
Assume 操作数束¶
在 llvm.assume 上的操作数束允许表示假设,例如 参数属性 或 函数属性 在特定位置对特定值成立。操作数束启用了一些假设,这些假设很难或不可能表示为 llvm.assume 的布尔参数。
Assume 操作数束具有以下形式
"<tag>"([ <arguments>] ])
在函数或参数属性的情况下,操作数束具有受限形式
"<tag>"([ <holds for value> [, <attribute argument>] ])
操作数束的标签通常是可以假定成立的属性的名称。它也可以是 ignore,此标签不包含任何信息,应被忽略。
如果存在,则第一个参数是属性成立的值。
如果存在,则第二个参数是属性的参数。
如果没有参数,则该属性是调用位置的属性。
例如
call void @llvm.assume(i1 true) ["align"(ptr %val, i32 8)]
允许优化器假设在调用 llvm.assume 的位置,%val
的对齐至少为 8。
call void @llvm.assume(i1 %cond) ["cold"(), "nonnull"(ptr %val)]
允许优化器假设 llvm.assume 调用位置是冷的,并且 %val
可能不为空。
就像 llvm.assume 的参数一样,如果在运行时违反了提供的任何保证,则行为是未定义的。
虽然属性期望常量参数,但 assume 操作数束可以提供动态值,例如
call void @llvm.assume(i1 true) ["align"(ptr %val, i32 %align)]
如果操作数束值违反了属性值上的任何要求,则行为是未定义的,除非以下异常之一适用
"align"
操作数束可以指定非 2 的幂的对齐方式(包括零对齐)。如果是这种情况,则指针值必须为空指针,否则行为是未定义的。dereferenceable(<n>)
操作数束仅保证指针在假设点是可解引用的。指针在稍后的指针处可能不可解引用,例如,因为它可能已被释放。
除了允许操作数束编码函数和参数属性之外,assume 操作数束还可以编码 separate_storage
操作数束。这具有以下形式
separate_storage(<val1>, <val2>)``
这表示没有基于其一个参数的 指针 可以与基于另一个参数的任何指针别名。
即使可以像 nonnull
一样将假定的属性编码为布尔值,使用操作数束来表示属性仍然可以带来好处
可以通过操作数束表达的属性直接是优化器使用和关心的属性。将属性编码为操作数束消除了对表示属性的指令序列的需求(例如,icmp ne ptr %p, null 对于 nonnull)以及优化器从该指令序列推断属性的需求。
使用操作数束表达属性使得容易识别值在 llvm.assume 中的使用。然后,这简化并改进了启发式方法,例如,用于“使用敏感”优化。
预分配操作数束¶
预分配操作数束以 "preallocated"
操作数束标签为特征。这些操作数束允许将调用参数内存的分配与调用点分离。这对于以与某些目标上的 MSVC 兼容的方式按值传递非平凡可复制对象是必要的。一个调用点最多可以附加一个 "preallocated"
操作数束,并且它必须只有一个束操作数,该操作数是由 @llvm.call.preallocated.setup
生成的令牌。带有此操作数束的调用不应在进入函数之前调整堆栈,因为这将由 @llvm.call.preallocated.*
intrinsic 之一完成。
%foo = type { i64, i32 }
...
%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call ptr @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%foo)
; initialize %b
call void @bar(i32 42, ptr preallocated(%foo) %a) ["preallocated"(token %t)]
GC Live 操作数束¶
“gc-live”操作数束仅在 gc.statepoint intrinsic 上有效。操作数束必须包含指向垃圾回收对象的每个指针,这些指针可能需要由垃圾回收器更新。
降低精度后,任何重定位的值都将记录在相应的 stackmap 条目 中。有关更多详细信息,请参阅 intrinsic 描述。
ObjC ARC 附加调用操作数束¶
调用上的 "clang.arc.attachedcall"
操作数束指示该调用之后隐式地跟随一个标记指令和一个调用 ObjC 运行时函数的调用,该运行时函数使用该调用的结果。操作数束接受一个指向运行时函数的强制指针(@objc_retainAutoreleasedReturnValue
或 @objc_unsafeClaimAutoreleasedReturnValue
)。带有此束的调用的返回值由对 @llvm.objc.clang.arc.noop.use
的调用使用,除非被调用函数的返回类型为 void,在这种情况下,操作数束将被忽略。
; The marker instruction and a runtime function call are inserted after the call
; to @foo.
call ptr @foo() [ "clang.arc.attachedcall"(ptr @objc_retainAutoreleasedReturnValue) ]
call ptr @foo() [ "clang.arc.attachedcall"(ptr @objc_unsafeClaimAutoreleasedReturnValue) ]
需要操作数束以确保调用紧随其后的是标记指令和最终输出中的 ObjC 运行时调用。
指针认证操作数束¶
指针认证操作数束以 "ptrauth"
操作数束标签为特征。它们在 指针认证 文档中进行了描述。
KCFI 操作数束¶
间接调用上的 "kcfi"
操作数束指示该调用之前将进行运行时类型检查,该检查验证调用目标是否以与操作数束属性匹配的 类型标识符 为前缀。例如
call void %0() ["kcfi"(i32 1234)]
Clang 使用 -fsanitize=kcfi
发出 KCFI 操作数束和必要的元数据。
收敛控制操作数束¶
“convergencectrl”操作数束仅在 convergent
操作上有效。存在时,操作数束必须恰好包含一个 token 类型的值。有关详细信息,请参阅 收敛操作语义 文档。
模块级内联汇编¶
模块可以包含“模块级内联汇编”块,这对应于 GCC 的“文件作用域内联汇编”块。这些块在内部由 LLVM 连接并被视为一个单一单元,但如果需要,可以在 .ll
文件中分隔。语法非常简单
module asm "inline asm code goes here"
module asm "more can go here"
字符串可以包含任何字符,通过转义非打印字符。使用的转义序列很简单,即 “\xx”,其中 “xx” 是数字的两位十六进制代码。
请注意,即使在发出 .s
文件时,汇编字符串 必须 可由 LLVM 的集成汇编器解析(除非它被禁用)。
数据布局¶
模块可以指定一个目标特定的数据布局字符串,该字符串指定数据在内存中的布局方式。数据布局的语法很简单:
target datalayout = "layout specification"
布局规范 由一系列规范组成,这些规范用减号字符(‘-’)分隔。每个规范以一个字母开头,并且可能在字母后包含其他信息,以定义数据布局的某些方面。接受的规范如下:
E
指定目标以大端序形式布局数据。也就是说,最高有效位的地址位置最低。
e
指定目标以小端序形式布局数据。也就是说,最低有效位的地址位置最低。
S<size>
指定堆栈的自然对齐方式,以位为单位。堆栈变量的对齐提升仅限于自然堆栈对齐,以避免动态堆栈重新对齐。堆栈对齐必须是 8 位的倍数。如果省略,自然堆栈对齐默认为“未指定”,这不会阻止任何对齐提升。
P<address space>
指定与程序内存对应的地址空间。哈佛架构可以使用它来指定 LLVM 应该将函数等内容放置在哪个空间中。如果省略,程序内存空间默认为默认地址空间 0,这对应于冯·诺依曼架构,该架构在同一空间中具有代码和数据。
G<address space>
指定在创建全局变量时默认使用的地址空间。如果省略,全局变量地址空间默认为默认地址空间 0。注意:没有地址空间的变量声明始终在地址空间 0 中创建,此属性仅影响在没有其他上下文信息(例如,在 LLVM passes 中)创建全局变量时要使用的默认值。
A<address space>
指定由 ‘
alloca
’ 创建的对象的地址空间。默认为默认地址空间 0。p[n]:<size>:<abi>[:<pref>][:<idx>]
这指定了指针的 大小 及其在地址空间
n
中的<abi>
和<pref>
首选对齐方式。<pref>
是可选的,默认为<abi>
。第四个参数<idx>
是用于地址计算的索引的大小,它必须小于或等于指针大小。如果未指定,则默认索引大小等于指针大小。所有大小均以位为单位。地址空间n
是可选的,如果未指定,则表示默认地址空间 0。n
的值必须在 [1,2^24) 范围内。i<size>:<abi>[:<pref>]
这指定了给定位
<size>
的整数类型的对齐方式。<size>
的值必须在 [1,2^24) 范围内。<pref>
是可选的,默认为<abi>
。对于i8
,<abi>
值必须等于 8,也就是说,i8
必须是自然对齐的。v<size>:<abi>[:<pref>]
这指定了给定位
<size>
的向量类型的对齐方式。<size>
的值必须在 [1,2^24) 范围内。<pref>
是可选的,默认为<abi>
。f<size>:<abi>[:<pref>]
这指定了给定位
<size>
的浮点类型的对齐方式。只有目标支持的<size>
值才有效。所有目标都支持 32 (float) 和 64 (double);某些目标也支持 80 或 128(long double 的不同变体)。<size>
的值必须在 [1,2^24) 范围内。<pref>
是可选的,默认为<abi>
。a:<abi>[:<pref>]
这指定了聚合类型对象的对齐方式。
<pref>
是可选的,默认为<abi>
。F<type><abi>
这指定了函数指针的对齐方式。
<type>
的选项有:i
: 函数指针的对齐方式独立于函数的对齐方式,并且是<abi>
的倍数。n
: 函数指针的对齐方式是函数上指定的显式对齐方式的倍数,并且是<abi>
的倍数。
m:<mangling>
如果存在,则指定在输出中会对 llvm 名称进行名称修饰。以名称修饰转义字符
\01
为前缀的符号将直接传递给汇编器,而无需转义字符。名称修饰样式选项有:e
: ELF 名称修饰:私有符号获得.L
前缀。l
: GOFF 名称修饰:私有符号获得@
前缀。m
: Mips 名称修饰:私有符号获得$
前缀。o
: Mach-O 名称修饰:私有符号获得L
前缀。其他符号获得_
前缀。x
: Windows x86 COFF 名称修饰:私有符号获得通常的前缀。常规 C 符号获得_
前缀。具有__stdcall
,__fastcall
, 和__vectorcall
的函数具有自定义名称修饰,该修饰会附加@N
,其中 N 是用于传递参数的字节数。以?
开头的 C++ 符号不会以任何方式进行名称修饰。w
: Windows COFF 名称修饰:类似于x
,但常规 C 符号不接收_
前缀。a
: XCOFF 名称修饰:私有符号获得L..
前缀。
n<size1>:<size2>:<size3>...
这指定了目标 CPU 的一组本机整数宽度,以位为单位。例如,它可能包含
n32
用于 32 位 PowerPC,n32:64
用于 PowerPC 64,或n8:16:32:64
用于 X86-64。此集合中的元素被认为可以有效地支持大多数通用算术运算。ni:<address space0>:<address space1>:<address space2>...
这指定了具有指定地址空间的指针类型作为非整型指针类型。
0
地址空间不能指定为非整型。
在每个采用 <abi>:<pref>
的规范中,指定 <pref>
对齐方式是可选的。如果省略,则也应省略前面的 :
,并且 <pref>
将等于 <abi>
。
在为给定目标构建数据布局时,LLVM 从一组默认规范开始,然后(可能)被 datalayout
关键字中的规范覆盖。默认规范在此列表中给出:
e
- 小端序p:64:64:64
- 64 位指针,具有 64 位对齐。p[n]:64:64:64
- 其他地址空间被假定为与默认地址空间相同。S0
- 自然堆栈对齐未指定i1:8:8
- i1 是 8 位(字节)对齐的i8:8:8
- i8 是 8 位(字节)对齐的,这是强制要求的i16:16:16
- i16 是 16 位对齐的i32:32:32
- i32 是 32 位对齐的i64:32:64
- i64 的 ABI 对齐为 32 位,但首选对齐为 64 位f16:16:16
- half 是 16 位对齐的f32:32:32
- float 是 32 位对齐的f64:64:64
- double 是 64 位对齐的f128:128:128
- quad 是 128 位对齐的v64:64:64
- 64 位向量是 64 位对齐的v128:128:128
- 128 位向量是 128 位对齐的a:0:64
- 聚合是 64 位对齐的
当 LLVM 确定给定类型的对齐方式时,它使用以下规则:
如果所寻求的类型与规范之一完全匹配,则使用该规范。
如果未找到匹配项,并且所寻求的类型是整数类型,则使用大于所寻求类型的位宽的最小整数类型。如果没有规范大于位宽,则使用最大的整数类型。例如,给定上面的默认规范,i7 类型将使用 i8 的对齐方式(下一个最大),而 i65 和 i256 都将使用 i64 的对齐方式(最大指定)。
数据布局字符串的功能可能与您期望的不同。值得注意的是,这不是来自前端的关于代码生成器应使用什么对齐方式的规范。
相反,如果指定了,目标数据布局必须与最终的 代码生成器 期望的相匹配。此字符串由中级优化器使用以改进代码,并且只有当它与最终代码生成器使用的内容匹配时才有效。无法生成不将此目标特定细节嵌入到 IR 中的 IR。如果您不指定字符串,则将使用默认规范来生成数据布局,并且优化阶段将相应地运行,并将目标特异性引入到关于这些默认规范的 IR 中。
目标三元组¶
模块可以指定一个目标三元组字符串,该字符串描述目标主机。目标三元组的语法很简单:
target triple = "x86_64-apple-macosx10.7.0"
目标三元组 字符串由一系列标识符组成,这些标识符用减号字符(‘-’)分隔。规范形式是:
ARCHITECTURE-VENDOR-OPERATING_SYSTEM
ARCHITECTURE-VENDOR-OPERATING_SYSTEM-ENVIRONMENT
此信息被传递到后端,以便它为正确的架构生成代码。可以在命令行中使用 -mtriple
命令行选项覆盖此信息。
已分配对象¶
已分配对象、内存对象或简称为对象,是内存空间的一个区域,该区域由诸如 alloca、堆分配调用和全局变量定义之类的内存分配保留。一旦分配,存储在该区域中的字节只能通过基于分配值的指针读取或写入。如果不是基于该对象的指针尝试读取或写入该对象,则这是未定义的行为。
以下属性适用于所有已分配对象,否则行为未定义:
没有已分配对象可以跨越无符号地址空间边界(包括对象末尾之后的指针),
所有已分配对象的大小必须是非负的,并且不超过适合索引类型的最大有符号整数。
对象生命周期¶
已分配对象的生命周期是决定其可访问性的属性。除非另有说明,否则已分配对象自其分配时起处于活动状态,并在其释放后死亡。访问未处于活动状态的已分配对象是未定义的行为,但是不取消引用的操作(例如 getelementptr、ptrtoint 和 icmp)会返回有效结果。这解释了这些指令跨影响对象生命周期的操作的代码移动。可以使用 llvm.lifetime.start 和 llvm.lifetime.end 内置函数调用显式指定堆栈对象的生命周期。
指针别名规则¶
任何内存访问都必须通过与内存访问的地址范围关联的指针值来完成,否则行为未定义。指针值根据以下规则与地址范围关联:
指针值与与其基于的任何值关联的地址关联。
全局变量的地址与变量存储的地址范围关联。
分配指令的结果值与已分配存储的地址范围关联。
默认地址空间中的空指针与任何地址都不关联。
任何地址空间中的 undef 值 与任何地址都不关联。
非零整数常量或从 LLVM 未定义的函数返回的指针值可能与通过 LLVM 提供的机制以外的机制分配的地址范围关联。此类范围不得与 LLVM 提供的机制分配的任何地址范围重叠。
指针值根据以下规则基于另一个指针值:
由标量
getelementptr
操作形成的指针值基于getelementptr
的指针类型操作数。向量
getelementptr
操作结果的通道 l 中的指针基于向量指针类型操作数的通道 l 中的指针。bitcast
的结果值基于bitcast
的操作数。由
inttoptr
形成的指针值基于所有(直接或间接)有助于指针值计算的指针值。“基于”关系是传递的。
请注意,此“基于”的定义有意类似于 C99 中“基于”的定义,尽管它略微弱一些。
LLVM IR 不会将类型与内存关联。load
的结果类型仅指示要加载的内存的大小和对齐方式,以及值的解释。store
的第一个操作数类型类似地仅指示 store
的大小和对齐方式。
因此,基于类型的别名分析,也称为 TBAA,也称为 -fstrict-aliasing
,不适用于一般的未修饰 LLVM IR。元数据可用于编码额外的的信息,专门的优化pass可以使用这些信息来实现基于类型的别名分析。
指针捕获¶
给定一个函数调用和一个作为参数传递或在调用之前存储在内存中的指针,该调用可能会捕获指针的两个组成部分:
指针的地址,即其整数值。这还包括地址的各个部分或关于地址的任何信息,包括它不等于一个特定值的事实。我们进一步区分是否仅捕获地址是/不是空的事实。
指针的出处,即通过指针执行内存访问的能力,从指针别名规则的意义上讲。我们进一步区分是否只允许读取访问,还是允许读取和写入访问。
例如,以下函数捕获了 %a
的地址,因为它与指针进行了比较,泄露了关于指针标识的信息:
@glb = global i8 0
define i1 @f(ptr %a) {
%c = icmp eq ptr %a, @glb
ret i1 %c
}
该函数不捕获指针的出处,因为 icmp
指令仅对指针地址进行操作。以下函数捕获指针的地址和出处,因为两者都可以在函数返回后从 @glb
读取:
@glb = global ptr null
define void @f(ptr %a) {
store ptr %a, ptr @glb
ret void
}
以下函数既不捕获指针的地址也不捕获指针的出处:
define i32 @f(ptr %a) {
%v = load i32, ptr %a
ret i32
}
虽然地址捕获包括函数体内部地址的使用,但出处捕获仅指在函数返回后执行访问的能力。函数内部的内存访问不被视为指针捕获。
我们可以进一步说捕获仅通过特定位置发生。在以下示例中,指针(地址和出处)仅通过返回值捕获:
define ptr @f(ptr %a) {
%gep = getelementptr i8, ptr %a, i64 4
ret ptr %gep
}
但是,我们始终认为直接检查指针地址(例如,使用 ptrtoint
)是位置独立的。即使 ptrtoint
最终仅贡献于返回值,以下示例也不被视为仅返回捕获:
@lookup = constant [4 x i8] [i8 0, i8 1, i8 2, i8 3]
define ptr @f(ptr %a) {
%a.addr = ptrtoint ptr %a to i64
%mask = and i64 %a.addr, 3
%gep = getelementptr i8, ptr @lookup, i64 %mask
ret ptr %gep
}
选择此定义是为了允许捕获分析以通常的方式继续使用返回值。
以下更详细地描述了捕获指针的可能方式,其中对“捕获”一词的非限定使用是指捕获地址和出处两者。
调用将携带信息的指针的任何位存储到某个位置,并且存储的位可以在此调用退出后由调用者从该位置读取。
@glb = global ptr null
@glb2 = global ptr null
@glb3 = global ptr null
@glbi = global i32 0
define ptr @f(ptr %a, ptr %b, ptr %c, ptr %d, ptr %e) {
store ptr %a, ptr @glb ; %a is captured by this call
store ptr %b, ptr @glb2 ; %b isn't captured because the stored value is overwritten by the store below
store ptr null, ptr @glb2
store ptr %c, ptr @glb3
call void @g() ; If @g makes a copy of %c that outlives this call (@f), %c is captured
store ptr null, ptr @glb3
%i = ptrtoint ptr %d to i64
%j = trunc i64 %i to i32
store i32 %j, ptr @glbi ; %d is captured
ret ptr %e ; %e is captured
}
调用将携带信息的指针的任何位存储到某个位置,并且存储的位可以通过同步由另一个线程安全地从该位置读取。
@lock = global i1 true
define void @f(ptr %a) {
store ptr %a, ptr @glb
store atomic i1 false, ptr @lock release ; %a is captured because another thread can safely read @glb
store ptr null, ptr @glb
ret void
}
调用的行为取决于携带信息的指针的任何位(仅地址捕获)。
@glb = global i8 0
define void @f(ptr %a) {
%c = icmp eq ptr %a, @glb
br i1 %c, label %BB_EXIT, label %BB_CONTINUE ; captures address of %a only
BB_EXIT:
call void @exit()
unreachable
BB_CONTINUE:
ret void
}
指针用作 volatile 访问的指针操作数。
Volatile 内存访问¶
某些内存访问,例如 load、store 和 llvm.memcpy 可以标记为 volatile
。优化器不得更改 volatile 操作的数量或更改它们相对于其他 volatile 操作的执行顺序。优化器 可以 更改 volatile 操作相对于非 volatile 操作的顺序。这不是 Java 的 “volatile”,也没有跨线程同步行为。
volatile load 或 store 可能具有额外的特定于目标的语义。任何 volatile 操作都可能具有副作用,并且任何 volatile 操作都可以读取和/或修改状态,这些状态无法通过此模块中的常规 load 或 store 访问。Volatile 操作可以使用不指向内存的地址(如 MMIO 寄存器)。这意味着编译器可能不会使用 volatile 操作来证明对该地址的非 volatile 访问具有定义的行为。
volatile 访问允许的副作用是有限的。如果对给定地址的非 volatile store 是合法的,则 volatile 操作可以修改该地址的内存。Volatile 操作不得修改正在编译的模块可访问的任何其他内存。Volatile 操作不得调用当前模块中的任何代码。
通常(没有特定于目标的上下文),volatile 操作的地址空间可能不会更改。当取消引用无效指针时,不同的地址空间可能具有不同的陷阱行为。
编译器可以假定在 volatile 操作之后执行将继续,因此修改内存或可能具有未定义行为的操作可以提升到 volatile 操作之后。
作为对先前规则的例外,编译器可能不会假定在 volatile store 操作之后执行将继续。此限制是必要的,以支持 C 中有意存储到无效指针以使程序崩溃的某种常见模式。将来,允许前端控制此行为可能是有意义的。
IR 级别的 volatile load 和 store 即使在这些 intrinsics 被标记为 volatile 的情况下,也不能安全地优化为 llvm.memcpy
或 llvm.memmove
intrinsics。同样,后端永远不应拆分或合并目标合法的 volatile load/store 指令。类似地,IR 级别的 volatile load 和 store 不能从整数更改为浮点数,反之亦然。
理由
平台可能依赖于以本机支持的数据宽度执行的 volatile load 和 store 作为单条指令。例如,在 C 中,这适用于具有本机硬件支持的 volatile 原始类型的左值,但不一定适用于聚合类型。前端坚持这些期望,这些期望在 IR 中是有意未指定的。以上规则确保 IR 转换不会违反前端与语言的约定。
并发操作的内存模型¶
LLVM IR 没有定义任何启动并行执行线程或注册信号处理程序的方法。尽管如此,存在特定于平台的方式来创建它们,并且我们定义了 LLVM IR 在它们存在时的行为。此模型受 C++ 内存模型的启发。
有关此模型的更非正式的介绍,请参阅《LLVM 原子指令和并发指南》。
我们将 先发生 部分顺序定义为最小的部分顺序,它:
是单线程程序顺序的超集,并且
当
a
与b
同步 时,包括从a
到b
的边。同步 对由特定于平台的技术(例如 pthread 锁、线程创建、线程加入等)和原子指令引入。(另请参见“原子内存排序约束”)。
请注意,程序顺序不会在线程和在该线程内部执行的信号之间引入 先发生 边。
每个(已定义的)读取操作(load 指令、memcpy、原子 load/read-modify-writes 等)R 读取一系列由(已定义的)写入操作(store 指令、原子 store/read-modify-writes、memcpy 等)写入的字节。就本节而言,初始化的全局变量被认为具有初始值设定项的写入,该写入是原子的,并且在对相关内存的任何其他读取或写入之前发生。对于读取 R 的每个字节,Rbyte 可能会看到对同一字节的任何写入,但以下情况除外:
如果 write1 先发生 于 write2,并且 write2 先发生 于 Rbyte,则 Rbyte 不会看到 write1。
如果 Rbyte 先发生 于 write3,则 Rbyte 不会看到 write3。
给定该定义,Rbyte 定义如下:
如果 R 是 volatile 的,则结果是特定于目标的。(Volatile 应该提供可以支持 C/C++ 中的
sig_atomic_t
的保证,并且可以用于访问不表现得像普通内存的地址。它通常不提供跨线程同步。)否则,如果没有 先发生 于 Rbyte 的对同一字节的写入,则 Rbyte 为该字节返回
undef
。否则,如果 Rbyte 可能只看到一个写入,则 Rbyte 返回该写入写入的值。
否则,如果 R 是原子的,并且 Rbyte 可能看到的所有写入都是原子的,则它选择写入的值之一。有关如何做出选择的其他约束,请参阅“原子内存排序约束”部分。
否则 Rbyte 返回
undef
。
R 返回由它读取的一系列字节组成的值。这意味着值中的某些字节可能是 undef
,而 整个值不是 undef
。请注意,这仅定义了操作的语义;这并不意味着目标将发出多条指令来读取一系列字节。
请注意,在未使用任何原子 intrinsics 的情况下,此模型仅对 IR 转换施加一个限制,即在单线程执行所需的内容之上:通常不允许引入对可能不会被存储的字节的 store。(具体而言,在另一个线程可能写入和读取地址的情况下,引入 store 可能会将可能只看到一个写入的 load 更改为可能看到多个写入的 load。)
原子内存排序约束¶
原子指令(cmpxchg、atomicrmw、fence、原子 load 和 原子 store)采用排序参数,这些参数确定它们与同一地址上的哪些其他原子指令同步。这些语义实现了 Java 或 C++ 内存模型;如果这些描述不够精确,请检查这些规范(请参阅原子指南中的规范参考)。fence 指令以稍微不同的方式处理这些排序,因为它们不接受地址。有关详细信息,请参阅该指令的文档。
有关排序约束的更简单介绍,请参阅《LLVM 原子指令和并发指南》。
unordered
值集合的可读性受先行发生偏序关系 (happens-before partial order) 支配。除非某个操作写入了一个值,否则无法读取该值。 这旨在提供足够强大的保证来建模 Java 的非 volatile 共享变量。此排序不能为读取-修改-写入操作指定;它不足以使它们在任何有趣的方式中成为原子操作。
monotonic
除了
unordered
的保证之外,对于每个地址上monotonic
操作的修改,还存在单个总顺序。所有修改顺序都必须与先行发生顺序兼容。不能保证修改顺序可以组合成整个程序的全局总顺序(而且这通常是不可能的)。原子读取-修改-写入操作(cmpxchg 和 atomicrmw)中的读取操作读取修改顺序中紧接在其写入值之前的值。如果一个原子读取操作先于对同一地址的另一个原子读取操作发生,则稍后的读取操作必须看到相同的值或地址修改顺序中稍后的值。这禁止对同一地址上的monotonic
(或更强)操作进行重排序。如果一个地址被一个线程monotonic
地写入,而其他线程monotonic
地重复读取该地址,则其他线程最终必须看到写入。这对应于 C/C++ 的memory_order_relaxed
。acquire
除了
monotonic
的保证之外,还可以与release
操作形成同步发生 (synchronizes-with) 边。这旨在建模 C/C++ 的memory_order_acquire
。release
除了
monotonic
的保证之外,如果此操作写入的值随后被acquire
操作读取,则它与该操作同步发生。此外,即使release
操作写入的值在被读取之前已被读取-修改-写入操作修改,这种情况也会发生。(这样一组操作构成一个释放序列 (release sequence))。这对应于 C/C++ 的memory_order_release
。acq_rel
(acquire+release)在其地址上充当
acquire
和release
操作。这对应于 C/C++ 的memory_order_acq_rel
。seq_cst
(顺序一致性)除了
acq_rel
的保证(对于仅读取的操作是acquire
,对于仅写入的操作是release
)之外,所有地址上的所有顺序一致性操作都存在全局总顺序。每个顺序一致性读取都看到此全局顺序中对同一地址的最后一次先前写入。这对应于 C/C++ 的memory_order_seq_cst
和 Java 的volatile
。注意:如果涉及非
seq_cst
访问,则不能保证此全局总顺序与先行发生偏序完全一致。有关确切保证的更多详细信息,请参阅 C++ 标准的 [atomics.order] 部分。
如果原子操作标记为 syncscope("singlethread")
,它仅与在同一线程(例如,在信号处理程序中)中运行的其他操作的 seq_cst 总顺序同步发生并仅参与其中。
如果原子操作标记为 syncscope("<target-scope>")
,其中 <target-scope>
是目标特定的同步作用域,则它是否与在其他操作的 seq_cst 总顺序中同步发生并参与其中取决于目标。
否则,未标记为 syncscope("singlethread")
或 syncscope("<target-scope>")
的原子操作与未标记为 syncscope("singlethread")
或 syncscope("<target-scope>")
的其他操作的 seq_cst 总顺序同步发生并参与其中。
浮点环境¶
默认的 LLVM 浮点环境假定陷阱 (traps) 被禁用,并且状态标志 (status flags) 不可观察。因此,浮点数学运算没有副作用,可以自由推测 (speculated freely)。结果假定为舍入到最近 (round-to-nearest) 的舍入模式,并且亚 нормаль数 (subnormals) 假定被保留。
在不满足这些假设的环境中运行 LLVM 代码通常会导致未定义的行为。 strictfp
和 denormal-fp-math
属性以及 受约束的浮点内在函数 可用于削弱 LLVM 的假设并确保在非默认浮点环境中的已定义行为;有关详细信息,请参阅它们各自的文档。
浮点 NaN 值的行为¶
浮点 NaN 值由一个符号位、一个静默/信令位 (quiet/signaling bit) 和一个有效载荷 (payload) 组成(有效载荷构成了尾数 (mantissa) 的其余部分,除了静默/信令位)。 LLVM 假定静默/信令位设置为 1
表示静默 NaN (QNaN),值为 0
表示信令 NaN (SNaN)。在下文中,我们将仅将其称为“静默位”。
浮点值的表示位不会任意突变;特别是,如果没有执行浮点运算,则 NaN 符号、静默位和有效载荷将被保留。
就本节而言,bitcast
以及以下操作不是“浮点数学运算”:fneg
、llvm.fabs
和 llvm.copysign
。这些操作直接作用于底层位表示,并且永远不会更改任何内容,除了可能的符号位。
返回 NaN 的浮点数学运算是 LLVM 实现 IEEE-754 语义的一般原则的例外。除非另有说明,否则每当 IEEE-754 语义说明返回 NaN 值时,以下规则适用:结果具有不确定的符号;静默位和有效载荷从以下选项集中非确定性地选择
静默位被设置,有效载荷全为零。(“首选 NaN”情况)
静默位被设置,有效载荷从任何作为 NaN 的输入操作数复制。(“静默 NaN 传播”情况)
静默位和有效载荷从任何作为 NaN 的输入操作数复制。(“不变 NaN 传播”情况)
静默位被设置,有效载荷从目标特定的“额外”可能的 NaN 有效载荷集中选取。该集合可以取决于输入操作数值。此集合在 x86 和 ARM 上为空,但在其他架构上可能非空。(例如,在 wasm 上,如果任何输入 NaN 没有首选的全零有效载荷或任何输入 NaN 是 SNaN,则此集合包含所有可能的有效载荷;否则,它为空。在 SPARC 上,此集合由全一有效载荷组成。)
特别是,如果所有输入 NaN 都是静默的(或者如果没有输入 NaN),则输出 NaN 肯定是静默的。信令 NaN 输出仅在它们作为输入值提供时才会发生。例如,“fmul SNaN, 1.0”可能会简化为 SNaN 而不是 QNaN。类似地,如果所有输入 NaN 都是首选的(或者如果没有输入 NaN)并且目标没有任何“额外”的 NaN 有效载荷,则保证输出 NaN 是首选的。
浮点数学运算允许将所有 NaN 视为静默 NaN。例如,“pow(1.0, SNaN)”可能会简化为 1.0。
需要与此不同行为的代码应使用 受约束的浮点内在函数。特别是,受约束的内在函数排除了“不变 NaN 传播”情况;它们保证返回 QNaN。
不幸的是,由于难以或不可能修复的问题,LLVM 在某些架构上违反了其自身的规范
未启用 SSE2 的 x86-32 在执行浮点数学运算时可能会将浮点值转换为 x86_fp80 并返回;这可能会导致结果的精度与预期不同,并且可能会更改 NaN 值。由于优化可能会做出相互矛盾的假设,这可能会导致任意的错误编译。请参阅 issue #44218。
x86-32(即使启用了 SSE2)也可能在某些调用约定中对从函数返回的值隐式执行此类转换。请参阅 issue #66803。
较旧的 MIPS 版本对静默/信令位使用相反的极性,并且 LLVM 未正确表示这一点。请参阅 issue #60796。
浮点语义¶
本节定义了 IEEE-745 指定格式的类型上的核心浮点运算的语义。这些类型是:half
、float
、double
和 fp128
,它们分别对应于 binary16、binary32、binary64 和 binary128 格式。“核心”运算是 IEEE-745 第 5 节中定义的运算,所有这些运算都有对应的 LLVM 运算。
这些运算返回的值与在默认 LLVM 浮点环境中执行的相应 IEEE-754 运算的值匹配,但 NaN 结果的行为在此处指定。特别是,返回非 NaN 值的浮点指令保证在所有机器和优化级别上始终返回相同的按位相同的 (bit-identical) 结果。
这意味着优化和后端可能不会以任何方式更改这些运算的观察到的按位结果(除非返回 NaN),并且前端可以依赖于这些运算提供标准中描述的正确舍入的结果。
(请注意,这仅关于这些运算返回的值;有关标志和异常,请参阅浮点环境部分。)
各种标志、属性和元数据可以改变这些运算的行为,从而使它们不再在机器和优化级别之间按位相同:最值得注意的是,快速数学标志以及 strictfp 和 denormal-fp-math 属性和 fpmath 元数据 <fpmath-metadata>。有关详细信息,请参阅它们对应的文档。
快速数学标志¶
LLVM IR 浮点运算(fneg、fadd、fsub、fmul、fdiv、frem、fcmp、fptrunc、fpext)以及 phi、select 或 call 返回浮点类型的指令可以使用以下标志来启用原本不安全的浮点转换。
fast
此标志是同时指定所有快速数学标志的简写,并且从使用所有标志中不赋予额外的语义。
nnan
无 NaN - 允许优化假定参数和结果不是 NaN。如果参数是 NaN,或者结果将是 NaN,则会生成毒化值 (poison value) 代替。
安静 NaN
No Infs - 允许优化假定参数和结果不是 +/-Inf。如果参数是 +/-Inf,或者结果将是 +/-Inf,则会生成毒化值 (poison value) 代替。
nsz
无符号零 - 允许优化将零参数或零结果的符号视为不重要。这并不意味着 -0.0 是毒化值和/或保证在操作中不存在。
注意:对于 phi、select 和 call 指令,以下返回类型被认为是浮点类型
浮点标量或向量类型
浮点标量或向量类型的数组类型(嵌套到任何深度)
浮点标量或向量类型的同质字面结构体类型
基于重写的标志¶
以下标志具有基于重写的语义。这些标志允许将表达式(可能包含多个非连续指令)重写为替代指令。当表达式中涉及多个指令时,所有指令都必须存在必要的基于重写的标志,并且重写的指令通常将具有输入指令上存在的标志的交集。
在以下示例中,@orig
主体中的浮点表达式具有共同的 contract
和 reassoc
,因此,如果将其重写为 @target
主体中的表达式,则所有新指令都将获得这两个标志,并且仅将这两个标志作为结果。由于 arcp
仅存在于表达式中的一个指令上,因此它不存在于转换后的表达式中。此外,此处的重结合 (reassociation) 仅在两个指令都具有 reassoc
标志时才合法;如果只有一个指令具有此标志,则进行转换将是不合法的。
define double @orig(double %a, double %b, double %c) {
%t1 = fmul contract reassoc double %a, %b
%val = fmul contract reassoc arcp double %t1, %c
ret double %val
}
define double @target(double %a, double %b, double %c) {
%t1 = fmul contract reassoc double %b, %c
%val = fmul contract reassoc double %a, %t1
ret double %val
}
这些规则不适用于其他快速数学标志。像 nnan
这样的标志是否存在于任何或所有重写的指令上,取决于给定原始标志的情况下,该指令是否可能具有 NaN 输入或输出。
arcp
允许将除法视为乘以倒数。具体来说,这允许将
a / b
视为等效于a * (1.0 / b)
(随后可能容易受到代码移动的影响),并且还允许将a / (b / c)
视为等效于a * (c / b)
。这两个重写都可以双向应用:a * (c / b)
可以重写为a / (b / c)
。contract
允许浮点收缩 (contraction)(例如,将乘法后跟加法融合到融合乘加 (fused multiply-and-add) 中)。这不会启用重结合来形成任意收缩。例如,
(a*b) + (c*d) + e
不能转换为(a*b) + ((c*d) + e)
以创建两个 fma 操作。
afn
近似函数 - 允许用近似计算替换函数(sin、log、sqrt 等)。请参阅浮点内在函数定义,了解这可以应用于 LLVM 的内在数学函数的位置。
reassoc
允许浮点指令的重结合转换。这可能会显着改变浮点数的结果。
用例列表顺序指令¶
用例列表指令编码每个用例列表的内存顺序,允许重新创建该顺序。<order-indexes>
是一个逗号分隔的索引列表,这些索引被分配给引用的值的用例。引用的值的用例列表会立即按这些索引排序。
用例列表指令可能出现在函数作用域或全局作用域中。它们不是指令,对 IR 的语义没有影响。当它们位于函数作用域时,它们必须出现在最终基本块的终止符之后。
如果基本块通过 blockaddress()
表达式获取其地址,则可以使用 uselistorder_bb
从其函数作用域外部重新排序其用例列表。
- 语法:
uselistorder <ty> <value>, { <order-indexes> }
uselistorder_bb @function, %block { <order-indexes> }
- 示例:
define void @foo(i32 %arg1, i32 %arg2) {
entry:
; ... instructions ...
bb:
; ... instructions ...
; At function scope.
uselistorder i32 %arg1, { 1, 0, 2 }
uselistorder label %bb, { 1, 0 }
}
; At global scope.
uselistorder ptr @global, { 1, 2, 0 }
uselistorder i32 7, { 1, 0 }
uselistorder i32 (i32) @bar, { 1, 0 }
uselistorder_bb @foo, %bb, { 5, 1, 3, 2, 0, 4 }
源文件名¶
源文件名字符串设置为原始模块标识符,例如,当通过 clang 前端从源代码编译时,它将是已编译的源文件的名称。然后,它通过 IR 和位代码保留。
目前,这对于为配置文件数据中使用的局部函数生成一致的唯一全局标识符是必要的,该标识符将源文件名添加到局部函数名称之前。
源文件名的语法很简单
source_filename = "/path/to/source.c"
类型系统¶
LLVM 类型系统是中间表示 (intermediate representation) 最重要的特征之一。类型化使能够在中间表示上直接执行许多优化,而无需在转换之前进行额外的分析。强大的类型系统使读取生成的代码更容易,并启用在普通三地址代码表示上不可行的新颖分析和转换。
Void 类型¶
- 概述:
void 类型不表示任何值,也没有大小。
- 语法:
void
函数类型¶
- 概述:
函数类型可以被认为是函数签名。它由返回类型和形式参数类型列表组成。函数类型的返回类型是 void 类型或第一类类型 —— 除了 label 和 metadata 类型。
- 语法:
<returntype> (<parameter list>)
…其中“<parameter list>
”是以逗号分隔的类型说明符列表。可选地,参数列表可以包括类型 ...
,这表示该函数接受可变数量的参数。可变参数函数可以使用 可变参数处理内在函数 访问其参数。“<returntype>
”是除 label 和 metadata 之外的任何类型。
- 示例:
|
接受 |
|
一个 vararg 函数,它至少接受一个 指针 参数并返回一个整数。这是 LLVM 中 |
|
一个接受 |
第一类类型¶
第一类 类型可能是最重要的。这些类型的值是唯一可以由指令生成的值。
单值类型¶
这些是从 CodeGen 的角度来看在寄存器中有效的类型。
整数类型¶
- 概述:
整数类型是一个非常简单的类型,它只是为所需的整数类型指定任意位宽。可以指定从 1 位到 223(约 800 万)的任何位宽。
- 语法:
iN
整数将占用的位数由 N
值指定。
示例:¶
|
单比特整数。 |
|
32 位整数。 |
|
一个超过 100 万位的非常大的整数。 |
浮点类型¶
类型 |
描述 |
---|---|
|
16 位浮点值 (IEEE-754 binary16) |
|
16 位“brain”浮点值(7 位有效数)。提供与 |
|
32 位浮点值 (IEEE-754 binary32) |
|
64 位浮点值 (IEEE-754 binary64) |
|
128 位浮点值 (IEEE-754 binary128) |
|
80 位浮点值 (X87) |
|
128 位浮点值(两个 64 位) |
X86_amx 类型¶
- 概述:
x86_amx 类型表示 x86 机器上的 AMX tile 寄存器中保存的值。允许对其执行的操作非常有限。仅允许使用少数内在函数:步幅加载和存储、零和点积。此类型不允许任何指令。此类型没有参数、数组、指针、向量或常量。
- 语法:
x86_amx
指针类型¶
- 概述:
指针类型 ptr
用于指定内存位置。指针通常用于引用内存中的对象。
指针类型可能具有可选的地址空间属性,用于定义被指向对象所在的编号地址空间。例如,ptr addrspace(5)
是指向地址空间 5 的指针。除了整数常量之外,addrspace
还可以引用 datalayout 字符串 中定义的地址空间之一。addrspace("A")
将使用 alloca 地址空间,addrspace("G")
将使用默认的全局地址空间,addrspace("P")
将使用程序地址空间。
默认地址空间是数字零。
非零地址空间的语义是特定于目标的。通过不可解引用 (non-dereferenceable) 指针进行的内存访问在任何地址空间中都是未定义的行为。除非函数标记有 null_pointer_is_valid
属性,否则仅假定位值 0 的指针在地址空间 0 中是不可解引用的。
如果可以证明可以通过具有不同地址空间的指针访问对象,则可以修改访问以使用该地址空间。如果操作是 volatile
,则例外适用。
在 LLVM 15 之前,指针类型还指定了被指向类型 (pointee type),例如 i8*
、[4 x i32]*
或 i32 (i32*)*
。在 LLVM 15 中,在非默认选项下仍然支持此类“类型化指针”。有关更多信息,请参阅 opaque pointers 文档。
目标扩展类型¶
- 概述:
目标扩展类型表示必须通过优化保留的类型,但在其他方面通常对编译器是不透明的。 它们可以用作函数参数或实参,以及在 phi 或 select 指令中。 一些类型也可以在 alloca 指令中或作为全局值使用,因此,对它们使用 load 和 store 指令是合法的。 这些类型的完整语义由目标定义。
目标扩展类型可能拥有的唯一常量是 zeroinitializer
、undef
和 poison
。 目标扩展类型的其他可能值可能来自特定于目标的内联函数和函数。
这些类型不能转换为其他类型。 因此,在 bitcast 指令中(作为源类型或目标类型)使用它们是不合法的,在 ptrtoint 或 inttoptr 指令中使用它们也是不合法的。 同样,在 icmp 指令中使用它们也是不合法的。
目标扩展类型具有名称和可选的类型或整数参数。 名称和参数的含义由目标定义。 在 LLVM IR 中定义时,所有类型参数都必须位于所有整数参数之前。
特定的目标扩展类型在 LLVM 中注册时具有特定的属性。 这些属性可用于限制类型出现在某些上下文中,例如作为全局变量的类型或使 zeroinitializer
常量有效。 类型属性的完整列表可以在 llvm::TargetExtType::Property
的文档中找到 (doxygen)。
- 语法:
target("label")
target("label", void)
target("label", void, i32)
target("label", 0, 1, 2)
target("label", void, i32, 0, 1, 2)
向量类型¶
- 概述:
向量类型是一种简单的派生类型,表示元素向量。 当使用单个指令 (SIMD) 并行操作多个原始数据时,将使用向量类型。 向量类型需要大小(元素数量)、底层原始数据类型和可伸缩属性,以表示在编译时硬件向量长度未知的向量。 向量类型被认为是 一等 类型。
- 内存布局:
通常,向量元素在内存中的布局方式与 数组类型 相同。 只要向量元素是字节大小,这种类比就很好用。 但是,当向量的元素不是字节大小时,情况会变得更加复杂。 描述布局的一种方法是描述当诸如 <N x iM> 的向量位cast为具有 N*M 位的整数类型,然后遵循将此类整数存储到内存的规则时会发生什么。
从向量类型到标量整数类型的位cast将看到元素被打包在一起(没有填充)。 元素插入整数的顺序取决于字节序。 对于小端字节序,元素零放在整数的最低有效位中,对于大端字节序,元素零放在最高有效位中。
以向量(例如 <i4 1, i4 2, i4 3, i4 5>
)为例,并结合我们可以用位cast后跟整数存储来替换向量存储的类比,对于大端字节序,我们得到这个
%val = bitcast <4 x i4> <i4 1, i4 2, i4 3, i4 5> to i16
; Bitcasting from a vector to an integral type can be seen as
; concatenating the values:
; %val now has the hexadecimal value 0x1235.
store i16 %val, ptr %ptr
; In memory the content will be (8-bit addressing):
;
; [%ptr + 0]: 00010010 (0x12)
; [%ptr + 1]: 00110101 (0x35)
小端字节序的相同示例
%val = bitcast <4 x i4> <i4 1, i4 2, i4 3, i4 5> to i16
; Bitcasting from a vector to an integral type can be seen as
; concatenating the values:
; %val now has the hexadecimal value 0x5321.
store i16 %val, ptr %ptr
; In memory the content will be (8-bit addressing):
;
; [%ptr + 0]: 00100001 (0x21)
; [%ptr + 1]: 01010011 (0x53)
当 <N*M>
不能被字节大小整除时,确切的内存布局是未指定的(就像大小相同的整数类型一样)。 这是因为当类型大小小于类型的存储大小时,不同的目标可能会将填充放在不同的位置。
- 语法:
< <# elements> x <elementtype> > ; Fixed-length vector
< vscale x <# elements> x <elementtype> > ; Scalable vector
元素的数量是一个大于 0 的常量整数值; elementtype 可以是任何整数、浮点或指针类型。 不允许大小为零的向量。 对于可伸缩向量,元素总数是指定元素数量的常数倍数(称为 vscale); vscale 是一个正整数,在编译时未知,并且对于运行时所有可伸缩向量都是相同的硬件相关常量。 因此,特定可伸缩向量类型的大小在 IR 中是恒定的,即使字节的确切大小在运行时之前无法确定。
- 示例:
|
包含 4 个 32 位整数值的向量。 |
|
包含 8 个 32 位浮点值的向量。 |
|
包含 2 个 64 位整数值的向量。 |
|
包含 4 个指针的向量 |
|
包含 4 的倍数的 32 位整数值的向量。 |
标签类型¶
- 概述:
标签类型表示代码标签。
- 语法:
label
令牌类型¶
- 概述:
当值与指令关联,但该值的所有用途都不应尝试内省或模糊化它时,将使用令牌类型。 因此,拥有令牌类型的 phi 或 select 是不合适的。
- 语法:
token
元数据类型¶
- 概述:
元数据类型表示嵌入式元数据。 除了 函数 参数外,不能从元数据创建派生类型。
- 语法:
metadata
聚合类型¶
聚合类型是可以包含多个成员类型的派生类型的子集。 数组 和 结构体 是聚合类型。 向量 不被认为是聚合类型。
数组类型¶
- 概述:
数组类型是一种非常简单的派生类型,它在内存中顺序排列元素。 数组类型需要大小(元素数量)和底层数据类型。
- 语法:
[<# elements> x <elementtype>]
元素的数量是一个常量整数值; elementtype
可以是任何具有大小的类型。
- 示例:
|
包含 40 个 32 位整数值的数组。 |
|
包含 41 个 32 位整数值的数组。 |
|
包含 4 个 8 位整数值的数组。 |
以下是一些多维数组的示例
|
3x4 的 32 位整数值数组。 |
|
12x10 的单精度浮点值数组。 |
|
2x3x4 的 16 位整数值数组。 |
对于超出静态类型暗示的数组末尾的索引没有限制(尽管在某些情况下,对于超出 已分配对象 边界的索引存在限制)。 这意味着单维“可变大小数组”寻址可以在 LLVM 中使用零长度数组类型来实现。 例如,LLVM 中“pascal 样式数组”的实现可以使用类型“{ i32, [0 x float]}
”。
结构体类型¶
- 概述:
结构体类型用于表示内存中数据成员的集合。 结构的元素可以是任何具有大小的类型。
内存中的结构体通过使用 'load
' 和 'store
',并通过使用 'getelementptr
' 指令获取指向字段的指针来访问。 寄存器中的结构体使用 'extractvalue
' 和 'insertvalue
' 指令来访问。
结构体可以选择是“packed”结构体,这表示结构体的对齐方式是一个字节,并且元素之间没有填充。 在非 packed 结构体中,字段类型之间的填充按照模块中 DataLayout 字符串的定义插入,这需要与底层代码生成器期望的匹配。
结构体可以是“字面量”或“已标识”的。 字面量结构体与其他类型内联定义(例如 [2 x {i32, i32}]
),而已标识类型始终在顶层使用名称定义。 字面量类型通过其内容进行唯一化,并且永远不能是递归的或不透明的,因为没有办法编写一个。 已标识类型可以是不透明的,并且永远不会被唯一化。 已标识类型不得是递归的。
- 语法:
%T1 = type { <type list> } ; Identified normal struct type
%T2 = type <{ <type list> }> ; Identified packed struct type
- 示例:
|
三个 |
|
一对,其中第一个元素是 |
|
已知大小为 5 字节的 packed 结构体。 |
不透明结构体类型¶
- 概述:
不透明结构体类型用于表示未指定主体的结构体类型。 这对应于(例如)C 语言中前向声明结构体的概念。 它们可以被命名 (%X
) 或未命名 (%52
)。
- 语法:
%X = type opaque
%52 = type opaque
- 示例:
|
一种不透明类型。 |
常量¶
LLVM 有几种不同的基本常量类型。 本节介绍它们的所有类型及其语法。
简单常量¶
- 布尔常量
字符串 '
true
' 和 'false
' 都是i1
类型的有效常量。- 整数常量
标准整数(例如 '
4
')是 整数 类型的常量。 它们可以是十进制或十六进制。 十进制整数可以以 - 为前缀来表示负整数,例如 '-1234
'。 十六进制整数必须以 u 或 s 为前缀,以分别指示它们是无符号的还是有符号的。 例如 'u0x8000
' 给出 32768,而 's0x8000
' 给出 -32768。请注意,十六进制整数从活动位的数量进行符号扩展,即位宽减去前导零的数量。 因此,'
s0x0001
' 的类型为 'i16
' 将为 -1,而不是 1。- 浮点常量
浮点常量使用标准十进制表示法(例如 123.421)、指数表示法(例如 1.23421e+2)或更精确的十六进制表示法(见下文)。 汇编器需要浮点常量的精确十进制值。 例如,汇编器接受 1.25 但拒绝 1.3,因为 1.3 是二进制中的循环小数。 浮点常量必须具有 浮点 类型。
- 空指针常量
标识符 '
null
' 被识别为空指针常量,并且必须是 指针类型。- 令牌常量
标识符 '
none
' 被识别为空令牌常量,并且必须是 令牌类型。
常量的一种非直观表示法是浮点常量的十六进制形式。 例如,形式 'double 0x432ff973cafa8000
' 等同于(但比 'double 4.5e+15
' 更难阅读)。 唯一需要十六进制浮点常量的时候(也是反汇编器生成它们的唯一时候)是当必须发出浮点常量,但它不能以合理位数的十进制浮点数表示时。 例如,NaN、无穷大和其他特殊值以其 IEEE 十六进制格式表示,以便汇编和反汇编不会导致常量中的任何位发生变化。
当使用十六进制形式时,bfloat、half、float 和 double 类型的常量使用上面显示的 16 位数字形式表示(这与 double 的 IEEE754 表示匹配); 但是,bfloat、half 和 float 值必须分别可以精确地表示为 bfloat、IEEE 754 half 和 IEEE 754 单精度。 十六进制格式始终用于 long double,并且 long double 有三种形式。 x86 使用的 80 位格式表示为 0xK
后跟 20 个十六进制数字。 PowerPC 使用的 128 位格式(两个相邻的 double)表示为 0xM
后跟 32 个十六进制数字。 IEEE 128 位格式表示为 0xL
后跟 32 个十六进制数字。 Long double 只有在它们与目标上的 long double 格式匹配时才有效。 IEEE 16 位格式(半精度)表示为 0xH
后跟 4 个十六进制数字。 bfloat 16 位格式表示为 0xR
后跟 4 个十六进制数字。 所有十六进制格式都是大端字节序(符号位在最左侧)。
没有 x86_amx 类型的常量。
复杂常量¶
复杂常量是简单常量和较小复杂常量的(可能是递归的)组合。
- 结构体常量
结构体常量用类似于结构体类型定义的符号表示(以逗号分隔的元素列表,用大括号 (
{}
) 包围)。 例如:“{ i32 4, float 17.0, ptr @G }
”,其中 “@G
” 声明为 “@G = external global i32
”。 结构体常量必须具有 结构体类型,并且元素的数量和类型必须与类型指定的匹配。- 数组常量
数组常量用类似于数组类型定义的符号表示(以逗号分隔的元素列表,用方括号 (
[]
) 包围)。 例如:“[ i32 42, i32 11, i32 74 ]
”。 数组常量必须具有 数组类型,并且元素的数量和类型必须与类型指定的匹配。 作为特例,字符数组常量也可以使用带有c
前缀的双引号字符串表示。 例如:“c"Hello World\0A\00"
”。- 向量常量
向量常量用类似于向量类型定义的符号表示(以逗号分隔的元素列表,用小于号/大于号 (
<>
) 包围)。 例如:“< i32 42, i32 11, i32 74, i32 100 >
”。 向量常量必须具有 向量类型,并且元素的数量和类型必须与类型指定的匹配。当创建元素具有相同常量值的向量时,首选语法是
splat (<Ty> Val)
。 例如:“splat (i32 11)
”。 这些向量常量必须具有 向量类型,其元素类型与splat
操作数匹配。- 零初始化
字符串 '
zeroinitializer
' 可用于将任何类型(包括标量和 聚合 类型)的值零初始化为零。 这通常用于避免打印大型零初始化器(例如,对于大型数组),并且始终与使用显式零初始化器完全等效。- 元数据节点
元数据节点是没有类型的常量元组。 例如:“
!{!0, !{!2, !0}, !"test"}
”。 元数据可以引用常量值,例如:“!{!0, i32 0, ptr @global, ptr @function, !"str"}
”。 与旨在解释为指令流一部分的其他类型化常量不同,元数据是附加额外信息(例如调试信息)的位置。
全局变量和函数地址¶
全局变量 和 函数 的地址始终是隐式有效的(链接时)常量。 当使用 全局变量的标识符 时,这些常量会被显式引用,并且始终具有 指针 类型。 例如,以下是一个合法的 LLVM 文件
@X = global i32 17
@Y = global i32 42
@Z = global [2 x ptr] [ ptr @X, ptr @Y ]
未定义值¶
字符串 'undef
' 可以在任何期望常量的地方使用,并指示值的用户可能会收到未指定的位模式。 未定义值可以是任何类型(除了 'label
' 或 'void
'),并且可以在任何允许常量的地方使用。
注意
只要有可能,就应该使用 'poison
' 值(在下一节中描述)来代替 'undef
'。 毒化值比未定义值更强,并且可以实现更多优化。 仅仅 'undef
' 的存在就会阻止某些优化(请参阅下面的示例)。
未定义值很有用,因为它们向编译器指示程序是良好定义的,无论使用什么值。 这使编译器有更多的自由来优化。 以下是一些有效(在伪 IR 中)的(可能令人惊讶的)转换示例
%A = add %X, undef
%B = sub %X, undef
%C = xor %X, undef
Safe:
%A = undef
%B = undef
%C = undef
这是安全的,因为所有输出位都受到未定义位的影响。 任何输出位都可以根据输入位为零或一。
%A = or %X, undef
%B = and %X, undef
Safe:
%A = -1
%B = 0
Safe:
%A = %X ;; By choosing undef as 0
%B = %X ;; By choosing undef as -1
Unsafe:
%A = undef
%B = undef
这些逻辑运算具有并非始终受输入影响的位。 例如,如果 %X
具有零位,那么 'and
' 运算的输出对于该位始终为零,无论来自 'undef
' 的对应位是什么。 因此,假设 'and
' 的结果是 'undef
' 进行优化或假设是不安全的。 但是,可以安全地假设 'undef
' 的所有位都可能是 0,并将 'and
' 优化为 0。 同样,可以安全地假设 'undef
' 操作数到 'or
' 的所有位都可以设置,从而允许将 'or
' 折叠为 -1。
%A = select undef, %X, %Y
%B = select undef, 42, %Y
%C = select %X, %Y, undef
Safe:
%A = %X (or %Y)
%B = 42 (or %Y)
%C = %Y (if %Y is provably not poison; unsafe otherwise)
Unsafe:
%A = undef
%B = undef
%C = undef
这组示例表明,未定义的 'select
' 条件可以双向进行,但它们必须来自两个操作数之一。 在 %A
示例中,如果已知 %X
和 %Y
都具有清除的低位,则 %A
必须具有清除的低位。 但是,在 %C
示例中,如果 %Y
可证明不是 'poison
',则优化器可以假设 'undef
' 操作数可能与 %Y
相同,从而允许消除整个 'select
'。 这是因为 'poison
' 比 'undef
' 更强。
%A = xor undef, undef
%B = undef
%C = xor %B, %B
%D = undef
%E = icmp slt %D, 4
%F = icmp gte %D, 4
Safe:
%A = undef
%B = undef
%C = undef
%D = undef
%E = undef
%F = undef
此示例指出,两个 'undef
' 操作数不一定相同。 这可能会让人感到惊讶(并且也符合 C 语义),他们在那里假设 “X^X
” 始终为零,即使 X
是未定义的。 这在很多原因上是不正确的,但简短的回答是 'undef
' “变量” 可能会在其 “生命周期” 内任意更改其值。 这是正确的,因为该变量实际上没有生命周期。 相反,该值在逻辑上是从需要时碰巧存在的任意寄存器中读取的,因此该值不一定随时间推移保持一致。 实际上,%A
和 %C
需要具有相同的语义,否则核心 LLVM “用所有用途替换” 的概念将不成立。
为了确保给定寄存器的所有用途都观察到相同的值(即使是 'undef
'),可以使用 freeze 指令。
%A = sdiv undef, %X
%B = sdiv %X, undef
Safe:
%A = 0
b: unreachable
这些示例显示了未定义值和未定义行为之间的关键区别。 未定义值(如 'undef
')允许具有任意位模式。 这意味着 %A
操作可以常量折叠为 '0
',因为 'undef
' 可能是零,并且零除以任何值都是零。 但是,在第二个示例中,我们可以做出更积极的假设:因为允许 'undef
' 是任意值,所以我们允许假设它可能是零。 由于除以零具有未定义行为,因此我们允许假设该操作根本不执行。 这使我们能够删除除法及其后的所有代码。 因为未定义的操作“不可能发生”,所以优化器可以假设它发生在死代码中。
a: store undef -> %X
b: store %X -> undef
Safe:
a: <deleted> (if the stored value in %X is provably not poison)
b: unreachable
可以假设存储未定义值没有任何效果; 我们可以假设该值被碰巧与已存在的值匹配的位覆盖。 仅当存储的值可证明不是 poison
时,此论点才有效。 但是,存储到未定义位置可能会损坏任意内存,因此,它具有未定义行为。
在未定义值上分支是未定义行为。 这解释了依赖于分支条件来构造谓词的优化,例如相关值传播和全局值编号。 在 switch 指令的情况下,分支条件应被冻结,否则它是未定义行为。
Unsafe:
br undef, BB1, BB2 ; UB
%X = and i32 undef, 255
switch %X, label %ret [ .. ] ; UB
store undef, ptr %ptr
%X = load ptr %ptr ; %X is undef
switch i8 %X, label %ret [ .. ] ; UB
Safe:
%X = or i8 undef, 255 ; always 255
switch i8 %X, label %ret [ .. ] ; Well-defined
%X = freeze i1 undef
br %X, BB1, BB2 ; Well-defined (non-deterministic jump)
毒化值¶
毒化值是错误操作的结果。 为了方便推测执行,许多指令在提供非法操作数时不会立即调用未定义行为,而是返回毒化值。 字符串 'poison
' 可以在任何期望常量的地方使用,并且诸如带有 nsw
标志的 add 之类的操作可以产生毒化值。
当指令的参数之一是 'poison
' 时,大多数指令都会返回 'poison
'。 一个值得注意的例外是 select 指令。 毒化值的传播可以通过 freeze 指令 停止。
用 未定义值 或该类型的任何值替换毒化值是正确的。
这意味着如果毒化值用作指令操作数,而该操作数具有任何触发未定义行为的值,则会立即发生未定义行为。 值得注意的是,这包括(但不限于)
以下是一些示例
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, ptr @h, i32 %still_poison
store i32 0, ptr %poison_yet_again ; Undefined behavior due to
; store to poison.
store i32 %poison, ptr @g ; Poison value stored to memory.
%poison3 = load i32, ptr @g ; Poison value loaded back from memory.
%poison4 = load i16, ptr @g ; Returns a poison value.
%poison5 = load i64, ptr @g ; Returns a poison value.
%cmp = icmp slt i32 %poison, 0 ; Returns a poison value.
br i1 %cmp, label %end, label %end ; undefined behavior
end:
良定义值¶
给定程序执行,如果值在执行中没有未定义位且不是毒化值,则该值是良定义的。 如果聚合值或向量的元素是良定义的,则聚合值或向量是良定义的。 聚合的填充不被考虑,因为它在不将其存储到内存中并使用不同类型加载它的情况下是不可见的。
如果 单值、非向量类型的常量既不是 'undef
' 常量也不是 'poison
' 常量,则是良定义的。 freeze 指令 的结果是良定义的,与其操作数无关。
基本块的地址¶
blockaddress(@function, %block)
'blockaddress
' 常量计算指定函数中指定基本块的地址。
它始终具有 ptr addrspace(P)
类型,其中 P
是包含 %block
的函数的地址空间(通常是 addrspace(0)
)。
获取入口块的地址是非法的。
此值仅在用作 ‘indirectbr’ 的操作数或与 null 进行比较时,才具有已定义的行为。标签地址之间的指针相等性测试会导致未定义的行为,但与 null 进行比较是可以的,并且没有标签等于空指针。只要不对这些位进行检查,就可以将其作为不透明的指针大小的值传递。这允许在这些值上执行 ptrtoint
和算术运算,只要在 indirectbr
指令之前重新构成原始值即可。
最后,某些目标可能在使用该值作为内联汇编的操作数时提供已定义的语义,但这取决于具体的目标。
DSO 本地等效项¶
dso_local_equivalent @func
‘dso_local_equivalent
’ 常量表示一个函数,该函数在功能上等同于给定的函数,但始终在当前的链接单元中定义。生成的指针与底层函数具有相同的类型。允许(但不是必需)生成的指针与函数指针不同,并且它在不同的翻译单元中可能具有不同的值。
目标函数不能具有 extern_weak
链接。
dso_local_equivalent
可以如下实现:
如果函数具有本地链接、隐藏可见性或
dso_local
,则dso_local_equivalent
可以简单地实现为指向该函数的指针。dso_local_equivalent
可以通过一个尾调用函数的桩来实现。许多目标支持重定位,这些重定位在链接时解析为函数或其桩,具体取决于函数是否在链接单元中定义;LLVM 将在可用时使用此方法。(这通常称为 “PLT 桩”。)在其他目标上,可能需要显式发出桩。
这可以用于任何需要函数的 dso_local
实例的场合,而无需显式地使原始函数成为 dso_local
。一个可以使用此功能的实例是函数和某些其他 dso_local
符号之间的静态偏移计算。这对于 Relative VTables C++ ABI 尤其有用,其中 VTable 中函数指针的动态重定位可以替换为 VTable 和虚拟函数之间偏移的静态重定位,而虚拟函数可能不是 dso_local
。
目前仅 ELF 二进制格式支持此功能。
无 CFI¶
no_cfi @func
使用 控制流完整性 (CFI),‘no_cfi
’ 常量表示一个函数引用,该引用在 LowerTypeTests
传递中不会被替换为 CFI 跳转表的引用。这些常量在低级程序(如操作系统内核)中可能很有用,这些程序需要引用实际的函数体。
指针身份验证常量¶
ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC]?]?)
‘ptrauth
’ 常量表示一个指针,该指针在某些位中嵌入了加密身份验证签名,如 指针身份验证 文档中所述。
‘ptrauth
’ 常量只是一个常量,等效于 llvm.ptrauth.sign
intrinsic,如果需要,可以由鉴别符 llvm.ptrauth.blend
提供。
它的类型与第一个参数相同。可以可选地指定整数常量鉴别符和地址鉴别符。否则,它们的值为 i64 0
和 ptr null
。
如果地址鉴别符为 null
,则表达式等效于:
%tmp = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 DISC)
%val = inttoptr i64 %tmp to ptr
否则,表达式等效于:
%tmp1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr ADDRDISC to i64), i64 DISC)
%tmp2 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 %tmp1)
%val = inttoptr i64 %tmp2 to ptr
常量表达式¶
常量表达式用于允许将涉及其他常量的表达式用作常量。常量表达式可以是任何 一级 类型,并且可以涉及任何没有副作用的 LLVM 操作(例如,不支持 load 和 call)。以下是常量表达式的语法:
trunc (CST to TYPE)
对常量执行 trunc 操作。
ptrtoint (CST to TYPE)
对常量执行 ptrtoint 操作。
inttoptr (CST to TYPE)
对常量执行 inttoptr 操作。这一个真的很危险!
bitcast (CST to TYPE)
将常量 CST 转换为另一种 TYPE。操作数的约束与 bitcast 指令 的约束相同。
addrspacecast (CST to TYPE)
将常量指针或常量指针向量 CST 转换为不同地址空间中的另一种 TYPE。操作数的约束与 addrspacecast 指令 的约束相同。
getelementptr (TY, CSTPTR, IDX0, IDX1, ...)
,getelementptr inbounds (TY, CSTPTR, IDX0, IDX1, ...)
对常量执行 getelementptr 操作。与 getelementptr 指令一样,索引列表可以有一个或多个索引,这些索引需要对 “指向 TY 的指针” 类型有意义。这些索引可以隐式地进行符号扩展或截断,以匹配 CSTPTR 地址空间的索引大小。
extractelement (VAL, IDX)
对常量执行 extractelement 操作。
insertelement (VAL, ELT, IDX)
对常量执行 insertelement 操作。
shufflevector (VEC1, VEC2, IDXMASK)
对常量执行 shufflevector 操作。
add (LHS, RHS)
对常量执行加法运算。
sub (LHS, RHS)
对常量执行减法运算。
xor (LHS, RHS)
对常量执行按位异或运算。
其他值¶
内联汇编器表达式¶
LLVM 通过使用特殊值来支持内联汇编器表达式(与 模块级内联汇编 相反)。此值将内联汇编器表示为模板字符串(包含要发出的指令)、操作数约束列表(存储为字符串)、一个指示内联汇编表达式是否具有副作用的标志,以及一个指示包含汇编的函数是否需要保守地对齐其堆栈的标志。
模板字符串支持使用 “$
” 后跟数字的操作数参数替换,以指示替换给定的寄存器/内存位置,如约束字符串所指定。“${NUM:MODIFIER}
” 也可以使用,其中 MODIFIER
是一个目标特定的注释,用于说明如何打印操作数(请参阅 Asm 模板参数修饰符)。
可以通过在模板中使用 “$$
” 来包含字面量 “$
”。要将其他特殊字符包含到输出中,可以使用常用的 “\XX
” 转义符,就像在其他字符串中一样。请注意,在模板替换之后,除非禁用,否则生成的汇编字符串将由 LLVM 的集成汇编器解析——即使在发出 .s
文件时也是如此——因此必须包含 LLVM 已知的汇编语法。
LLVM 还支持一些对于编写内联汇编更有用的替换:
${:uid}
:展开为对于此内联汇编代码块唯一的十进制整数。当声明本地标签时,此替换很有用。许多标准编译器优化(如内联)可能会复制内联汇编代码块。添加代码块唯一标识符可确保两个标签在汇编期间不会冲突。这用于实现 GCC 的 %= 特殊格式字符串。${:comment}
:展开为当前目标汇编方言的注释字符。这通常是#
,但许多目标使用其他字符串,例如;
、//
或!
。${:private}
:展开为汇编器私有标签前缀。具有此前缀的标签不会出现在已汇编对象文件的符号表中。通常前缀是L
,但目标可以使用其他字符串。.L
相对流行。
LLVM 对内联汇编的支持在很大程度上以 Clang 的 GCC 兼容内联汇编支持的要求为模型。因此,此处列出的功能集以及约束和修饰符代码与 GCC 的内联汇编支持中的类似或相同。但是,需要明确的是,此处描述的模板和约束字符串的语法与 GCC 和 Clang 接受的语法不相同,并且,虽然大多数约束字母由 Clang 按原样传递,但在从 C 源代码转换为 LLVM 汇编时,某些约束字母会被转换为其他代码。
内联汇编器表达式的示例是:
i32 (i32) asm "bswap $0", "=r,r"
内联汇编器表达式仅可用作 call 或 invoke 指令的被调用者操作数。因此,通常我们有:
%X = call i32 asm "bswap $0", "=r,r"(i32 %Y)
约束列表中不可见的副作用的内联汇编必须标记为具有副作用。这通过使用 ‘sideeffect
’ 关键字来完成,如下所示:
call void asm sideeffect "eieio", ""()
在某些情况下,内联汇编将包含一些代码,这些代码除非堆栈以某种方式对齐(例如 x86 上的调用或 SSE 指令),否则将无法工作,但不会包含在汇编中进行对齐的代码。编译器应该对汇编可能包含的内容做出保守的假设,并且如果存在 ‘alignstack
’ 关键字,则应在其序言中生成其通常的堆栈对齐代码:
call void asm alignstack "eieio", ""()
内联汇编还支持使用非标准汇编方言。假定的方言是 ATT。当存在 ‘inteldialect
’ 关键字时,内联汇编正在使用 Intel 方言。目前,ATT 和 Intel 是唯一支持的方言。一个例子是:
call void asm inteldialect "eieio", ""()
如果内联汇编可能会展开堆栈,则必须使用 ‘unwind
’ 关键字,以便编译器发出展开信息:
call void asm unwind "call func", ""()
如果内联汇编展开堆栈但未标记 ‘unwind
’ 关键字,则行为是未定义的。
如果出现多个关键字,则 ‘sideeffect
’ 关键字必须首先出现,‘alignstack
’ 关键字第二,‘inteldialect
’ 关键字第三,‘unwind
’ 关键字最后。
内联汇编约束字符串¶
约束列表是一个逗号分隔的字符串,每个元素包含一个或多个约束代码。
对于约束列表中的每个元素,将选择适当的寄存器或内存操作数,并使其可用于汇编模板字符串扩展,第一个约束在列表中为 $0
,第二个为 $1
,依此类推。
有三种不同类型的约束,它们通过约束代码前面的前缀符号来区分:输出、输入和损坏。约束必须始终按该顺序给出:先输出,然后输入,然后损坏。它们不能混杂在一起。
还有三种不同类别的约束代码:
寄存器约束。这可以是寄存器类,也可以是固定的物理寄存器。这种约束将分配一个寄存器,并在必要时将参数或结果位转换为适当的类型。
内存约束。这种约束用于使用内存操作数的指令。不同的约束允许目标使用的不同寻址模式。
立即值约束。这种约束用于整数或其他可以直接呈现到指令中的立即值。各种目标特定的约束允许选择适合您要使用的指令范围内的值。
输出约束¶
输出约束由 “=
” 前缀指定(例如 “=r
”)。这表示汇编将写入此操作数,然后该操作数将作为 asm
表达式的返回值提供。输出约束不消耗来自调用指令的参数。(除非,请参阅下面关于间接输出的内容)。
通常,期望在读取所有输入之前,汇编表达式不会写入任何输出位置。因此,LLVM 可能会将相同的寄存器分配给输出和输入。如果这不安全(例如,如果汇编包含两条指令,其中第一条写入一个输出,第二条读取一个输入并写入第二个输出),则必须使用 “&
” 修饰符(例如 “=&r
”)来指定输出是 “提前损坏” 输出。将输出标记为 “提前损坏” 可确保 LLVM 不会将相同的寄存器用于任何输入(除了绑定到此输出的输入)。
输入约束¶
输入约束没有前缀——只有约束代码。每个输入约束将消耗来自调用指令的一个参数。汇编不允许写入任何输入寄存器或内存位置(除非该输入绑定到输出)。另请注意,如果 LLVM 可以确定它们必然都包含相同的值,则多个输入可以全部分配给相同的寄存器。
输入约束也可以通过提供整数作为约束字符串来 “绑定” 到输出约束,而不是提供约束代码。绑定的输入仍然消耗来自调用指令的参数,并在 asm 模板编号中占据一个位置,就像通常一样——它们将被约束为始终使用与其绑定的输出相同的寄存器。例如,约束字符串 “=r,0
” 表示为输出分配一个寄存器,并将该寄存器也用作输入(它是第 0 个约束)。
允许将输入绑定到 “提前损坏” 输出。在这种情况下,没有其他输入可以与绑定到提前损坏的输入共享相同的寄存器(即使另一个输入具有相同的值)。
您只能将输入绑定到具有寄存器约束的输出,而不能绑定到内存约束的输出。只有一个输入可以绑定到一个输出。
还有一个 “有趣” 的功能值得解释一下:如果寄存器类约束分配的寄存器对于作为输入提供的 value type 操作数来说太小,则输入值将被拆分为多个寄存器,并将所有寄存器传递给内联汇编。
但是,此功能通常不如您想象的那么有用。
首先,不能保证寄存器是连续的。因此,在那些具有对多个连续指令进行操作的指令的架构上,这不是支持它们的合适方法。(例如,32 位 SparcV8 有一个 64 位加载,该指令采用单个 32 位寄存器。然后硬件加载到命名寄存器和下一个寄存器中。内联汇编的此功能对于支持该功能没有用处。)
一些目标提供了一个模板字符串修饰符,允许显式访问双寄存器操作数的第二个寄存器(例如,MIPS L
、M
和 D
)。在这样的架构上,您实际上可以访问第二个分配的寄存器(但仍然不能访问任何后续寄存器)。但是,在这种情况下,您可能仍然最好简单地将值拆分为两个单独的操作数,以提高清晰度。(例如,请参阅 X86 上 A
约束的描述,尽管它仅用于此功能,但实际上并不是一个好主意)。
间接输入和输出¶
间接输出或输入约束可以通过 “*
” 修饰符指定(在输出的情况下,它位于 “=
” 之后)。这表示汇编将写入或读取作为输入参数提供的地址的内容。(请注意,通过这种方式,间接输出的行为更像输入而不是输出:就像输入一样,它们消耗调用表达式的参数,而不是产生返回值。间接输出约束仅在汇编预期写入输入内存位置的内容而不是仅从中读取内容时才是 “输出”)。
这最常用于内存约束,例如 “=*m
”,以将变量的地址作为值传递。
也可以使用间接寄存器约束,但只能用于输出(例如 “=*r
”)。这将导致 LLVM 为输出值正常分配一个寄存器,然后,在提供的内联汇编之后,单独发出一个存储到作为输入提供的地址的指令。(尚不清楚此功能提供了什么价值,与在 asm 语句之后显式写入存储相比,它只能产生更糟糕的代码,因为它绕过了许多优化过程。我建议不要使用它。)
间接约束的调用参数必须具有指针类型,并且必须指定 elementtype 属性以指示指针元素类型。
损坏约束¶
损坏约束由 “~
” 前缀指示。损坏不消耗输入操作数,也不生成输出。损坏不能使用任何通用约束代码字母——它们只能使用显式寄存器约束,例如 “~{eax}
”。一个例外是 “~{memory}
” 的损坏字符串,它表示汇编写入任意未声明的内存位置——不仅是声明的间接输出指向的内存。
请注意,损坏也出现在输出约束中的命名寄存器是不合法的。
标签约束¶
标签约束由 “!
” 前缀指示,通常以 "!i"
的形式使用。标签约束不消耗调用参数,而是消耗 callbr
指令的间接目标标签。
标签约束只能与 callbr
一起使用,并且标签约束的数量必须与 callbr
指令中的间接目标标签的数量匹配。
约束代码¶
在可能的前缀之后是约束代码或代码集。
约束代码可以是单个字母(例如 “r
”)、“^
” 字符后跟两个字母(例如 “^wc
”),或 “{
” 寄存器名称 “}
”(例如 “{eax}
”)。
单字母和双字母约束代码通常被选择为与 GCC 的约束代码相同。
单个约束可能包含一个或多个约束代码,这取决于 LLVM 选择使用哪一个。这主要为了与来自 clang 的 GCC 内联汇编的翻译兼容。
有两种方法可以指定替代方案,并且可以在内联汇编约束列表中使用其中一种或两种:
将代码相互附加,构成约束代码集。例如 “
im
” 或 “{eax}m
”。这意味着 “选择集合中的任何选项”。约束的选择是针对约束列表中的每个约束独立进行的。在约束代码集之间使用 “
|
”,创建替代方案。约束列表中的每个约束必须具有相同数量的替代集。使用此语法,将一起选择所有约束列表中相同位置的替代方案。
将它们放在一起,您可能会得到一个双操作数约束字符串,如 "rm|r,ri|rm"
。这表示如果操作数 0 是 r
或 m
,则操作数 1 可以是 r
或 i
之一。如果操作数 0 是 r
,则操作数 1 可以是 r
或 m
之一。但是,操作数 0 和 1 不能同时为 m 类型。
但是,不建议使用任何一种替代方案功能,因为 LLVM 无法智能地选择使用哪一种。(在它当前需要选择的时候,没有足够的信息以聪明的方式这样做。)因此,它只是尝试做出最有可能编译的选择,而不是最佳性能的选择。(例如,给定 “rm
”,它总是选择使用内存,而不是寄存器)。并且,如果给定多个寄存器或多个寄存器类,它将仅选择第一个。(实际上,它目前甚至不确保显式指定的物理寄存器是唯一的,因此将多个物理寄存器指定为替代方案,如 {r11}{r12},{r11}{r12}
,会将 r11 分配给两个操作数,这完全不是预期的结果。)
支持的约束代码列表¶
一般来说,约束代码的预期行为与它们在 GCC 中的行为相同。LLVM 的支持通常是根据 “按需” 的原则实现的,以支持 GCC 支持的 C 内联汇编代码。LLVM 和 GCC 之间的行为不匹配可能表示 LLVM 中存在错误。
某些约束代码通常受所有目标支持:
r
:目标通用寄存器类中的寄存器。m
:内存地址操作数。目标特定支持哪些寻址模式,典型的示例是寄存器,或寄存器 + 寄存器偏移,或寄存器 + 立即数偏移(具有某些目标特定的大小)。p
:地址操作数。类似于m
,但由 “加载地址” 类型指令使用,而无需访问内存。i
:整数常量(具有目标特定的宽度)。允许简单的立即数或可重定位的值。n
:整数常量——不包括可重定位的值。s
:具有常量偏移的符号或标签引用。X
:允许任何类型的操作数,没有任何约束。通常用于传递 asm 分支或调用的标签。{register-name}
:精确地要求命名的物理寄存器。
其他约束是目标特定的:
AArch64
z
:立即数整数 0。根据需要输出WZR
或XZR
。I
:对于ADD
或SUB
指令有效的立即数整数,即 0 到 4095,可选左移 12 位。J
:当取反时,对于ADD
或SUB
指令有效的立即数整数,即 -1 到 -4095,可选左移 12 位。K
:对于逻辑指令(如AND
、EOR
或ORR
)的 ‘bitmask immediate 32’ 有效的立即数整数,用于 32 位寄存器。L
:对于逻辑指令(如AND
、EOR
或ORR
)的 ‘bitmask immediate 64’ 有效的立即数整数,用于 64 位寄存器。M
:用于 32 位寄存器上MOV
汇编别名的立即数整数。这是K
的超集:除了位掩码立即数之外,还允许可以使用单个MOVZ
或MOVL
指令加载的立即数整数。N
:用于 64 位寄存器上MOV
汇编别名的立即数整数。这是L
的超集。Q
:内存地址操作数必须在单个寄存器中(无偏移量)。(但是,LLVM 当前也对m
约束执行此操作。)r
:32 位或 64 位整数寄存器(W* 或 X*)。S
:具有常量偏移的符号或标签引用。不支持通用的s
。Uci
:类似于 r,但仅限于寄存器 8 到 11(包括 8 和 11)。Ucj
:类似于 r,但仅限于寄存器 12 到 15(包括 12 和 15)。w
:32 位、64 位或 128 位浮点、SIMD 或 SVE 向量寄存器。x
:类似于 w,但仅限于寄存器 0 到 15(包括 0 和 15)。y
:类似于 w,但仅限于 SVE 向量寄存器 Z0 到 Z7(包括 Z0 和 Z7)。Uph
:上八个 SVE 谓词寄存器之一(P8 到 P15)。Upl
: 低八个 SVE 谓词寄存器之一 (P0 到 P7)Upa
: 任何 SVE 谓词寄存器 (P0 到 P15)
AMDGPU
r
: 32 位或 64 位整数寄存器。[0-9]v
: 32 位 VGPR 寄存器,编号 0-9。[0-9]s
: 32 位 SGPR 寄存器,编号 0-9。[0-9]a
: 32 位 AGPR 寄存器,编号 0-9。I
: -16 到 64 范围内的整数内联常量。J
: 16 位有符号整数常量。A
: 整数或浮点内联常量。B
: 32 位有符号整数常量。C
: 32 位无符号整数常量或 -16 到 64 范围内的整数内联常量。DA
: 可以拆分为两个 “A” 常量的 64 位常量。DB
: 可以拆分为两个 “B” 常量的 64 位常量。
所有 ARM 模式
Q
,Um
,Un
,Uq
,Us
,Ut
,Uv
,Uy
: 内存地址操作数。目前被视为与操作数m
相同。Te
: 偶数通用 32 位整数寄存器:r0,r2,...,r12,r14
To
: 奇数通用 32 位整数寄存器:r1,r3,...,r11
ARM 和 ARM 的 Thumb2 模式
j
: 0 到 65535 之间的立即整数 (对MOVW
有效)I
: 对数据处理指令有效的立即整数。J
: -4095 到 4095 之间的立即整数。K
: 其按位逆对于数据处理指令有效的立即整数。(可以与模板修饰符 “B
” 一起使用来打印反转值)。L
: 其否定对于数据处理指令有效的立即整数。(可以与模板修饰符 “n
” 一起使用来打印取反值)。M
: 二的幂或 0 到 32 之间的整数。N
: 无效的立即约束。O
: 无效的立即约束。r
: 通用 32 位整数寄存器 (r0-r15
)。l
: 在 Thumb2 模式下,低 32 位 GPR 寄存器 (r0-r7
)。在 ARM 模式下,与r
相同。h
: 在 Thumb2 模式下,高 32 位 GPR 寄存器 (r8-r15
)。在 ARM 模式下,无效。w
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s31
、d0-d31
或q0-q15
。t
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s31
、d0-d15
或q0-q7
。x
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s15
、d0-d7
或q0-q3
。
ARM 的 Thumb1 模式
I
: 0 到 255 之间的立即整数。J
: -255 到 -1 之间的立即整数。K
: 0 到 255 之间的立即整数,可以选择左移一定量。L
: -7 到 7 之间的立即整数。M
: 0 到 1020 之间且为 4 的倍数的立即整数。N
: 0 到 31 之间的立即整数。O
: -508 到 508 之间且为 4 的倍数的立即整数。r
: 低 32 位 GPR 寄存器 (r0-r7
)。l
: 低 32 位 GPR 寄存器 (r0-r7
)。h
: 高 GPR 寄存器 (r0-r7
)。w
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s31
、d0-d31
或q0-q15
。t
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s31
、d0-d15
或q0-q7
。x
: 32 位、64 位或 128 位浮点/SIMD 寄存器,范围分别是s0-s15
、d0-d7
或q0-q3
。
Hexagon
o
,v
: 内存地址操作数,目前被视为与约束m
相同。r
: 32 位或 64 位寄存器。
LoongArch
f
: 浮点寄存器 (如果可用)。k
: 内存操作数,其地址由基址寄存器和(可选的缩放)索引寄存器形成。l
: 有符号 16 位常量。m
: 内存操作数,其地址由基址寄存器和偏移量形成,该偏移量适用于与 st.w 和 ld.w 具有相同寻址模式的指令。I
: 有符号 12 位常量 (用于算术指令)。J
: 立即整数零。K
: 无符号 12 位常量 (用于逻辑指令)。ZB
: 保存在通用寄存器中的地址。偏移量为零。ZC
: 内存操作数,其地址由基址寄存器和偏移量形成,该偏移量适用于与 ll.w 和 sc.w 具有相同寻址模式的指令。
MSP430
r
: 8 位或 16 位寄存器。
MIPS
I
: 立即有符号 16 位整数。J
: 立即整数零。K
: 立即无符号 16 位整数。L
: 立即 32 位整数,其中低 16 位为 0。N
: -65535 到 -1 之间的立即整数。O
: 立即有符号 15 位整数。P
: 1 到 65535 之间的立即整数。m
: 内存地址操作数。在 MIPS-SE 模式下,允许基址寄存器加上 16 位立即偏移量。在 MIPS 模式下,仅允许基址寄存器。R
: 内存地址操作数。在 MIPS-SE 模式下,允许基址寄存器加上 9 位有符号偏移量。在 MIPS 模式下,与约束m
相同。ZC
: 内存地址操作数,适用于给定子目标上的pref
、ll
或sc
指令(细节有所不同)。r
,d
,y
: 32 位或 64 位 GPR 寄存器。f
: 32 位或 64 位 FPU 寄存器 (F0-F31
),或 128 位 MSA 寄存器 (W0-W31
)。对于 MSA 寄存器,建议使用w
参数修饰符以与 GCC 兼容。c
: 适用于间接跳转的 32 位或 64 位 GPR 寄存器 (始终为25
)。l
:lo
寄存器,32 位或 64 位。x
: 无效。
NVPTX
b
: 1 位整数寄存器。c
或h
: 16 位整数寄存器。r
: 32 位整数寄存器。l
或N
: 64 位整数寄存器。q
: 128 位整数寄存器。f
: 32 位浮点寄存器。d
: 64 位浮点寄存器。
PowerPC
I
: 立即有符号 16 位整数。J
: 立即无符号 16 位整数,左移 16 位。K
: 立即无符号 16 位整数。L
: 立即有符号 16 位整数,左移 16 位。M
: 大于 31 的立即整数。N
: 为 2 的精确幂的立即整数。O
: 立即整数常量 0。P
: 其否定是有符号 16 位常量的立即整数常量。es
,o
,Q
,Z
,Zy
: 内存地址操作数,目前被视为与m
相同。r
: 32 位或 64 位整数寄存器。b
: 32 位或 64 位整数寄存器,不包括R0
(即:R1-R31
)。f
: 32 位或 64 位浮点寄存器 (F0-F31
)。v
: 对于4 x f32
或4 x f64
类型,128 位 altivec 向量寄存器 (
V0-V31
)。
y
: 条件寄存器 (CR0-CR7
)。wc
: CR 寄存器中的单个 CR 位。wa
,wd
,wf
: 任何 128 位 VSX 向量寄存器,来自完整的 VSX 寄存器集(与浮点和向量寄存器文件重叠)。ws
: 32 位或 64 位浮点寄存器,来自完整的 VSX 寄存器集。
RISC-V
A
: 地址操作数(使用通用寄存器,没有偏移量)。I
: 12 位有符号整数立即操作数。J
: 零整数立即操作数。K
: 5 位无符号整数立即操作数。f
: 32 位或 64 位浮点寄存器(需要 F 或 D 扩展)。r
: 32 位或 64 位通用寄存器(取决于平台XLEN
)。S
:s
的别名。vd
: 向量寄存器,不包括v0
(需要 V 扩展)。vm
: 向量寄存器v0
(需要 V 扩展)。vr
: 向量寄存器 (需要 V 扩展)。
Sparc
I
: 立即 13 位有符号整数。r
: 32 位整数寄存器。f
: SparcV8 上的任何浮点寄存器,或 SparcV9 上寄存器 “低” 半部分的浮点寄存器。e
: 任何浮点寄存器。(在 SparcV8 上与f
相同。)
SystemZ
I
: 立即无符号 8 位整数。J
: 立即无符号 12 位整数。K
: 立即有符号 16 位整数。L
: 立即有符号 20 位整数。M
: 立即整数 0x7fffffff。Q
: 具有基址和 12 位立即无符号位移的内存地址操作数。R
: 具有基址、12 位立即无符号位移和索引寄存器的内存地址操作数。S
: 具有基址和 20 位立即有符号位移的内存地址操作数。T
: 具有基址、20 位立即有符号位移和索引寄存器的内存地址操作数。r
或d
: 32 位、64 位或 128 位整数寄存器。a
: 32 位、64 位或 128 位整数地址寄存器(不包括 R0,在地址上下文中 R0 评估为零)。h
: 64 位数据寄存器高位部分的 32 位值 (LLVM 特有)f
: 32 位、64 位或 128 位浮点寄存器。
X86
I
: 0 到 31 之间的立即整数。J
: 0 到 64 之间的立即整数。K
: 立即有符号 8 位整数。L
: 立即整数,0xff 或 0xffff 或 (仅在 64 位模式下) 0xffffffff。M
: 0 到 3 之间的立即整数。N
: 立即无符号 8 位整数。O
: 0 到 127 之间的立即整数。e
: 立即 32 位有符号整数。Z
: 立即 32 位无符号整数。q
: 可以作为 8 位l
整数寄存器访问的 8 位、16 位、32 位或 64 位寄存器。在 X86-32 上,这是a
、b
、c
和d
寄存器,而在 X86-64 上,它是所有整数寄存器。当 egpr 和 inline-asm-use-gpr32 功能都开启时,它将扩展到 gpr32。Q
: 可以作为 8 位h
整数寄存器访问的 8 位、16 位、32 位或 64 位寄存器。这是a
、b
、c
和d
寄存器。r
或l
: 8 位、16 位、32 位或 64 位整数寄存器。当 egpr 和 inline-asm-use-gpr32 功能都开启时,它将扩展到 gpr32。R
: 8 位、16 位、32 位或 64 位 “legacy” 整数寄存器 – 自 i386 以来就存在的寄存器,并且可以在没有 REX 前缀的情况下访问。f
: 32 位、64 位或 80 位 ‘387 FPU 堆栈伪寄存器。y
: 64 位 MMX 寄存器,如果启用了 MMX。v
: 如果启用了 SSE:SSE 寄存器中的 32 位或 64 位标量操作数,或 128 位向量操作数。如果也启用了 AVX,也可以是 AVX 寄存器中的 256 位向量操作数。如果也启用了 AVX-512,也可以是 AVX512 寄存器中的 512 位向量操作数。否则,会报错。Ws
: 带有可选常量加数或标签引用的符号引用。x
: 与v
相同,但当启用 AVX-512 时,x
代码仅分配到前 16 个 AVX-512 寄存器,而v
代码分配到任何 32 个 AVX-512 寄存器。Y
: 与x
相同,如果启用了 SSE2,否则会报错。A
: 特殊情况:首先分配 EAX,然后分配 EDX,用于单个操作数(在 32 位模式下,64 位整数操作数将被拆分为两个寄存器)。不建议使用此约束,因为在 64 位模式下,64 位操作数将仅分配给 RAX – 如果需要两个 32 位操作数,您最好在将其传递给 asm 语句之前自行拆分。jr
: 8 位、16 位、32 位或 64 位整数 gpr16。当 egpr 或 inline-asm-use-gpr32 功能开启时,它不会扩展到 gpr32。jR
: 当 egpr 功能开启时,8 位、16 位、32 位或 64 位整数 gpr32。否则,与r
相同。
XCore
r
: 32 位整数寄存器。
Asm 模板参数修饰符¶
在 asm 模板字符串中,修饰符可以用于操作数引用,例如 “${0:n}
”。
一般来说,修饰符的预期行为与它们在 GCC 中的行为相同。LLVM 的支持通常基于 “按需” 原则实现,以支持 GCC 支持的 C 内联汇编代码。LLVM 和 GCC 之间行为的不匹配可能表明 LLVM 中存在错误。
目标无关
c
: 打印未修饰的立即整数常量,不带目标特定的立即标点符号(例如,没有$
前缀)。n
: 取反并打印未修饰的立即整数常量,不带目标特定的立即标点符号(例如,没有$
前缀)。l
: 打印为未修饰的标签,不带目标特定的标签标点符号(例如,没有$
前缀)。
AArch64
w
: 使用w*
名称而不是x*
名称打印 GPR 寄存器。例如,代替x30
,打印w30
。x
: 使用x*
名称打印 GPR 寄存器。(无论如何,这是默认值)。b
,h
,s
,d
,q
: 使用b*
、h*
、s*
、d*
或q*
名称而不是默认的v*
名称打印浮点/SIMD 寄存器。
AMDGPU
r
: 无效果。
ARM
a
: 将操作数打印为地址(用[
和]
包围寄存器)。P
: 无效果。q
: 无效果。y
: 将 VFP 单精度寄存器打印为索引双精度寄存器(例如,打印为d4[1]
而不是s9
)B
: 按位反转并打印未带#
前缀的立即整数常量。L
: 打印立即整数常量的低 16 位。M
: 打印适用于 ldm/stm 的寄存器集。也打印指定操作数之后所有寄存器操作数 (!),因此请谨慎使用。Q
: 打印寄存器对的低位寄存器,或双寄存器操作数的低位寄存器。R
: 打印寄存器对的高位寄存器,或双寄存器操作数的高位寄存器。H
: 打印寄存器对的第二个寄存器。(在 big-endian 系统上,H
等效于Q
,而在 little-endian 系统上,H
等效于R
。)e
: 打印 NEON 四字寄存器的低双字寄存器。f
: 打印 NEON 四字寄存器的高双字寄存器。m
: 打印内存操作数的基址寄存器,不带[
和]
修饰。
Hexagon
L
: 打印双寄存器操作数的第二个寄存器。要求它已与第一个寄存器连续分配。I
: 如果操作数是整数常量,则打印字母 ‘i’,否则不打印任何内容。用于打印 ‘addi’ 与 ‘add’ 指令。
LoongArch
u
: 打印 LASX 寄存器。w
: 打印 LSX 寄存器。z
: 如果操作数为零,则打印 $zero 寄存器,否则正常打印。
MSP430
无其他修饰符。
MIPS
X
: 将立即整数打印为十六进制x
: 将立即整数的低 16 位打印为十六进制。d
: 将立即整数打印为十进制。m
: 减一并将立即整数打印为十进制。z
: 如果是立即零,则打印 $0,否则正常打印。L
: 打印双寄存器操作数的低位寄存器,或打印双字内存操作数的低位字的地址。M
: 打印双寄存器操作数的高位寄存器,或打印双字内存操作数的高位字的地址。D
: 打印双寄存器操作数的第二个寄存器,或打印双字内存操作数的第二个字。(在 big-endian 系统上,D
等效于L
,而在 little-endian 系统上,D
等效于M
。)w
: 无效果。为与 GCC 兼容而提供,GCC 需要此修饰符才能使用f
约束打印 MSA 寄存器 (W0-W31
)。
NVPTX
r
: 无效果。
PowerPC
L
: 打印双寄存器操作数的第二个寄存器。要求它已与第一个寄存器连续分配。I
: 如果操作数是整数常量,则打印字母 ‘i’,否则不打印任何内容。用于打印 ‘addi’ 与 ‘add’ 指令。y
: 对于内存操作数,打印双寄存器 X 形式指令的格式化程序。(当前始终打印r0,OPERAND
)。U
: 如果内存操作数是更新形式,则打印 ‘u’,否则不打印任何内容。(注意:LLVM 不支持更新形式,因此目前将始终不打印任何内容)X
: 如果内存操作数是索引形式,则打印 ‘x’。(注意:LLVM 不支持索引形式,因此目前将始终不打印任何内容)
RISC-V
i
: 如果操作数不是寄存器,则打印字母 ‘i’,否则不打印任何内容。用于打印 ‘addi’ 与 ‘add’ 指令等。z
: 如果是立即零,则打印寄存器zero
,否则正常打印。
Sparc
L
: 打印双寄存器操作数的低位寄存器。H
: 打印双寄存器操作数的高位寄存器。r
: 无效果。
SystemZ
SystemZ 仅实现 n
,并且不支持任何其他目标无关的修饰符。
X86
c
: 打印未修饰的整数或符号名称。(后者是此通常与目标无关的修饰符的目标特定行为)。A
:打印寄存器名称,并在其前面加上 ‘*
’。b
:打印 8 位寄存器名称(例如al
);对内存操作数不执行任何操作。h
:打印高 8 位寄存器名称(例如ah
);对内存操作数不执行任何操作。w
:打印 16 位寄存器名称(例如ax
);对内存操作数不执行任何操作。k
:打印 32 位寄存器名称(例如eax
);对内存操作数不执行任何操作。q
:如果 64 位寄存器可用,则打印 64 位寄存器名称(例如rax
),否则打印 32 位寄存器名称;对内存操作数不执行任何操作。n
:取反并打印未修饰的整数,或者,对于立即整数以外的操作数(例如可重定位的符号表达式),在操作数前打印 ‘-’。(可重定位的符号表达式的行为是特定于目标的行为,通常用于此目标无关的修饰符)H
:打印带有附加偏移量 +8 的内存引用。p
:打印原始符号名称(不带特定于语法的前缀)。P
:打印用作 call 指令参数或与显式基址寄存器和索引寄存器一起用作其偏移量的内存引用。因此它不能使用额外的寄存器来表示内存引用。(例如,省略(rip)
,即使它是 PC 相对的。)
XCore
无其他修饰符。
内联汇编元数据¶
包装内联汇编节点的 call 指令可能附加了一个 “!srcloc
” MDNode,其中包含常量整数列表。 如果存在,代码生成器将在通过 LLVMContext
错误报告机制报告错误时,将该整数用作位置 cookie 值。 这允许前端将内联汇编中发生的后端错误与生成它的源代码相关联。 例如
call void asm sideeffect "something bad", ""(), !srcloc !42
...
!42 = !{ i64 1234567 }
由前端来理解它放置在 IR 中的幻数。 如果 MDNode 包含多个常量,则代码生成器将使用与发生错误的汇编行对应的常量。
元数据¶
LLVM IR 允许将元数据附加到程序中的指令和全局对象,这些元数据可以向优化器和代码生成器传达有关代码的额外信息。
有两种元数据原语:字符串和节点。 还有一些特殊的节点,它们具有独特的名称和一组命名的参数。
注意
元数据的一个示例应用是源级调试信息,目前它是专门节点的唯一用户。
元数据没有类型,也不是值。
可以使用语法 ‘<type> <value>
’ 在元数据上下文中使用非 metadata
类型的值。
所有其他元数据在语法中都以感叹号(‘!
’)开头标识。
通过使用 metadata
类型,元数据可以在以下值上下文中使用
某些内在函数的参数,如其规范中所述。
catchpad
/cleanuppad
指令的参数。
注意
元数据可以“包装”在 MetadataAsValue
中,以便可以在值上下文中引用它:MetadataAsValue
是 Value
的一种。
类型化的值可以“包装”在 ValueAsMetadata
中,以便可以在元数据上下文中引用它:ValueAsMetadata
是 Metadata
的一种。
没有用于 ValueAsMetadata
的显式语法,而是使用类型标识符不能以感叹号开头这一事实来消除歧义。
metadata
类型暗示 MetadataAsValue
,当后跟 ‘<type> <value>
’ 对时,它将类型化的值包装在 ValueAsMetadata
中。
例如,此调用的第一个参数是 MetadataAsValue(ValueAsMetadata(Value))
call void @llvm.foo(metadata i32 1)
而此调用的第一个参数是 MetadataAsValue(MDNode)
call void @llvm.foo(metadata !0)
此 MDTuple
的第一个元素是 MDNode
!{!0}
而此 MDTuple
的第一个元素是 ValueAsMetadata(Value)
!{i32 1}
元数据字符串 (MDString
)¶
元数据字符串是由双引号包围的字符串。 它可以通过使用 “\xx
” 转义非打印字符来包含任何字符,其中 “xx
” 是两位十六进制代码。 例如:“!"test\00"
”。
注意
元数据字符串是元数据,但不是元数据节点。
元数据节点 (MDNode
)¶
元数据元组以类似于结构常量的表示法表示:一个逗号分隔的元素列表,用大括号括起来,前面加上感叹号。 元数据节点的运算对象可以是任何值。 例如
!{!"test\00", i32 10}
未唯一化的元数据节点使用 distinct
关键字。 例如
!0 = distinct !{!"test\00", i32 10}
distinct
节点在不应基于其内容合并节点时很有用。 当转换导致元数据运算对象更改时,它们也可能发生唯一化冲突。
命名元数据是元数据节点的集合,可以在模块符号表中查找。 例如
!foo = !{!4, !3}
元数据可以用作函数参数。 这里 llvm.dbg.value
intrinsic 正在使用三个元数据参数
call void @llvm.dbg.value(metadata !24, metadata !25, metadata !26)
元数据可以附加到指令。 这里元数据 !21
使用 !dbg
标识符附加到 add
指令
%indvar.next = add i64 %indvar, 1, !dbg !21
指令可能没有具有相同标识符的多个元数据附件。
元数据也可以附加到函数或全局变量。 这里元数据 !22
使用 !dbg
标识符附加到 f1
和 f2
函数以及全局变量 g1
和 g2
declare !dbg !22 void @f1()
define void @f2() !dbg !22 {
ret void
}
@g1 = global i32 0, !dbg !22
@g2 = external global i32, !dbg !22
与指令不同,全局对象(函数和全局变量)可能具有具有相同标识符的多个元数据附件。
转换需要删除它不知道或知道它无法保留的任何元数据附件。 目前,全局变量的元数据附件 !func_sanitize
、!type
、!absolute_symbol
和 !associated
存在例外,除非全局变量本身被删除,否则不能无条件删除它们。
使用命名元数据附加到模块的元数据可能不会被删除,调试元数据(名称为 !llvm.dbg.*
的命名元数据)除外。
有关优化器和代码生成器识别的特定元数据节点的更多信息,请参见下文。
专用元数据节点¶
专用元数据节点是元数据中的自定义数据结构(与通用元组相反)。 它们的字段被标记,并且可以以任何顺序指定。
这些本质上不是以调试信息为中心的,但目前所有专用元数据节点都与调试信息相关。
DICompileUnit¶
DICompileUnit
节点表示编译单元。 enums:
、retainedTypes:
、globals:
、imports:
和 macros:
字段是包含要与编译单元一起发出的调试信息的元组,而与代码优化无关(某些节点仅在指令引用它们时才发出)。 debugInfoForProfiling:
字段是一个布尔值,指示是否更新行表鉴别器以提供更准确的调试信息以用于分析结果。
!0 = !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang",
isOptimized: true, flags: "-O2", runtimeVersion: 2,
splitDebugFilename: "abc.debug", emissionKind: FullDebug,
enums: !2, retainedTypes: !3, globals: !4, imports: !5,
macros: !6, dwoId: 0x0abcd)
编译单元描述符为在特定编译单元中声明的对象提供根作用域。 文件描述符是使用此作用域定义的。 这些描述符由命名元数据节点 !llvm.dbg.cu
收集。 它们跟踪全局变量、类型信息和导入的实体(声明和命名空间)。
DIFile¶
DIFile
节点表示文件。 filename:
可以包含斜杠。
!0 = !DIFile(filename: "path/to/file", directory: "/path/to/dir",
checksumkind: CSK_MD5,
checksum: "000102030405060708090a0b0c0d0e0f")
文件有时在 scope:
字段中使用,并且是 file:
字段的唯一有效目标。
checksum:
和 checksumkind:
字段是可选的。 如果存在其中一个字段,则另一个字段也必须存在。 checksumkind:
字段的有效值是:{CSK_MD5, CSK_SHA1, CSK_SHA256}
DIBasicType¶
DIBasicType
节点表示原始类型,例如 int
、bool
和 float
。 tag:
默认为 DW_TAG_base_type
。
!0 = !DIBasicType(name: "unsigned char", size: 8, align: 8,
encoding: DW_ATE_unsigned_char)
!1 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "decltype(nullptr)")
encoding:
描述了类型的详细信息。 通常它是以下之一
DW_ATE_address = 1
DW_ATE_boolean = 2
DW_ATE_float = 4
DW_ATE_signed = 5
DW_ATE_signed_char = 6
DW_ATE_unsigned = 7
DW_ATE_unsigned_char = 8
DISubroutineType¶
DISubroutineType
节点表示子例程类型。 它们的 types:
字段引用一个元组; 第一个运算对象是返回类型,其余的是形式参数的类型(按顺序)。 如果第一个运算对象是 null
,则表示没有返回值的函数(例如 C++ 中的 void foo() {}
)。
!0 = !BasicType(name: "int", size: 32, align: 32, DW_ATE_signed)
!1 = !BasicType(name: "char", size: 8, align: 8, DW_ATE_signed_char)
!2 = !DISubroutineType(types: !{null, !0, !1}) ; void (int, char)
DIDerivedType¶
DIDerivedType
节点表示从其他类型派生的类型,例如限定类型。
!0 = !DIBasicType(name: "unsigned char", size: 8, align: 8,
encoding: DW_ATE_unsigned_char)
!1 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !0, size: 32,
align: 32)
以下 tag:
值有效
DW_TAG_member = 13
DW_TAG_pointer_type = 15
DW_TAG_reference_type = 16
DW_TAG_typedef = 22
DW_TAG_inheritance = 28
DW_TAG_ptr_to_member_type = 31
DW_TAG_const_type = 38
DW_TAG_friend = 42
DW_TAG_volatile_type = 53
DW_TAG_restrict_type = 55
DW_TAG_atomic_type = 71
DW_TAG_immutable_type = 75
DW_TAG_member
用于定义 复合类型 的成员。 成员的类型是 baseType:
。 offset:
是成员的位偏移量。 如果复合类型具有 ODR identifier:
并且未设置 flags: DIFwdDecl
,则成员仅基于其 name:
和 scope:
进行唯一化。
DW_TAG_inheritance
和 DW_TAG_friend
在 复合类型 的 elements:
字段中使用,以描述父类和友元。
DW_TAG_typedef
用于为 baseType:
提供名称。
DW_TAG_pointer_type
、DW_TAG_reference_type
、DW_TAG_const_type
、DW_TAG_volatile_type
、DW_TAG_restrict_type
、DW_TAG_atomic_type
和 DW_TAG_immutable_type
用于限定 baseType:
。
请注意,void *
类型表示为从 NULL 派生的类型。
DICompositeType¶
DICompositeType
节点表示由其他类型组成的类型,如结构和联合。 elements:
指向组成类型的元组。
如果源语言支持 ODR,则 identifier:
字段给出用于模块之间类型合并的唯一标识符。 当指定时,在其 scope:
中引用 ODR 类型的子程序声明 和 成员派生类型 会更改唯一化规则。
对于给定的 identifier:
,应该只有一个未设置 flags: DIFlagFwdDecl
的复合类型。 将模块链接在一起的 LLVM 工具将在解析时通过 identifier:
字段唯一化此类定义,即使节点是 distinct
。
!0 = !DIEnumerator(name: "SixKind", value: 7)
!1 = !DIEnumerator(name: "SevenKind", value: 7)
!2 = !DIEnumerator(name: "NegEightKind", value: -8)
!3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", file: !12,
line: 2, size: 32, align: 32, identifier: "_M4Enum",
elements: !{!0, !1, !2})
以下 tag:
值有效
DW_TAG_array_type = 1
DW_TAG_class_type = 2
DW_TAG_enumeration_type = 4
DW_TAG_structure_type = 19
DW_TAG_union_type = 23
对于 DW_TAG_array_type
,elements:
应该是 子范围描述符 或 子范围描述符类型,每个都表示该索引级别的下标范围。 DIFlagVector
标志到 flags:
指示数组类型是本机打包向量。 可选的 dataLocation
是一个 DIExpression,它描述了如何从对象的地址获取到实际的原始数据(如果它们不相等)。 这仅适用于数组类型,特别是描述 Fortran 数组,这些数组除了数组数据之外还具有数组描述符。 或者,它也可以是 DIVariable,它具有实际原始数据的地址。 Fortran 语言支持可以附加到实际数组的指针数组,指针和被指向者之间的这种附加称为关联。 可选的 associated
是一个 DIExpression,它描述了指针数组当前是否已关联。 可选的 allocated
是一个 DIExpression,它描述了可分配数组当前是否已分配。 可选的 rank
是一个 DIExpression,它描述了 Fortran 假定秩数组的秩(维度数)(秩在运行时已知)。
对于 DW_TAG_enumeration_type
,elements:
应该是 枚举器描述符,每个都表示集合的枚举值的定义。 所有枚举类型描述符都收集在 编译单元 的 enums:
字段中。
对于 DW_TAG_structure_type
、DW_TAG_class_type
和 DW_TAG_union_type
,elements:
应该是 派生类型,其 tag: DW_TAG_member
、tag: DW_TAG_inheritance
或 tag: DW_TAG_friend
; 或 子程序,其 isDefinition: false
。
DISubrange¶
DISubrange
节点是 DICompositeType 的 DW_TAG_array_type
变体的元素。
count: -1
表示一个空数组。count: !10
使用 DILocalVariable 描述计数。count: !12
使用 DIGlobalVariable 描述计数。
!0 = !DISubrange(count: 5, lowerBound: 0) ; array counting from 0
!1 = !DISubrange(count: 5, lowerBound: 1) ; array counting from 1
!2 = !DISubrange(count: -1) ; empty array.
; Scopes used in rest of example
!6 = !DIFile(filename: "vla.c", directory: "/path/to/file")
!7 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6)
!8 = distinct !DISubprogram(name: "foo", scope: !7, file: !6, line: 5)
; Use of local variable as count value
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = !DILocalVariable(name: "count", scope: !8, file: !6, line: 42, type: !9)
!11 = !DISubrange(count: !10, lowerBound: 0)
; Use of global variable as count value
!12 = !DIGlobalVariable(name: "count", scope: !8, file: !6, line: 22, type: !9)
!13 = !DISubrange(count: !12, lowerBound: 0)
DISubrangeType¶
DISubrangeType
类似于 DISubrange
,但它也是 DIType
。 它可以用作对象的类型,但也可以用作数组索引。
与 DISubrange
一样,它可以保存下限和计数,或下限和上限。 DISubrangeType
引用它是子范围的底层类型; 此类型可以是整数类型或枚举类型。
DISubrangeType
也可能具有步幅 - 与 DISubrange
不同,此步幅是位步幅。 仅当 DISubrangeType
用作数组索引类型时,步幅才有用。
最后,DISubrangeType
可能具有偏差。 在 Ada 中,程序可以请求将子范围值存储在所需的最少位数中。 在这种情况下,存储的值会受到下限的偏差 - 例如,范围 -7 .. 0
可能占用内存中的 3 位,而值 -5 将存储为 2(偏差为 -7)。
; Scopes used in rest of example
!0 = !DIFile(filename: "vla.c", directory: "/path/to/file")
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !0)
!2 = distinct !DISubprogram(name: "foo", scope: !1, file: !0, line: 5)
; Base type used in example.
!3 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
; A simple subrange with a name.
!4 = !DISubrange(name: "subrange", file: !0, line: 17, size: 32,
align: 32, baseType: !3, lowerBound: 18, count: 12)
; A subrange with a bias.
!5 = !DISubrange(name: "biased", lowerBound: -7, upperBound: 0,
bias: -7, size: 3)
; A subrange with a bit stride.
!6 = !DISubrange(name: "biased", lowerBound: 0, upperBound: 7,
stride: 3)
DIEnumerator¶
DIEnumerator
节点是 DICompositeType 的 DW_TAG_enumeration_type
变体的元素。
!0 = !DIEnumerator(name: "SixKind", value: 7)
!1 = !DIEnumerator(name: "SevenKind", value: 7)
!2 = !DIEnumerator(name: "NegEightKind", value: -8)
DITemplateTypeParameter¶
DITemplateTypeParameter
节点表示通用源语言构造的类型参数。 它们(可选地)在 DICompositeType 和 DISubprogram templateParams:
字段中使用。
!0 = !DITemplateTypeParameter(name: "Ty", type: !1)
DITemplateValueParameter¶
DITemplateValueParameter
节点表示通用源语言构造的值参数。 tag:
默认为 DW_TAG_template_value_parameter
,但如果指定,也可以设置为 DW_TAG_GNU_template_template_param
或 DW_TAG_GNU_template_param_pack
。 它们(可选地)在 DICompositeType 和 DISubprogram templateParams:
字段中使用。
!0 = !DITemplateValueParameter(name: "Ty", type: !1, value: i32 7)
DINamespace¶
DINamespace
节点表示源语言中的命名空间。
!0 = !DINamespace(name: "myawesomeproject", scope: !1, file: !2, line: 7)
DIGlobalVariable¶
DIGlobalVariable
节点表示源语言中的全局变量。
@foo = global i32, !dbg !0
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = !DIGlobalVariable(name: "foo", linkageName: "foo", scope: !2,
file: !3, line: 7, type: !4, isLocal: true,
isDefinition: false, declaration: !5)
DIGlobalVariableExpression¶
DIGlobalVariableExpression
节点将 DIGlobalVariable 与 DIExpression 绑定在一起。
@lower = global i32, !dbg !0
@upper = global i32, !dbg !1
!0 = !DIGlobalVariableExpression(
var: !2,
expr: !DIExpression(DW_OP_LLVM_fragment, 0, 32)
)
!1 = !DIGlobalVariableExpression(
var: !2,
expr: !DIExpression(DW_OP_LLVM_fragment, 32, 32)
)
!2 = !DIGlobalVariable(name: "split64", linkageName: "split64", scope: !3,
file: !4, line: 8, type: !5, declaration: !6)
所有全局变量表达式都应由 编译单元 的 globals: 字段引用。
DISubprogram¶
DISubprogram
节点表示源语言中的函数。 可以使用 !dbg
元数据将不同的 DISubprogram
附加到函数定义。 可以将唯一的 DISubprogram
附加到用于调用站点调试信息的函数声明。 retainedNodes:
字段是 变量 和 标签 的列表,即使它们的 IR 对等项已从 IR 中优化掉,也必须保留它们。 type:
字段必须指向 DISubroutineType。
当 spFlags: DISPFlagDefinition
不存在时,子程序描述类型树中的声明,而不是函数的定义。 在这种情况下,declaration
字段必须为空。 如果作用域是具有 ODR identifier:
并且未设置 flags: DIFwdDecl
的复合类型,则子程序声明仅基于其 linkageName:
和 scope:
进行唯一化。
define void @_Z3foov() !dbg !0 {
...
}
!0 = distinct !DISubprogram(name: "foo", linkageName: "_Zfoov", scope: !1,
file: !2, line: 7, type: !3,
spFlags: DISPFlagDefinition | DISPFlagLocalToUnit,
scopeLine: 8, containingType: !4,
virtuality: DW_VIRTUALITY_pure_virtual,
virtualIndex: 10, flags: DIFlagPrototyped,
isOptimized: true, unit: !5, templateParams: !6,
declaration: !7, retainedNodes: !8,
thrownTypes: !9)
DILexicalBlock¶
DILexicalBlock
节点描述 子程序 中的嵌套块。 行号和列号用于区分相同深度的两个词法块。 它们是 scope:
字段的有效目标。
!0 = distinct !DILexicalBlock(scope: !1, file: !2, line: 7, column: 35)
通常,词法块是 distinct
的,以防止基于操作数进行节点合并。
DILexicalBlockFile¶
DILexicalBlockFile
节点用于区分 词法块 的各个部分。 可以更改 file:
字段以指示文本包含,或者可以使用 discriminator:
字段来区分源语言中单个块内的控制流。
!0 = !DILexicalBlock(scope: !3, file: !4, line: 7, column: 35)
!1 = !DILexicalBlockFile(scope: !0, file: !4, discriminator: 0)
!2 = !DILexicalBlockFile(scope: !0, file: !4, discriminator: 1)
DILocation¶
DILocation
节点表示源调试位置。 scope:
字段是必需的,并且指向 DILexicalBlockFile、DILexicalBlock 或 DISubprogram。
!0 = !DILocation(line: 2900, column: 42, scope: !1, inlinedAt: !2)
DILocalVariable¶
DILocalVariable
节点表示源语言中的局部变量。 如果 arg:
字段设置为非零值,则此变量是子程序参数,它将包含在其 DISubprogram 的 retainedNodes:
字段中。
!0 = !DILocalVariable(name: "this", arg: 1, scope: !3, file: !2, line: 7,
type: !3, flags: DIFlagArtificial)
!1 = !DILocalVariable(name: "x", arg: 2, scope: !4, file: !2, line: 7,
type: !3)
!2 = !DILocalVariable(name: "y", scope: !5, file: !2, line: 7, type: !3)
DIExpression¶
DIExpression
节点表示受 DWARF 表达式语言启发的表达式。 它们在 调试记录(例如 #dbg_declare
和 #dbg_value
)中使用,以描述引用的 LLVM 变量如何与源语言变量相关。 调试表达式从左到右解释:首先将记录的值/地址运算对象推送到堆栈上,然后重复推送和评估 DIExpression 中的操作码,直到生成最终的变量描述。
当前支持的操作码词汇表是有限的
DW_OP_deref
解引用表达式堆栈的顶部。DW_OP_plus
从表达式栈中弹出最后两个条目,将它们相加,并将结果附加到表达式栈中。DW_OP_minus
从表达式栈中弹出最后两个条目,从倒数第二个条目中减去最后一个条目,并将结果附加到表达式栈中。DW_OP_plus_uconst, 93
将93
加到工作表达式中。DW_OP_LLVM_fragment, 16, 8
指定变量片段相对于工作表达式的偏移量和大小(此处分别为16
和8
)。 请注意,与 DW_OP_bit_piece 相反,此偏移量描述的是源变量内的位置。DW_OP_LLVM_convert, 16, DW_ATE_signed
指定表达式栈顶部的条目要转换成的位大小和编码(此处分别为16
和DW_ATE_signed
)。 映射到引用从提供的数值构建的基本类型的DW_OP_convert
操作。DW_OP_LLVM_extract_bits_sext, 16, 8,
指定要从表达式栈顶部的值中提取并进行符号扩展的位偏移量和大小(此处分别为16
和8
)。 如果表达式栈的顶部是内存位置,则这些位是从该内存位置指向的值中提取的。 映射到DW_OP_shl
,后跟DW_OP_shra
。DW_OP_LLVM_extract_bits_zext
的行为类似于DW_OP_LLVM_extract_bits_sext
,但进行零扩展而不是符号扩展。 映射到DW_OP_shl
,后跟DW_OP_shr
。DW_OP_LLVM_tag_offset, tag_offset
指定内存标签应可选地应用于指针。 内存标签以实现定义的方式从给定的标签偏移量派生。DW_OP_swap
交换栈顶两个条目。DW_OP_xderef
提供扩展的解引用机制。 栈顶的条目被视为地址。 第二个栈条目被视为空格标识符。DW_OP_stack_value
标记一个常量值。DW_OP_LLVM_entry_value, N
引用寄存器在函数入口时的值。 当以 DWARF 为目标时,DBG_VALUE(reg, ..., DIExpression(DW_OP_LLVM_entry_value, 1, ...)
被降低为DW_OP_entry_value [reg], ...
,这会将reg
在函数入口时的值推送到 DWARF 表达式栈上。接下来的
(N - 1)
个操作将是DW_OP_entry_value
代码块参数的一部分。 例如,!DIExpression(DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 123, DW_OP_stack_value)
指定一个表达式,其中reg
的入口值被推送到栈上,并与 123 相加。 由于框架限制,N
必须为 1,换句话说,DW_OP_entry_value
始终引用指令的值/地址操作数。由于
DW_OP_LLVM_entry_value
是根据寄存器定义的,因此它通常在 MIR 中使用,但也允许在以 swiftasync 参数为目标的 LLVM IR 中使用。 该操作由以下引入:LiveDebugValues
pass,它将其应用于在整个函数中未修改的函数参数。 支持仅限于简单的寄存器位置描述,或作为间接位置(例如,通过指向调用者中创建的临时副本的指针,按值传递给被调用者的参数)。AsmPrinter
pass,当调用点参数值 (DW_AT_call_site_parameter_value
) 表示为参数的入口值时。CoroSplit
pass,它可能会将变量从 allocas 移动到协程帧中。 如果协程帧是 swiftasync 参数,则变量使用DW_OP_LLVM_entry_value
操作进行描述。
DW_OP_LLVM_arg, N
用于引用多个值的调试内联函数中,例如计算两个寄存器之和的内联函数。 这始终与值的有序列表结合使用,以便DW_OP_LLVM_arg, N
引用该列表中的第N
th 个元素。 例如,!DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus, DW_OP_stack_value)
与列表(%reg1, %reg2)
一起使用将评估为%reg1 - reg2
。 此值列表应由包含的内联函数/指令提供。DW_OP_breg
(或DW_OP_bregx
)表示指定寄存器的提供的带符号偏移量上的内容。 操作码仅由AsmPrinter
pass 生成,以描述需要跨两个寄存器的表达式的调用点参数值。DW_OP_push_object_address
推送对象的地址,然后该地址可以用作后续计算中的描述符。 此操作码可用于计算具有数组描述符的 Fortran 可分配数组的边界。DW_OP_over
复制当前栈中第二个条目到栈顶。 此操作码可用于计算 Fortran 假定秩数组的边界,该数组的秩在运行时已知,并且当前维度编号隐式地是栈的第一个元素。DW_OP_LLVM_implicit_pointer
它指定解引用的值。 它可以用于表示被优化掉的指针变量,但它指向的值是已知的。 此运算符是必需的,因为它在表示和规范(操作数的数量和类型)方面与 DWARF 运算符 DW_OP_implicit_pointer 不同,并且后者不能用作多级。
IR for "*ptr = 4;"
--------------
#dbg_value(i32 4, !17, !DIExpression(DW_OP_LLVM_implicit_pointer), !20)
!17 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 5,
type: !18)
!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!20 = !DILocation(line: 10, scope: !12)
IR for "**ptr = 4;"
--------------
#dbg_value(i32 4, !17,
!DIExpression(DW_OP_LLVM_implicit_pointer, DW_OP_LLVM_implicit_pointer),
!21)
!17 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 5,
type: !18)
!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64)
!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!21 = !DILocation(line: 10, scope: !12)
DWARF 指定了三种简单的位置描述:寄存器、内存和隐式位置描述。 请注意,位置描述是在程序的某些范围内定义的,即变量的位置可能会在程序的过程中发生变化。 寄存器和内存位置描述描述了源变量的具体位置(从调试器可能会修改其值的意义上来说),而隐式位置仅描述了源变量的实际值,该值可能不存在于寄存器或内存中(请参阅 DW_OP_stack_value
)。
#dbg_declare
记录描述了源变量的间接值(地址)。 记录的第一个操作数必须是某种地址。 记录的 DIExpression 操作数细化此地址,以生成源变量的具体位置。
#dbg_value
记录描述了源变量的直接值。 记录的第一个操作数可以是直接值或间接值。 记录的 DIExpression 操作数细化第一个操作数,以生成直接值。 例如,如果第一个操作数是间接值,则可能需要在 DIExpression 中插入 DW_OP_deref
,以便生成有效的调试记录。
注意
DIExpression 的解释方式相同,无论它附加到哪种调试记录。
DIExpression 始终内联打印和解析;它们永远不能通过 ID(例如 !1
)引用。
!DIExpression(DW_OP_deref)
!DIExpression(DW_OP_plus_uconst, 3)
!DIExpression(DW_OP_constu, 3, DW_OP_plus)
!DIExpression(DW_OP_bit_piece, 3, 7)
!DIExpression(DW_OP_deref, DW_OP_constu, 3, DW_OP_plus, DW_OP_LLVM_fragment, 3, 7)
!DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef)
!DIExpression(DW_OP_constu, 42, DW_OP_stack_value)
DIAssignID¶
DIAssignID
节点没有操作数,并且始终是不同的。 它们用于链接在一起(#dbg_assign 记录)和在 IR 中存储的指令。 有关更多信息,请参阅 Debug Info Assignment Tracking。
store i32 %a, ptr %a.addr, align 4, !DIAssignID !2
#dbg_assign(%a, !1, !DIExpression(), !2, %a.addr, !DIExpression(), !3)
!2 = distinct !DIAssignID()
DIArgList¶
DIArgList
节点保存常量或 SSA 值引用的列表。 这些节点与 debug records 结合使用,并结合使用 DIExpression
,后者使用 DW_OP_LLVM_arg
运算符。 由于 DIArgList 可能引用函数内的局部值,因此它必须仅用作函数参数,必须始终内联,并且不能出现在命名元数据中。
#dbg_value(!DIArgList(i32 %a, i32 %b),
!16,
!DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus),
!26)
DIFlags¶
这些标志编码 DINode 的各种属性。
ExportSymbols 标志标记类、结构体或联合体,其成员可以被引用,就像它们在包含的类或联合体中定义一样。 此标志用于决定是否可以将 DW_AT_export_symbols 用于结构类型。
DIObjCProperty¶
DIObjCProperty
节点表示 Objective-C 属性节点。
!3 = !DIObjCProperty(name: "foo", file: !1, line: 7, setter: "setFoo",
getter: "getFoo", attributes: 7, type: !2)
DIImportedEntity¶
DIImportedEntity
节点表示导入到编译单元中的实体(例如模块)。 elements
字段是导入实体(例如模块)中重命名的实体(例如变量和子程序)的列表。
!2 = !DIImportedEntity(tag: DW_TAG_imported_module, name: "foo", scope: !0,
entity: !1, line: 7, elements: !3)
!3 = !{!4}
!4 = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "bar", scope: !0,
entity: !5, line: 7)
DIMacro¶
DIMacro
节点表示宏标识符的定义或取消定义。 name:
字段是宏标识符,后跟定义类似函数的宏时的宏参数,value
字段是用于扩展宏标识符的标记字符串。
!2 = !DIMacro(macinfo: DW_MACINFO_define, line: 7, name: "foo(x)",
value: "((x) + 1)")
!3 = !DIMacro(macinfo: DW_MACINFO_undef, line: 30, name: "foo")
DIMacroFile¶
DIMacroFile
节点表示包含源文件。 nodes:
字段是出现在包含的源文件中的 DIMacro
和 DIMacroFile
节点的列表。
!2 = !DIMacroFile(macinfo: DW_MACINFO_start_file, line: 7, file: !2,
nodes: !3)
DILabel¶
DILabel
节点表示 DISubprogram 内的标签。 DILabel
的所有字段都是强制性的。 scope:
字段必须是 DILexicalBlockFile、DILexicalBlock 或 DISubprogram 之一。 name:
字段是标签标识符。 file:
字段是标签所在的 DIFile。 line:
字段是标签在文件中声明的源行。
!2 = !DILabel(scope: !0, name: "foo", file: !1, line: 7)
DICommonBlock¶
DICommonBlock
节点表示 Fortran 公共块。 scope:
字段是强制性的,并指向 DILexicalBlockFile、DILexicalBlock 或 DISubprogram。 declaration:
、name:
、file:
和 line:
字段是可选的。
DIModule¶
DIModule
节点表示源语言模块,例如 Clang 模块或 Fortran 模块。 scope:
字段是强制性的,并指向 DILexicalBlockFile、DILexicalBlock 或 DISubprogram。 name:
字段是强制性的。 configMacros:
、includePath:
、apinotes:
、file:
、line:
和 isDecl:
字段是可选的。
DIStringType¶
DIStringType
节点表示 Fortran CHARACTER(n)
类型,其动态长度和位置被编码为表达式。 tag:
字段是可选的,默认为 DW_TAG_string_type
。 name:
、stringLength:
、stringLengthExpression
、stringLocationExpression:
、size:
、align:
和 encoding:
字段是可选的。
如果不存在,则 size:
和 align:
字段默认为零值。
字符串的位长度由存在的以下第一个字段指定:
stringLength:
,它指向一个DIVariable
,其值是以位为单位的字符串长度。stringLengthExpression:
,它指向一个DIExpression
,用于计算以位为单位的长度。size
,其中包含以位为单位的字面长度。
stringLocationExpression:
指向一个 DIExpression
,用于描述字符串对象的“数据位置”(如果存在)。
‘tbaa
’ 元数据¶
在 LLVM IR 中,内存没有类型,因此 LLVM 自己的类型系统不适合进行基于类型的别名分析 (TBAA)。 相反,元数据被添加到 IR 中,以描述更高级语言的类型系统。 这可以用于实现 C/C++ 严格类型别名规则,但也可以用于为其他语言实现自定义别名分析行为。
对 LLVM 的 TBAA 系统的描述分为两个部分:语义 讨论了高级问题,表示 讨论了各种实体的元数据编码。
始终可以将任何 TBAA 节点追溯到“根”TBAA 节点(详细信息请参阅 表示 部分)。 具有不同根的 TBAA 节点具有未知的别名关系,LLVM 保守地推断它们之间为 MayAlias
。 本节中提到的规则仅适用于位于同一根下的 TBAA 节点。
语义¶
TBAA 元数据系统,称为“结构路径 TBAA”(不要与 tbaa.struct
混淆),由以下高级概念组成:类型描述符,进一步细分为标量类型描述符和结构体类型描述符;以及访问标签。
类型描述符描述了正在编译的更高级语言的类型系统。 标量类型描述符描述了不包含其他类型的类型。 每个标量类型都有一个父类型,该父类型也必须是标量类型或 TBAA 根。 通过此父关系,TBAA 根内的标量类型形成树。 结构体类型描述符表示包含一系列其他类型描述符的类型,偏移量已知。 这些包含的类型描述符可以是结构体类型描述符本身,也可以是标量类型描述符。
访问标签是附加到加载和存储指令的元数据节点。 访问标签使用类型描述符来根据更高级语言的类型系统描述正在访问的位置。 访问标签是由基本类型、访问类型和偏移量组成的元组。 基本类型是标量类型描述符或结构体类型描述符,访问类型是标量类型描述符,偏移量是常量整数。
访问标签 (BaseTy, AccessTy, Offset)
可以描述以下两种情况之一:
如果
BaseTy
是结构体类型,则该标签描述在结构体类型BaseTy
中偏移量为Offset
处包含的类型为AccessTy
的值的内存访问(加载或存储)。如果
BaseTy
是标量类型,则Offset
必须为 0,并且BaseTy
和AccessTy
必须相同;并且访问标签描述标量类型为AccessTy
的标量访问。
我们首先以这种方式在 (BaseTy, Offset)
元组上定义一个 ImmediateParent
关系:
如果
BaseTy
是标量类型,则ImmediateParent(BaseTy, 0)
是(ParentTy, 0)
,其中ParentTy
是 TBAA 元数据中描述的标量类型的父类型。 如果Offset
为非零,则ImmediateParent(BaseTy, Offset)
未定义。如果
BaseTy
是结构体类型,则ImmediateParent(BaseTy, Offset)
是(NewTy, NewOffset)
,其中NewTy
是包含在BaseTy
中偏移量为Offset
处的类型,NewOffset
是Offset
,调整后相对于内部类型。
如果 (BaseTy1, Offset1)
可以通过 Parent
关系从 (Base2, Offset2)
到达,反之亦然,则具有访问标签 (BaseTy1, AccessTy1, Offset1)
的内存访问与具有访问标签 (BaseTy2, AccessTy2, Offset2)
的内存访问互为别名。 即使内存访问根据 !tbaa
元数据是 noalias,但如果它们仍然互为别名,则行为未定义。
作为一个具体的示例,以下程序的类型描述符图为:
struct Inner {
int i; // offset 0
float f; // offset 4
};
struct Outer {
float f; // offset 0
double d; // offset 4
struct Inner inner_a; // offset 12
};
void f(struct Outer* outer, struct Inner* inner, float* f, int* i, char* c) {
outer->f = 0; // tag0: (OuterStructTy, FloatScalarTy, 0)
outer->inner_a.i = 0; // tag1: (OuterStructTy, IntScalarTy, 12)
outer->inner_a.f = 0.0; // tag2: (OuterStructTy, FloatScalarTy, 16)
*f = 0.0; // tag3: (FloatScalarTy, FloatScalarTy, 0)
}
是(请注意,在 C 和 C++ 中,char
可以用于访问任何任意类型):
Root = "TBAA Root"
CharScalarTy = ("char", Root, 0)
FloatScalarTy = ("float", CharScalarTy, 0)
DoubleScalarTy = ("double", CharScalarTy, 0)
IntScalarTy = ("int", CharScalarTy, 0)
InnerStructTy = {"Inner" (IntScalarTy, 0), (FloatScalarTy, 4)}
OuterStructTy = {"Outer", (FloatScalarTy, 0), (DoubleScalarTy, 4),
(InnerStructTy, 12)}
其中(例如)ImmediateParent(OuterStructTy, 12)
= (InnerStructTy, 0)
,ImmediateParent(InnerStructTy, 0)
= (IntScalarTy, 0)
,并且 ImmediateParent(IntScalarTy, 0)
= (CharScalarTy, 0)
。
表示¶
TBAA 类型层次结构的根节点是一个 MDNode
,具有 0 个操作数或正好一个 MDString
操作数。
标量类型描述符表示为具有两个操作数的 MDNode
s。 第一个操作数是一个 MDString
,表示结构体类型的名称。 LLVM 不为此操作数的值分配含义,它只关心它是一个 MDString
。 第二个操作数是一个 MDNode
,它指向所述标量类型描述符的父类型,该父类型是另一个标量类型描述符或 TBAA 根。 标量类型描述符可以具有可选的第三个参数,但该参数必须是常量整数零。
结构体类型描述符表示为操作数数量大于 1 的奇数的 MDNode
s。 第一个操作数是一个 MDString
,表示结构体类型的名称。 与标量类型描述符类似,此名称操作数的实际值与 LLVM 无关。 在名称操作数之后,结构体类型描述符具有交替的 MDNode
和 ConstantInt
操作数序列。 从 N=1 开始,第 2N - 1 个操作数(一个 MDNode
)表示包含的字段,第 2N 个操作数(一个 ConstantInt
)是所述包含字段的偏移量。 偏移量必须是非递减顺序。
访问标签表示为具有 3 个或 4 个操作数的 MDNode
s。 第一个操作数是一个 MDNode
,指向表示基本类型的节点。 第二个操作数是一个 MDNode
,指向表示访问类型的节点。 第三个操作数是一个 ConstantInt
,用于说明访问的偏移量。 如果存在第四个字段,则它必须是一个值为 0 或 1 的 ConstantInt
。 如果为 1,则访问标签说明正在访问的位置是“常量”(意味着 pointsToConstantMemory
应返回 true;请参阅其他有用的 AliasAnalysis 方法)。 访问类型的 TBAA 根和访问标签的基本类型必须相同,并且那是访问标签的 TBAA 根。
‘tbaa.struct
’ 元数据¶
llvm.memcpy 常用于在 C 和类似语言中实现聚合赋值操作,但它的定义是复制一块连续的内存区域,对于因填充而包含空洞的聚合类型来说,这实际上是超出严格必要的。而且,它不包含关于聚合类型字段的任何 TBAA 信息。
!tbaa.struct
元数据可以描述 memcpy 操作中哪些内存子区域是填充区,以及结构的 TBAA 标签是什么。
当前的元数据格式非常简单。!tbaa.struct
元数据节点是一个操作数列表,这些操作数在概念上被分为三组。对于每组三个操作数,第一个操作数给出字段的字节偏移量(以字节为单位),第二个操作数给出其大小(以字节为单位),第三个操作数给出其 tbaa 标签。例如:
!4 = !{ i64 0, i64 4, !1, i64 8, i64 4, !2 }
这描述了一个包含两个字段的结构体。第一个字段的偏移量为 0 字节,大小为 4 字节,并且具有 tbaa 标签 !1。第二个字段的偏移量为 8 字节,大小为 4 字节,并且具有 tbaa 标签 !2。
请注意,这些字段不必是连续的。在这个例子中,两个字段之间有一个 4 字节的间隙。这个间隙表示填充,它不包含有用的数据,因此不需要保留。
‘noalias
’ 和 ‘alias.scope
’ 元数据¶
noalias
和 alias.scope
元数据提供了指定通用 noalias 内存访问集合的能力。这意味着,一些携带 noalias
元数据的内存访问指令集合(加载、存储、内存访问调用等)可以被明确指定为与另一些携带 alias.scope
元数据的内存访问指令集合不发生别名冲突。如果来自不同集合的访问发生别名冲突,则行为是未定义的。每种元数据类型都指定了一个作用域列表,其中每个作用域都有一个 ID 和一个域。
在评估别名查询时,如果对于某个域,一个指令的 alias.scope
列表中具有该域的作用域集合是另一个指令的 noalias
列表中具有该域的作用域集合的子集(或相等),则假定这两个内存访问不发生别名冲突。
由于一个域中的作用域不会影响其他域中的作用域,因此可以使用单独的域来组合多个独立的 noalias 集合。例如,这在内联期间使用。当 noalias 函数参数转换为 noalias 作用域元数据时,每次函数内联时都会使用一个新的域。
标识每个域的元数据本身是一个包含一个或两个条目的列表。第一个条目是域的名称。请注意,如果名称是字符串,则可以在函数和翻译单元之间组合。可以使用自引用来创建全局唯一的域名。描述性字符串可以可选地作为第二个列表条目提供。
标识每个作用域的元数据本身也是一个包含两个或三个条目的列表。第一个条目是作用域的名称。请注意,如果名称是字符串,则可以在函数和翻译单元之间组合。可以使用自引用来创建全局唯一的作用域名。对作用域域的元数据引用是第二个条目。描述性字符串可以可选地作为第三个列表条目提供。
例如,
; Two scope domains:
!0 = !{!0}
!1 = !{!1}
; Some scopes in these domains:
!2 = !{!2, !0}
!3 = !{!3, !0}
!4 = !{!4, !1}
; Some scope lists:
!5 = !{!4} ; A list containing only scope !4
!6 = !{!4, !3, !2}
!7 = !{!3}
; These two instructions don't alias:
%0 = load float, ptr %c, align 4, !alias.scope !5
store float %0, ptr %arrayidx.i, align 4, !noalias !5
; These two instructions also don't alias (for domain !1, the set of scopes
; in the !alias.scope equals that in the !noalias list):
%2 = load float, ptr %c, align 4, !alias.scope !5
store float %2, ptr %arrayidx.i2, align 4, !noalias !6
; These two instructions may alias (for domain !0, the set of scopes in
; the !noalias list is not a superset of, or equal to, the scopes in the
; !alias.scope list):
%2 = load float, ptr %c, align 4, !alias.scope !6
store float %0, ptr %arrayidx.i, align 4, !noalias !7
‘fpmath
’ 元数据¶
fpmath
元数据可以附加到任何浮点类型的指令。它可用于表示该指令结果中可接受的最大误差,以 ULP 为单位,从而可能允许编译器使用更有效但精度较低的计算方法。ULP 的定义如下:
如果
x
是一个实数,位于两个有限连续的浮点数a
和b
之间,且不等于其中任何一个,则ulp(x) = |b - a|
,否则ulp(x)
是最接近x
的两个不相等的有限浮点数之间的距离。此外,ulp(NaN)
是NaN
。
元数据节点应由单个正浮点类型数字组成,表示最大相对误差,例如:
!0 = !{ float 2.5 } ; maximum acceptable inaccuracy is 2.5 ULPs
‘range
’ 元数据¶
range
元数据只能附加到整数或整数向量类型的 load
、call
和 invoke
指令。它表示在此调用点加载的值或被调用函数返回的值可能在的范围。如果加载或返回的值不在指定范围内,则返回一个 poison 值。范围用扁平化的整数列表表示。已知加载的值或返回的值在由每个连续对定义的范围的并集中。每对都具有以下属性:
类型必须与指令的标量类型匹配。
类型必须与参数或返回值的标量类型匹配。
对
a,b
表示范围[a,b)
。a
和b
都是常量。范围不应表示完整集或空集。也就是说,
a!=b
。
此外,这些对必须按有符号下限排序,并且必须是不连续的。
对于向量类型指令,范围是逐元素应用的。
示例:
%a = load i8, ptr %x, align 1, !range !0 ; Can only be 0 or 1
%b = load i8, ptr %y, align 1, !range !1 ; Can only be 255 (-1), 0 or 1
%c = call i8 @foo(), !range !2 ; Can only be 0, 1, 3, 4 or 5
%d = invoke i8 @bar() to label %cont
unwind label %lpad, !range !3 ; Can only be -2, -1, 3, 4 or 5
%e = load <2 x i8>, ptr %x, !range 0 ; Can only be <0 or 1, 0 or 1>
...
!0 = !{ i8 0, i8 2 }
!1 = !{ i8 255, i8 2 }
!2 = !{ i8 0, i8 2, i8 3, i8 6 }
!3 = !{ i8 -2, i8 0, i8 3, i8 6 }
‘absolute_symbol
’ 元数据¶
absolute_symbol
元数据可以附加到全局变量声明。它将声明标记为对绝对符号的引用,这会导致后端即使在位置无关代码中也对该符号使用绝对重定位,并以与 range
元数据相同的格式表示全局变量的地址(而非其值)可能在的范围,扩展之处在于,可以使用 all-ones,all-ones
对来表示完整集。
示例(假设 64 位指针):
@a = external global i8, !absolute_symbol !0 ; Absolute symbol in range [0,256)
@b = external global i8, !absolute_symbol !1 ; Absolute symbol in range [0,2^64)
...
!0 = !{ i64 0, i64 256 }
!1 = !{ i64 -1, i64 -1 }
‘callees
’ 元数据¶
callees
元数据可以附加到间接调用点。如果 callees
元数据附加到调用点,并且任何被调用者不在元数据提供的函数集中,则行为是未定义的。此元数据的目的是促进诸如间接调用提升之类的优化。例如,在下面的代码中,调用指令可能仅以 add
或 sub
函数为目标:
%result = call i64 %binop(i64 %x, i64 %y), !callees !0
...
!0 = !{ptr @add, ptr @sub}
‘callback
’ 元数据¶
callback
元数据可以附加到函数声明或定义。(调用点被排除在外仅仅是因为缺少用例。)为了便于说明,我们将带有元数据注释的函数称为代理函数。元数据描述了对代理函数的调用的参数如何依次传递给元数据指定的回调函数。因此,callback
元数据提供了代理函数内部调用点的部分描述,涉及对代理函数的调用的参数。对代理函数本身的唯一语义限制是,不允许它检查或修改在 callback
元数据中引用的作为传递给回调函数的参数。
不要求代理函数在运行时实际调用回调函数。但是,关于不检查或修改将传递给指定回调函数的参数的假设仍然成立,即使回调函数不是动态调用的。代理函数允许在每次调用代理函数时多次调用回调函数。代理函数还允许通过另一种用途调用(直接或间接地)作为回调传递的函数。最后,代理函数还允许将回调被调用者的调用转发到不同的线程。
元数据的结构如下:在外部级别,callback
元数据是一个 callback
编码列表。每个编码都以一个常量 i64
开头,该常量描述了回调函数在对代理函数的调用中的参数位置。以下元素(最后一个元素除外)描述了哪些参数传递给回调函数。每个元素又是一个 i64
常量,用于标识代理函数的哪个参数被传递,或者 i64 -1
表示未知或检查的参数。它们被列出的顺序必须与它们传递给回调被调用者的顺序相同。编码的最后一个元素是一个布尔值,用于指定如何处理代理函数的可变参数。如果为真,则代理函数的所有可变参数都会在之前显式编码的参数之后传递给回调函数。
在下面的代码中,pthread_create
函数通过 !callback !1
元数据标记为代理函数。在该示例中,只有一个回调编码,即 !2
,与代理函数关联。此编码将回调函数标识为代理函数的第二个参数(i64 2
),并将回调函数的唯一参数标识为代理函数的第三个参数(i64 3
)。
declare !callback !1 dso_local i32 @pthread_create(ptr, ptr, ptr, ptr)
...
!2 = !{i64 2, i64 3, i1 false}
!1 = !{!2}
下面显示了另一个示例。回调被调用者是 __kmpc_fork_call
函数的第二个参数(i64 2
)。被调用者被赋予两个未知值(每个值都由 i64 -1
标识),然后是传递给 __kmpc_fork_call
调用的所有可变参数(由于最后的 i1 true
)。
declare !callback !0 dso_local void @__kmpc_fork_call(ptr, i32, ptr, ...)
...
!1 = !{i64 2, i64 -1, i64 -1, i1 true}
!0 = !{!1}
‘exclude
’ 元数据¶
exclude
元数据可以附加到全局变量,以表示其节不应包含在最终的可执行文件或共享库中。此选项仅对具有以 ELF 或 COFF 为目标的显式节的全局变量有效。这通过在 ELF 目标上使用 SHF_EXCLUDE
标志,以及在 COFF 目标上使用 IMAGE_SCN_LNK_REMOVE
和 IMAGE_SCN_MEM_DISCARDABLE
标志来完成。此外,此元数据仅用作标志,因此关联的节点必须为空。显式节不应与用户不希望在链接后删除的任何其他节冲突。
@object = private constant [1 x i8] c"\00", section ".foo" !exclude !0
...
!0 = !{}
‘unpredictable
’ 元数据¶
unpredictable
元数据可以附加到任何分支或 switch 指令。它可用于表示控制流的不可预测性。与 llvm.expect intrinsic 类似,它可用于更改与比较和分支指令相关的优化。元数据被视为布尔值;如果它存在,则表示它附加到的分支或 switch 是完全不可预测的。
‘dereferenceable
’ 元数据¶
指令上存在 !dereferenceable
元数据会告诉优化器,加载的值已知是可解引用的,否则行为是未定义的。已知可解引用的字节数由元数据节点中的整数值指定。这类似于参数和返回值上的“dereferenceable”属性。
‘dereferenceable_or_null
’ 元数据¶
指令上存在 !dereferenceable_or_null
元数据会告诉优化器,加载的值已知是可解引用或为空的,否则行为是未定义的。已知可解引用的字节数由元数据节点中的整数值指定。这类似于参数和返回值上的“dereferenceable_or_null”属性。
‘llvm.loop
’¶
有时将信息附加到循环结构很有用。目前,循环元数据被实现为附加到循环锁存块中的分支指令的元数据。循环元数据节点是其他元数据节点的列表,每个节点代表循环的一个属性。通常,属性节点的第一个项是一个字符串。例如,llvm.loop.unroll.count
向循环展开器建议一个展开因子。
br i1 %exitcond, label %._crit_edge, label %.lr.ph, !llvm.loop !0
...
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll.enable"}
!2 = !{!"llvm.loop.unroll.count", i32 4}
出于遗留原因,循环元数据节点的第一个项必须是对自身的引用。在“distinct”关键字出现之前,这迫使保留原本相同的元数据节点。由于循环元数据节点可以附加到多个节点,“distinct”关键字已变得不必要。
在属性节点之前,列表中可以存在一个或两个 DILocation
(调试位置)节点。第一个节点(如果存在)标识循环开始的源代码位置。第二个节点(如果存在)标识循环结束的源代码位置。
循环元数据节点不能用作唯一标识符。它们对于通过转换的同一循环既不是持久的,也不一定对于仅一个循环是唯一的。
‘llvm.loop.disable_nonforced
’¶
此元数据禁用所有可选的循环转换,除非使用其他转换元数据(如 llvm.loop.unroll.enable
)显式指示。也就是说,没有启发式方法会尝试确定转换是否有利可图。目的是避免在应用显式请求的(强制的)转换之前,循环被转换为不同的循环。例如,循环融合会使其他转换变得不可能。强制性的循环规范化(如循环旋转)仍然适用。
建议除了任何 llvm.loop.* 转换指令之外,还使用此元数据。此外,任何循环都应最多应用一个指令(以及使用 followup-attributes 构建的转换序列)。否则,将应用哪个转换取决于实现细节,例如 pass pipeline 顺序。
有关详细信息,请参阅 代码转换元数据。
‘llvm.loop.vectorize
’ 和 ‘llvm.loop.interleave
’¶
以 llvm.loop.vectorize
或 llvm.loop.interleave
为前缀的元数据用于控制每个循环的向量化和交错参数,例如向量化宽度和交错计数。这些元数据应与 llvm.loop
循环标识元数据结合使用。llvm.loop.vectorize
和 llvm.loop.interleave
元数据仅为优化提示,并且优化器仅在认为安全的情况下才会交错和向量化循环。包含有关循环携带的内存依赖性信息的 llvm.loop.parallel_accesses
元数据可能有助于确定这些转换的安全性。
‘llvm.loop.interleave.count
’ 元数据¶
此元数据向循环交错器建议一个交错计数。第一个操作数是字符串 llvm.loop.interleave.count
,第二个操作数是指定交错计数的整数。例如:
!0 = !{!"llvm.loop.interleave.count", i32 4}
请注意,将 llvm.loop.interleave.count
设置为 1 会禁用循环的多次迭代的交错。如果 llvm.loop.interleave.count
设置为 0,则交错计数将自动确定。
‘llvm.loop.vectorize.enable
’ 元数据¶
此元数据有选择地启用或禁用循环的向量化。第一个操作数是字符串 llvm.loop.vectorize.enable
,第二个操作数是一个位。如果位操作数值为 1,则启用向量化。值为 0 则禁用向量化。
!0 = !{!"llvm.loop.vectorize.enable", i1 0}
!1 = !{!"llvm.loop.vectorize.enable", i1 1}
‘llvm.loop.vectorize.predicate.enable
’ 元数据¶
此元数据有选择地启用或禁用为循环创建谓词指令,这可以实现将标量尾声循环折叠到主循环中。第一个操作数是字符串 llvm.loop.vectorize.predicate.enable
,第二个操作数是一个位。如果位操作数值为 1,则启用向量化。值为 0 则禁用向量化。
!0 = !{!"llvm.loop.vectorize.predicate.enable", i1 0}
!1 = !{!"llvm.loop.vectorize.predicate.enable", i1 1}
‘llvm.loop.vectorize.scalable.enable
’ 元数据¶
此元数据有选择地启用或禁用循环的可伸缩向量化,并且仅在已启用循环向量化时才有效。第一个操作数是字符串 llvm.loop.vectorize.scalable.enable
,第二个操作数是一个位。如果位操作数值为 1,则启用可伸缩向量化,而值为 0 则恢复为默认的固定宽度向量化。
!0 = !{!"llvm.loop.vectorize.scalable.enable", i1 0}
!1 = !{!"llvm.loop.vectorize.scalable.enable", i1 1}
‘llvm.loop.vectorize.width
’ 元数据¶
此元数据设置向量化器的目标宽度。第一个操作数是字符串 llvm.loop.vectorize.width
,第二个操作数是指定宽度的整数。例如:
!0 = !{!"llvm.loop.vectorize.width", i32 4}
请注意,将 llvm.loop.vectorize.width
设置为 1 会禁用循环的向量化。如果 llvm.loop.vectorize.width
设置为 0,或者如果循环没有此元数据,则宽度将自动确定。
‘llvm.loop.vectorize.followup_vectorized
’ 元数据¶
此元数据定义了向量化循环将具有哪些循环属性。有关详细信息,请参阅 代码转换元数据。
‘llvm.loop.vectorize.followup_epilogue
’ 元数据¶
此元数据定义了尾声循环将具有哪些循环属性。当向量化循环未知是否保留语义(例如,因为它处理两个被运行时检查发现别名的数组)或者对于不填充完整向量通道集的最后迭代时,尾声循环不进行向量化并执行。有关详细信息,请参阅 转换元数据。
‘llvm.loop.vectorize.followup_all
’ 元数据¶
元数据中的属性将添加到向量化循环和尾声循环。有关详细信息,请参阅 转换元数据。
‘llvm.loop.unroll
’¶
以 llvm.loop.unroll
为前缀的元数据是循环展开优化提示,例如展开因子。llvm.loop.unroll
元数据应与 llvm.loop
循环标识元数据结合使用。llvm.loop.unroll
元数据仅为优化提示,并且仅在优化器认为安全的情况下才会执行展开。
‘llvm.loop.unroll.count
’ 元数据¶
此元数据向循环展开器建议一个展开因子。第一个操作数是字符串 llvm.loop.unroll.count
,第二个操作数是指定展开因子的正整数。例如:
!0 = !{!"llvm.loop.unroll.count", i32 4}
如果循环的 trip count 小于展开计数,则循环将部分展开。
‘llvm.loop.unroll.disable
’ 元数据¶
此元数据禁用循环展开。该元数据具有单个操作数,即字符串 llvm.loop.unroll.disable
。例如:
!0 = !{!"llvm.loop.unroll.disable"}
‘llvm.loop.unroll.runtime.disable
’ 元数据¶
此元数据禁用运行时循环展开。该元数据具有单个操作数,即字符串 llvm.loop.unroll.runtime.disable
。例如:
!0 = !{!"llvm.loop.unroll.runtime.disable"}
‘llvm.loop.unroll.enable
’ 元数据¶
此元数据建议,如果在编译时已知 trip count,则应完全展开循环;如果编译时未知 trip count,则应部分展开循环。该元数据具有单个操作数,即字符串 llvm.loop.unroll.enable
。例如:
!0 = !{!"llvm.loop.unroll.enable"}
‘llvm.loop.unroll.full
’ 元数据¶
此元数据建议应完全展开循环。该元数据具有单个操作数,即字符串 llvm.loop.unroll.full
。例如:
!0 = !{!"llvm.loop.unroll.full"}
‘llvm.loop.unroll.followup
’ 元数据¶
此元数据定义了展开循环将具有哪些循环属性。有关详细信息,请参阅 转换元数据。
‘llvm.loop.unroll.followup_remainder
’ 元数据¶
此元数据定义了部分/运行时展开后的剩余循环将具有哪些循环属性。有关详细信息,请参阅 转换元数据。
‘llvm.loop.unroll_and_jam
’¶
此元数据的处理方式与上面的 llvm.loop.unroll
元数据非常相似,但会影响 unroll and jam pass。此外,任何具有 llvm.loop.unroll
元数据但没有 llvm.loop.unroll_and_jam
元数据的循环都将禁用 unroll and jam(因此 llvm.loop.unroll
元数据将留给展开器,并且 llvm.loop.unroll.disable
元数据也将禁用 unroll and jam。)
unroll and jam 的元数据在其他方面与 unroll
相同。llvm.loop.unroll_and_jam.enable
、llvm.loop.unroll_and_jam.disable
和 llvm.loop.unroll_and_jam.count
的作用与 unroll 相同。llvm.loop.unroll_and_jam.full
不受支持。同样,这些只是提示,正常的安全检查仍将执行。
‘llvm.loop.unroll_and_jam.count
’ 元数据¶
此元数据建议使用 unroll and jam 因子,类似于 llvm.loop.unroll.count
。第一个操作数是字符串 llvm.loop.unroll_and_jam.count
,第二个操作数是指定展开因子的正整数。例如:
!0 = !{!"llvm.loop.unroll_and_jam.count", i32 4}
如果循环的 trip count 小于展开计数,则循环将部分 unroll and jammed。
‘llvm.loop.unroll_and_jam.disable
’ 元数据¶
此元数据禁用循环展开和阻塞。此元数据只有一个操作数,即字符串 llvm.loop.unroll_and_jam.disable
。例如
!0 = !{!"llvm.loop.unroll_and_jam.disable"}
‘llvm.loop.unroll_and_jam.enable
’ 元数据¶
此元数据建议,如果循环计数在编译时已知,则应完全展开和阻塞循环;如果循环计数在编译时未知,则应部分展开循环。此元数据只有一个操作数,即字符串 llvm.loop.unroll_and_jam.enable
。例如
!0 = !{!"llvm.loop.unroll_and_jam.enable"}
‘llvm.loop.unroll_and_jam.followup_outer
’ 元数据¶
此元数据定义外部展开循环将具有哪些循环属性。 有关详细信息,请参见转换元数据。
‘llvm.loop.unroll_and_jam.followup_inner
’ 元数据¶
此元数据定义内部阻塞循环将具有哪些循环属性。 有关详细信息,请参见转换元数据。
‘llvm.loop.unroll_and_jam.followup_remainder_outer
’ 元数据¶
此元数据定义外部循环的尾声将具有哪些属性。此循环通常是展开的,这意味着没有这样的循环。在这种情况下,此属性将被忽略。 有关详细信息,请参见转换元数据。
‘llvm.loop.unroll_and_jam.followup_remainder_inner
’ 元数据¶
此元数据定义尾声的内部循环将具有哪些属性。外部尾声通常会展开,这意味着可以有多个内部剩余循环。 有关详细信息,请参见转换元数据。
‘llvm.loop.unroll_and_jam.followup_all
’ 元数据¶
元数据中指定的属性将添加到所有 llvm.loop.unroll_and_jam.*
循环中。 有关详细信息,请参见转换元数据。
‘llvm.loop.licm_versioning.disable
’ 元数据¶
此元数据指示不应为启用循环不变代码移动 (LICM) 而对循环进行版本控制。此元数据只有一个操作数,即字符串 llvm.loop.licm_versioning.disable
。例如
!0 = !{!"llvm.loop.licm_versioning.disable"}
‘llvm.loop.distribute.enable
’ 元数据¶
循环分发允许将一个循环拆分为多个循环。目前,仅当由于不安全的内存依赖关系而无法向量化整个循环时,才会执行此操作。转换将尝试将不安全的依赖关系隔离到它们自己的循环中。
此元数据可用于选择性地启用或禁用循环的分发。第一个操作数是字符串 llvm.loop.distribute.enable
,第二个操作数是位。如果位操作数值为 1,则启用分发。值为 0 则禁用分发
!0 = !{!"llvm.loop.distribute.enable", i1 0}
!1 = !{!"llvm.loop.distribute.enable", i1 1}
此元数据应与 llvm.loop
循环标识元数据结合使用。
‘llvm.loop.distribute.followup_coincident
’ 元数据¶
此元数据定义没有循环依赖关系的提取循环(即可以向量化)将具有哪些属性。 有关详细信息,请参见转换元数据。
‘llvm.loop.distribute.followup_sequential
’ 元数据¶
此元数据定义具有不安全内存依赖关系的隔离循环将具有哪些属性。 有关详细信息,请参见转换元数据。
‘llvm.loop.distribute.followup_fallback
’ 元数据¶
如果循环版本控制是必要的,则此元数据定义非分布式回退版本将具有的属性。 有关详细信息,请参见转换元数据。
‘llvm.loop.distribute.followup_all
’ 元数据¶
此元数据中的属性将添加到循环分发pass的所有后续循环中。 有关详细信息,请参见转换元数据。
‘llvm.licm.disable
’ 元数据¶
此元数据指示不应在此循环上执行循环不变代码移动 (LICM)。此元数据只有一个操作数,即字符串 llvm.licm.disable
。例如
!0 = !{!"llvm.licm.disable"}
请注意,尽管它按循环操作,但它没有给定 llvm.loop 前缀,因为它不受 llvm.loop.disable_nonforced
元数据的影响。
‘llvm.access.group
’ 元数据¶
llvm.access.group
元数据可以附加到任何可能访问内存的指令。它可以指向单个不同的元数据节点,我们称之为访问组。此节点表示通过 llvm.access.group
引用它的所有内存访问指令。当指令属于多个访问组时,它也可以指向访问组列表,如下例所示。
%val = load i32, ptr %arrayidx, !llvm.access.group !0
...
!0 = !{!1, !2}
!1 = distinct !{}
!2 = distinct !{}
列表节点为空是非法的,因为它可能会与访问组混淆。
访问组元数据节点必须是“distinct”,以避免按内容折叠多个访问组。访问组元数据节点必须始终为空,这可用于区分访问组元数据节点与访问组列表。保持为空避免了必须更新内容的情况,因为元数据在设计上是不可变的,因此需要查找和更新对访问组节点的所有引用。
访问组可用于引用内存访问指令,而无需直接指向它(这在全局元数据中是不可能的)。目前,唯一使用它的元数据是 llvm.loop.parallel_accesses
。
‘llvm.loop.parallel_accesses
’ 元数据¶
llvm.loop.parallel_accesses
元数据引用一个或多个访问组元数据节点(参见 llvm.access.group
)。它表示在循环中具有此元数据的指令与其他指令之间不存在循环携带的内存依赖性。
设 m1
和 m2
是两个指令,它们都具有指向访问组 g1
的 llvm.access.group
元数据,分别为 g2
(可能相同)。如果循环在其 llvm.loop.parallel_accesses
元数据中包含两个访问组,则编译器可以假定 m1
和 m2
之间没有由此循环携带的依赖关系。如果至少一个访问组与 llvm.loop.parallel_accesses
列表匹配,则属于多个访问组的指令被认为具有此属性。
如果循环中的所有内存访问指令都具有 llvm.access.group
元数据,并且每个元数据都引用循环的 llvm.loop.parallel_accesses
元数据的一个访问组,则循环没有循环携带的内存依赖性,并且被认为是并行循环。如果存在循环携带的依赖性,则行为未定义。
请注意,如果并非所有内存访问指令都属于 llvm.loop.parallel_accesses
引用的访问组,则循环不得被视为平凡并行。需要额外的内存依赖性分析才能做出该判断。作为一种故障安全机制,这会导致最初并行的循环被视为顺序循环(如果不知道并行语义的优化pass将新的内存指令插入循环体中)。
由于正确使用了 llvm.access.group
和 llvm.loop.parallel_accesses
元数据类型,因此被认为是并行的循环示例。
for.body:
...
%val0 = load i32, ptr %arrayidx, !llvm.access.group !1
...
store i32 %val0, ptr %arrayidx1, !llvm.access.group !1
...
br i1 %exitcond, label %for.end, label %for.body, !llvm.loop !0
for.end:
...
!0 = distinct !{!0, !{!"llvm.loop.parallel_accesses", !1}}
!1 = distinct !{}
也可以有嵌套的并行循环
outer.for.body:
...
%val1 = load i32, ptr %arrayidx3, !llvm.access.group !4
...
br label %inner.for.body
inner.for.body:
...
%val0 = load i32, ptr %arrayidx1, !llvm.access.group !3
...
store i32 %val0, ptr %arrayidx2, !llvm.access.group !3
...
br i1 %exitcond, label %inner.for.end, label %inner.for.body, !llvm.loop !1
inner.for.end:
...
store i32 %val1, ptr %arrayidx4, !llvm.access.group !4
...
br i1 %exitcond, label %outer.for.end, label %outer.for.body, !llvm.loop !2
outer.for.end: ; preds = %for.body
...
!1 = distinct !{!1, !{!"llvm.loop.parallel_accesses", !3}} ; metadata for the inner loop
!2 = distinct !{!2, !{!"llvm.loop.parallel_accesses", !3, !4}} ; metadata for the outer loop
!3 = distinct !{} ; access group for instructions in the inner loop (which are implicitly contained in outer loop as well)
!4 = distinct !{} ; access group for instructions in the outer, but not the inner loop
‘llvm.loop.mustprogress
’ 元数据¶
llvm.loop.mustprogress
元数据指示此循环需要终止、展开或以可观察的方式与环境交互,例如通过易失性内存访问、I/O 或其他同步。如果未发现此类循环以可观察的方式与环境交互,则可以删除该循环。这对应于 mustprogress
函数属性。
‘irr_loop
’ 元数据¶
irr_loop
元数据可以附加到作为不可约循环头的基本块的终止符指令(请注意,不可约循环具有多个头基本块。)如果 irr_loop
元数据附加到实际上不是不可约循环头的基本块的终止符指令,则行为未定义。此元数据的目的是提高块频率传播的准确性。例如,在下面的代码中,块 header0
可能具有 100 的循环头权重(相对于不可约循环的其他头)
header0:
...
br i1 %cmp, label %t1, label %t2, !irr_loop !0
...
!0 = !{"loop_header_weight", i64 100}
不可约循环头权重通常基于profile数据。
‘invariant.group
’ 元数据¶
实验性的 invariant.group
元数据可以附加到引用单个无条目元数据的 load
/store
指令。指令上 invariant.group
元数据的存在告诉优化器,可以假定每个指向同一指针操作数的 load
和 store
加载或存储相同的值(但请参阅 llvm.launder.invariant.group
intrinsic,它影响何时将两个指针视为相同)。仅使用零索引的 bitcast 或 getelementptr 返回的指针被认为是相同的。
示例:
@unknownPtr = external global i8
...
%ptr = alloca i8
store i8 42, ptr %ptr, !invariant.group !0
call void @foo(ptr %ptr)
%a = load i8, ptr %ptr, !invariant.group !0 ; Can assume that value under %ptr didn't change
call void @foo(ptr %ptr)
%newPtr = call ptr @getPointer(ptr %ptr)
%c = load i8, ptr %newPtr, !invariant.group !0 ; Can't assume anything, because we only have information about %ptr
%unknownValue = load i8, ptr @unknownPtr
store i8 %unknownValue, ptr %ptr, !invariant.group !0 ; Can assume that %unknownValue == 42
call void @foo(ptr %ptr)
%newPtr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
%d = load i8, ptr %newPtr2, !invariant.group !0 ; Can't step through launder.invariant.group to get value of %ptr
...
declare void @foo(ptr)
declare ptr @getPointer(ptr)
declare ptr @llvm.launder.invariant.group.p0(ptr)
!0 = !{}
当基于别名信息用另一个指针替换一个指针时,必须删除 invariant.group 元数据。这是因为 invariant.group 与指针操作数的 SSA 值相关联。
%v = load i8, ptr %x, !invariant.group !0
; if %x mustalias %y then we can replace the above instruction with
%v = load i8, ptr %y
请注意,这是一个实验性功能,这意味着其语义将来可能会发生变化。
‘type
’ 元数据¶
参见类型元数据。
‘associated
’ 元数据¶
associated
元数据可以附加到全局变量定义,其单个参数引用全局对象(可选地通过别名)。
此元数据降低为 ELF section flag SHF_LINK_ORDER
,这可以防止在链接器 GC 中丢弃全局变量,除非引用的对象也被丢弃。链接器对此功能的支持是不稳定的。为了获得最佳兼容性,携带此元数据的全局变量应
位于
@llvm.compiler.used
中。如果引用的全局变量在 comdat 中,则位于同一 comdat 中。
!associated
无法表达多对一关系。具有元数据的全局变量通常不应被函数引用:函数可能会内联到其他函数中,从而导致更多对元数据的引用。理想情况下,我们希望只要任何内联位置处于活动状态就保持元数据处于活动状态,但是这种多对一关系是不可表示的。此外,如果在函数被丢弃时保留元数据,则链接器将报告引用已丢弃节的重定位错误。
元数据通常与由有效 C 标识符组成的显式节一起使用,以便运行时可以使用链接器定义的封装符号 __start_<section_name>
和 __stop_<section_name>
找到元数据节。
它对非 ELF 目标没有任何影响。
示例
$a = comdat any
@a = global i32 1, comdat $a
@b = internal global i32 2, comdat $a, section "abc", !associated !0
!0 = !{ptr @a}
‘prof
’ 元数据¶
prof
元数据用于在 IR 中记录profile数据。元数据节点的第一个操作数指示profile元数据类型。目前有 3 种类型:branch_weights、function_entry_count 和 VP。
branch_weights¶
附加到分支、select、switch 或 call 指令的分支权重元数据表示关联分支被采用的可能性。有关更多信息,请参见LLVM 分支权重元数据。
function_entry_count¶
函数入口计数元数据可以附加到函数定义,以记录函数被调用的次数。与 BFI 信息一起使用,它也用于导出基本块profile计数。有关更多信息,请参见LLVM 分支权重元数据。
VP¶
VP(值profile)元数据可以附加到具有值profile信息的指令。目前,这是间接调用(它记录最热门的被调用者)和对内存intrinsic(如 memcpy、memmove 和 memset)的调用(它记录最热门的字节长度)。
每个 VP 元数据节点都包含 “VP” 字符串,然后是值profile类型的 uint32_t 值,指令执行总次数的 uint64_t 值,后跟 uint64_t 值和执行计数组合。值profile类型对于间接调用目标为 0,对于内存操作为 1。对于间接调用目标,每个profile值是被调用函数名称的哈希值,对于内存操作,每个值是字节长度。
请注意,值计数不需要加起来等于第三个操作数中列出的总计数(实际上,仅跟踪和报告最热门的值)。
间接调用示例
call void %f(), !prof !1
!1 = !{!"VP", i32 0, i64 1600, i64 7651369219802541373, i64 1030, i64 -4377547752858689819, i64 410}
请注意,VP 类型为 0(第二个操作数),这表示这是间接调用值profile数据。第三个操作数表示间接调用执行了 1600 次。第 4 和第 6 个操作数给出了 2 个最热门目标函数名称的哈希值(这与用于表示profile数据库中函数名称的哈希值相同),第 5 和第 7 个操作数给出了每个先前目标函数被调用的执行计数。
‘annotation
’ 元数据¶
annotation
元数据可用于将注释字符串元组或注释字符串元组的元组附加到任何指令。此元数据不影响程序的语义,可能仅用于向用户提供有关程序和转换的更多见解。
示例
%a.addr = alloca ptr, align 8, !annotation !0
!0 = !{!"auto-init"}
嵌入字符串元组示例
%a.ptr = getelementptr ptr, ptr %base, i64 0. !annotation !0
!0 = !{!1}
!1 = !{!"gep offset", !"0"}
‘func_sanitize
’ 元数据¶
func_sanitize
元数据用于附加函数 sanitizer instrumentation 的两个值。第一个值是 ubsan 函数签名。第二个值是代理变量的地址,该代理变量存储 RTTI 描述符的地址。如果 prologue 和 ‘func_sanitize
’ 同时使用,则 prologue 在输出中在 ‘func_sanitize
’ 之前发出。
示例
@__llvm_rtti_proxy = private unnamed_addr constant ptr @_ZTIFvvE
define void @_Z3funv() !func_sanitize !0 {
return void
}
!0 = !{i32 846595819, ptr @__llvm_rtti_proxy}
‘kcfi_type
’ 元数据¶
kcfi_type
元数据可用于将类型标识符附加到可以间接调用的函数。类型数据在汇编中的函数入口之前发出。带有 kcfi 操作数bundle 的间接调用将发出检查,该检查将类型标识符与元数据进行比较。
示例
define dso_local i32 @f() !kcfi_type !0 {
ret i32 0
}
!0 = !{i32 12345678}
Clang 为带有 -fsanitize=kcfi
的 address-taken 函数发出 kcfi_type
元数据节点。
‘memprof
’ 元数据¶
memprof
元数据用于记录堆分配调用上的内存profile数据。可以使用单个 memprof
元数据附件表示多个上下文相关的profile。
示例
%call = call ptr @_Znam(i64 10), !memprof !0, !callsite !5
!0 = !{!1, !3}
!1 = !{!2, !"cold"}
!2 = !{i64 4854880825882961848, i64 1905834578520680781}
!3 = !{!4, !"notcold"}
!4 = !{i64 4854880825882961848, i64 -6528110295079665978}
!5 = !{i64 4854880825882961848}
memprof
元数据附件中的每个操作数都描述了与给定上下文的关联分配分配的内存的profile行为。在上面的示例中,有 2 个profile上下文,一个分配的内存通常是冷的,一个分配的内存通常不是冷的。
描述上下文特定profile的元数据的格式(例如上面的 !1
和 !3
)需要第一个操作数,该操作数是描述上下文的元数据节点,后跟描述profile行为的字符串元数据标记列表(例如上面的 cold
和 notcold
)。描述上下文的元数据节点(例如上面的 !2
和 !4
)是对应于调用站点的唯一 ID,可以通过 callsite 元数据 将其与关联的 IR 调用匹配。实际上,这些 ID 是通过调用站点的调试信息哈希形成的,并且关联的调用可能在不同的模块中。上下文按从最叶子的调用(分配本身)到唯一标识描述的profile行为所需的最外层调用站点上下文的顺序列出(请注意,这可能不是profile调用堆栈的顶部)。
‘callsite
’ 元数据¶
callsite
元数据用于标识参与 memprof 元数据 中描述的内存profile上下文的调用站点。
它既附加到profile分配调用(参见 memprof 元数据 中的示例),也附加到堆分配 memprof
元数据中描述的profile上下文中其他调用站点。
示例
%call = call ptr @_Z1Bb(void), !callsite !0
!0 = !{i64 -6528110295079665978, i64 5462047985461644151}
callsite
元数据附件中的每个操作数都是对应于调用站点(可能是内联的)的唯一 ID。实际上,这些 ID 是通过调用站点的调试信息哈希形成的。如果调用未内联到任何调用者中,它将包含单个操作数(ID)。如果它已内联,它将包含 ID 列表,包括完整内联序列中调用站点的 ID,顺序从最叶子的调用的 ID 到最外层的内联调用。
‘noalias.addrspace
’ 元数据¶
noalias.addrspace
元数据用于标识无法访问在地址空间范围内分配的对象的内存操作。它附加到内存指令,包括 atomicrmw、cmpxchg 和 call 指令。
这遵循与 range 元数据 相同的形式,只是字段条目必须是 i32 类型。解释与应用于 IR 值的数值地址空间相同。
示例
; %ptr cannot point to an object allocated in addrspace(5)
%rmw.valid = atomicrmw and ptr %ptr, i64 %value seq_cst, !noalias.addrspace !0
; Undefined behavior. The underlying object is allocated in one of the listed
; address spaces.
%alloca = alloca i64, addrspace(5)
%alloca.cast = addrspacecast ptr addrspace(5) %alloca to ptr
%rmw.ub = atomicrmw and ptr %alloca.cast, i64 %value seq_cst, !noalias.addrspace !0
!0 = !{i32 5, i32 6} ; Exclude addrspace(5) only
这旨在用于具有通用地址空间概念的目标,这些目标在运行时解析为不同的物理内存空间。地址空间值的解释是特定于目标的。如果运行时内存地址未解析为在指示的地址空间之一中定义的对象,则行为未定义。
模块标志元数据¶
关于整个模块的信息很难传达给 LLVM 的子系统。 LLVM IR 不足以传输此信息。 llvm.module.flags
命名元数据存在是为了促进这一点。这些标志采用键/值对的形式 — 很像字典 — 使任何关心标志的子系统都可以轻松查找它。
llvm.module.flags
元数据包含元数据三元组的列表。每个三元组具有以下形式
第一个元素是行为标志,它指定当两个(或多个)模块合并在一起时,并且遇到两个(或多个)具有相同 ID 的元数据时的行为。下面描述了支持的行为。
第二个元素是元数据字符串,它是元数据的唯一 ID。每个模块对于每个唯一 ID 只能有一个标志条目(不包括具有 Require 行为的条目)。
第三个元素是标志的值。
当两个(或多个)模块合并在一起时,生成的 llvm.module.flags
元数据是模块标志的并集。也就是说,对于每个唯一的元数据 ID 字符串,合并模块的 llvm.module.flags
元数据表中将恰好有一个条目,并且该条目的值将由合并行为标志确定,如下所述。唯一的例外是具有 Require 行为的条目始终保留。
支持以下行为
值 |
行为 |
---|---|
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
对于特定的唯一标志 ID,具有多种行为是错误的,除非在 Require(添加对另一个元数据值的限制)或 Override 的情况下。
模块标志示例
!0 = !{ i32 1, !"foo", i32 1 }
!1 = !{ i32 4, !"bar", i32 37 }
!2 = !{ i32 2, !"qux", i32 42 }
!3 = !{ i32 3, !"qux",
!{
!"foo", i32 1
}
}
!llvm.module.flags = !{ !0, !1, !2, !3 }
元数据
!0
具有 ID!"foo"
和值 ‘1’。如果看到两个或多个!"foo"
标志,则如果它们的值不相等,则行为是发出错误。元数据
!1
具有 ID!"bar"
和值 ‘37’。如果看到两个或多个!"bar"
标志,则行为是使用值 ‘37’。元数据
!2
具有 ID!"qux"
和值 ‘42’。如果看到两个或多个!"qux"
标志,则如果它们的值不相等,则行为是发出警告。元数据
!3
具有 ID!"qux"
和值!{ !"foo", i32 1 }
该行为是,如果
llvm.module.flags
在执行链接后不包含具有值 ‘1’ 的 ID 为!"foo"
的标志,则发出错误。
合成函数模块标志元数据¶
这些元数据指定合成函数应具有的默认属性。这些元数据目前受到一些 instrumentation pass 的尊重,例如 sanitizers。
这些元数据对应于一些具有重要代码生成行为的函数属性。仅具有优化目的的函数属性不应列出,因为这些合成函数的性能影响很小。
“frame-pointer”:Max。该值可以是 0、1 或 2。合成函数将获得 “frame-pointer” 函数属性,其值分别为 “none”、“non-leaf” 或 “all”。
“function_return_thunk_extern”:合成函数将获得
fn_return_thunk_extern
函数属性。“uwtable”:Max。该值可以是 0、1 或 2。如果值为 1,则合成函数将获得
uwtable(sync)
函数属性;如果值为 2,则合成函数将获得uwtable(async)
函数属性。
Objective-C 垃圾回收模块标志元数据¶
在 Mach-O 平台上,Objective-C 将关于垃圾回收的元数据存储在一个名为“image info”的特殊 section 中。该元数据由一个版本号和一个位掩码组成,位掩码指定了文件支持的垃圾回收类型(如果有)。如果两个或多个模块链接在一起,它们的垃圾回收元数据需要合并,而不是简单地附加在一起。
Objective-C 垃圾回收模块标志元数据由以下键值对组成
键 |
值 |
---|---|
|
[必需] — Objective-C ABI 版本。有效值为 1 和 2。 |
|
[必需] — image info section 的版本。目前始终为 0。 |
|
[必需] — 放置元数据的 section。对于 Objective-C ABI 版本 1,有效值为 |
|
[必需] — 指定是否支持垃圾回收。有效值为 0(不支持垃圾回收)和 2(支持垃圾回收)。 |
|
[可选] — 指定仅支持垃圾回收。如果存在,其值必须为 6。此标志要求 |
一些重要的标志交互
如果将
Objective-C Garbage Collection
设置为 0 的模块与Objective-C Garbage Collection
设置为 2 的模块合并,则生成的模块的Objective-C Garbage Collection
标志将设置为 0。无法将
Objective-C Garbage Collection
设置为 0 的模块与Objective-C GC Only
设置为 6 的模块合并。
C 类型宽度模块标志元数据¶
ARM 后端会向每个生成的对象文件发出一个 section,以描述它编译时使用的选项(以编译器无关的方式),以防止链接不兼容的对象,并允许自动库选择。其中一些选项在 IR 级别不可见,即 wchar_t 宽度和 enum 宽度。
为了将此信息传递给后端,这些选项被编码在模块标志元数据中,使用以下键值对
键 |
值 |
---|---|
short_wchar |
|
short_enum |
|
例如,以下元数据 section 指定模块使用 4 字节的 wchar_t
宽度编译,并且枚举的基础类型是可以表示其所有值的最小类型
!llvm.module.flags = !{!0, !1}
!0 = !{i32 1, !"short_wchar", i32 1}
!1 = !{i32 1, !"short_enum", i32 0}
栈对齐元数据¶
更改目标 ABI 隐式默认栈对齐方式的默认栈对齐方式。接受一个 i32 值,单位为字节。将具有不同此元数据值的两个模块链接在一起被认为是错误。
例如
!llvm.module.flags = !{!0} !0 = !{i32 1, !”override-stack-alignment”, i32 8}
这将把栈对齐方式更改为 8B。
嵌入对象名称元数据¶
Offloading 编译需要将设备代码嵌入到宿主 section 表中以创建胖二进制文件。此元数据节点引用模块中将要嵌入的每个全局变量。此元数据的主要用途是使在 IR 中更有效地引用这些全局变量。元数据引用包含指向要嵌入的全局变量的指针的节点,后跟它将存储在其中的 section 名称
!llvm.embedded.objects = !{!0}
!0 = !{ptr @object, !".section"}
自动链接器标志命名元数据¶
某些目标支持将标志嵌入到单个对象文件中的链接器。通常,这与语言扩展结合使用,语言扩展允许源文件包含链接器命令行选项,并使这些选项通过对象文件自动传输到链接器。
这些标志使用名为 !llvm.linker.options
的命名元数据在 IR 中进行编码。每个操作数都应为一个元数据节点,该节点应为其他元数据节点的列表,其中每个节点应为元数据字符串列表,用于定义链接器选项。
例如,以下元数据 section 指定了两组独立的链接器选项,大概是为了链接 libz
和 Cocoa
框架
!0 = !{ !"-lz" }
!1 = !{ !"-framework", !"Cocoa" }
!llvm.linker.options = !{ !0, !1 }
选择将元数据编码为选项列表的列表,而不是折叠的选项列表,以便 IR 编码可以使用多个选项字符串来指定例如单个库,同时仍然使该说明符保留为原子元素,可以被目标特定的汇编编写器或对象文件发射器识别。
每个单独的选项都必须是目标链接器的有效选项,或者是目标特定的汇编编写器或对象文件发射器保留的选项。IR 不定义这些选项的其他方面。
依赖库命名元数据¶
某些目标支持将字符串嵌入到对象文件中,以指示要添加到链接的一组库。通常,这与语言扩展结合使用,语言扩展允许源文件显式声明它们依赖的库,并使这些库通过对象文件自动传输到链接器。
该列表使用名为 !llvm.dependent-libraries
的命名元数据在 IR 中进行编码。每个操作数都应为一个元数据节点,该节点应包含单个字符串操作数。
例如,以下元数据 section 包含两个库说明符
!0 = !{!"a library specifier"}
!1 = !{!"another library specifier"}
!llvm.dependent-libraries = !{ !0, !1 }
每个库说明符将由使用者链接器独立处理。库说明符的效果由使用者链接器定义。
ThinLTO 摘要¶
使用 ThinLTO 编译会导致构建模块的紧凑摘要,该摘要被发出到 bitcode 中。摘要被发出到 LLVM 汇编中,并通过插入符号 (’^
’) 在语法中标识。
摘要与模块 IR 一起被解析为 bitcode 输出,通过 “llvm-as
” 工具。为优化目的解析模块 IR 的工具(例如 “clang -x ir
” 和 “opt
”),将忽略摘要条目(就像它们目前忽略 bitcode 输入文件中的摘要条目一样)。
最终,摘要将被解析为 ModuleSummaryIndex 对象,条件与当前从 bitcode 构建摘要索引的条件相同。具体来说,测试 ThinLTO 编译的 Thin Link 部分的工具(即 llvm-lto 和 llvm-lto2),或者当通过 clang 的 “-fthinlto-index=<>
” 标志解析分布式 ThinLTO 后端的组合索引时(这部分尚未实现,现在使用 llvm-as 创建 bitcode 对象,然后再馈送到 thin link 工具中)。
LLVM 汇编中目前有 3 种类型的摘要条目:模块路径、全局值 和 类型标识符。
模块路径摘要条目¶
每个模块路径摘要条目列出包含在摘要中的全局值的模块。对于单个 IR 模块,将有一个这样的条目,但在 thin link 期间生成的组合摘要索引中,每个具有摘要的链接模块将有一个模块路径条目。
示例
^0 = module: (path: "/path/to/file.o", hash: (2468601609, 1329373163, 1565878005, 638838075, 3148790418))
path
字段是 bitcode 文件的字符串路径,hash
字段是 IR bitcode 内容的 160 位 SHA-1 哈希值,用于增量构建和缓存。
全局值摘要条目¶
每个全局值摘要条目对应于由摘要模块定义或引用的全局值。
示例
^4 = gv: (name: "f"[, summaries: (Summary)[, (Summary)]*]?) ; guid = 14740650423002898831
对于声明,将没有摘要列表。对于定义,全局值将包含摘要列表,每个摘要列表对应于包含定义的模块。对于具有弱链接的符号,在组合摘要索引中可以有多个条目。
每个 Summary
格式将取决于全局值是 函数、变量 还是 别名。
函数摘要¶
如果全局值是一个函数,则 Summary
条目将如下所示
function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2[, FuncFlags]?[, Calls]?[, TypeIdInfo]?[, Params]?[, Refs]?
module
字段包含包含此定义的模块的摘要条目 ID,flags
字段包含诸如链接类型、指示是否允许导入定义的标志、它是否全局存活以及链接器是否将其解析为本地定义的信息(后两者在 thin link 期间填充)。insts
字段包含函数中 IR 指令的数量。最后,有几个可选字段:FuncFlags、Calls、TypeIdInfo、Params、Refs。
全局变量摘要¶
如果全局值是一个变量,则 Summary
条目将如下所示
variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)[, Refs]?
变量条目包含 函数摘要 中字段的子集,请参阅那里的描述。
别名摘要¶
如果全局值是一个别名,则 Summary
条目将如下所示
alias: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), aliasee: ^2)
module
和 flags
字段的描述与 函数摘要 相同。aliasee
字段包含对 aliasee 的全局值摘要条目的引用。
函数标志¶
可选的 FuncFlags
字段如下所示
funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0)
如果未指定,则假定标志持有保守的 false
值 0
。
调用¶
可选的 Calls
字段如下所示
calls: ((Callee)[, (Callee)]*)
其中每个 Callee
如下所示
callee: ^1[, hotness: None]?[, relbf: 0]?
callee
引用被调用者的摘要条目 ID。在 hotness
(可以取值 Unknown
、Cold
、None
、Hot
和 Critical
)和 relbf
(它保存相对于条目频率缩放 2^8 倍的整数分支频率)中,最多可以指定一个。Unknown
和 0
分别为默认值。
参数¶
可选的 Params
由 StackSafety
使用,如下所示
Params: ((Param)[, (Param)]*)
其中每个 Param
描述函数内部的指针参数访问,如下所示
param: 4, offset: [0, 5][, calls: ((Callee)[, (Callee)]*)]?
其中第一个 param
是它描述的参数的编号,offset
是从指针参数到函数可以访问的字节的包含偏移量范围。此范围不包括来自 calls
列表的函数调用的访问。
其中每个 Callee
描述参数如何转发到其他函数,如下所示
callee: ^3, param: 5, offset: [-3, 3]
callee
引用被调用者的摘要条目 ID,param
是被调用者参数的编号,该参数指向调用者的参数,偏移量已知在 offset
范围内。calls
将被 thin link 阶段消耗和删除,以更新 Param::offset
,以便它涵盖 calls
可能的所有访问。
没有相应 Param
的指针参数被认为是不安全的,我们假设可以使用任何偏移量进行访问。
示例
如果我们有以下函数
define i64 @foo(ptr %0, ptr %1, ptr %2, i8 %3) {
store ptr %1, ptr @x
%5 = getelementptr inbounds i8, ptr %2, i64 5
%6 = load i8, ptr %5
%7 = getelementptr inbounds i8, ptr %2, i8 %3
tail call void @bar(i8 %3, ptr %7)
%8 = load i64, ptr %0
ret i64 %8
}
我们可以预期如下记录
params: ((param: 0, offset: [0, 7]),(param: 2, offset: [5, 5], calls: ((callee: ^3, param: 1, offset: [-128, 127]))))
该函数可能只访问参数 %0 的 8 个字节。calls
为空,因此该参数要么不用于函数调用,要么 offset
已经涵盖了来自嵌套函数调用的所有访问。参数 %1 逸出,因此访问未知。函数本身只能访问参数 %2 的单个字节。在 @bar
或 ^3
内部可能会进行额外的访问。该函数将有符号偏移量添加到指针,并将结果作为参数 %1 传递到 ^3
中。此记录本身并未告诉我们 ^3
将如何访问参数。参数 %3 不是指针。
Refs¶
可选的 Refs
字段如下所示
refs: ((Ref)[, (Ref)]*)
其中每个 Ref
包含对引用的值的摘要 ID 的引用(例如 ^1
)。
TypeIdInfo¶
可选的 TypeIdInfo
字段,用于 控制流完整性,如下所示
typeIdInfo: [(TypeTests)]?[, (TypeTestAssumeVCalls)]?[, (TypeCheckedLoadVCalls)]?[, (TypeTestAssumeConstVCalls)]?[, (TypeCheckedLoadConstVCalls)]?
这些可选字段具有以下形式
TypeTests¶
typeTests: (TypeIdRef[, TypeIdRef]*)
其中每个 TypeIdRef
通过摘要 ID 或 GUID
引用 类型 ID。
TypeTestAssumeVCalls¶
typeTestAssumeVCalls: (VFuncId[, VFuncId]*)
其中每个 VFuncId 的格式如下
vFuncId: (TypeIdRef, offset: 16)
其中每个 TypeIdRef
通过摘要 ID 或以 guid:
标签开头的 GUID
引用 类型 ID。
TypeCheckedLoadVCalls¶
typeCheckedLoadVCalls: (VFuncId[, VFuncId]*)
其中每个 VFuncId 的格式与 TypeTestAssumeVCalls
描述的格式相同。
TypeTestAssumeConstVCalls¶
typeTestAssumeConstVCalls: (ConstVCall[, ConstVCall]*)
其中每个 ConstVCall 的格式如下
(VFuncId, args: (Arg[, Arg]*))
其中每个 VFuncId 的格式与 TypeTestAssumeVCalls
描述的格式相同,每个 Arg 都是一个整数参数编号。
TypeCheckedLoadConstVCalls¶
typeCheckedLoadConstVCalls: (ConstVCall[, ConstVCall]*)
其中每个 ConstVCall 的格式与 TypeTestAssumeConstVCalls
描述的格式相同。
类型 ID 摘要条目¶
每个类型 ID 摘要条目都对应于在构建 控制流完整性 时编译的 LTO 链接部分期间生成的类型标识符解析,因此这些条目仅存在于组合摘要索引中。
示例
^4 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7[, alignLog2: 0]?[, sizeM1: 0]?[, bitMask: 0]?[, inlineBits: 0]?)[, WpdResolutions]?)) ; guid = 7004155349499253778
typeTestRes
给出了类型测试解析 kind
(可以是 unsat
、byteArray
、inline
、single
或 allOnes
)和 size-1
位宽度。 之后是可选标志(默认为 0)和一个可选的 WpdResolutions(全程序去虚拟化解析)字段,如下所示
wpdResolutions: ((offset: 0, WpdRes)[, (offset: 1, WpdRes)]*
其中每个条目都是从给定字节偏移量到全程序去虚拟化解析 WpdRes 的映射,WpdRes 具有以下格式之一
wpdRes: (kind: branchFunnel)
wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi")
wpdRes: (kind: indir)
此外,每个 wpdRes 都有一个可选的 resByArg
字段,该字段描述了具有所有常量整数参数的调用的解析
resByArg: (ResByArg[, ResByArg]*)
其中 ResByArg 是
args: (Arg[, Arg]*), byArg: (kind: UniformRetVal[, info: 0][, byte: 0][, bit: 0])
其中 kind
可以是 Indir
、UniformRetVal
、UniqueRetVal
或 VirtualConstProp
。info
字段仅在 kind 为 UniformRetVal
(指示统一返回值)或 UniqueRetVal
(保存与唯一 vtable 关联的返回值(0 或 1))时使用。byte
和 bit
字段仅在目标不支持使用绝对符号来存储常量时使用。
固有全局变量¶
LLVM 有许多“魔法”全局变量,其中包含影响代码生成或其他 IR 语义的数据。这些在此处记录。所有此类全局变量都应将 section 指定为 “llvm.metadata
”。此 section 和所有以 “llvm.
” 开头的全局变量都保留供 LLVM 使用。
‘llvm.used
’ 全局变量¶
@llvm.used
全局变量是一个数组,它具有附加链接。此数组包含指向命名全局变量、函数和别名的指针列表,这些指针可以选择性地具有由 bitcast 或 getelementptr 形成的指针转换。例如,它的合法用法是
@X = global i8 4
@Y = global i32 123
@llvm.used = appending global [2 x ptr] [
ptr @X,
ptr @Y
], section "llvm.metadata"
如果一个符号出现在 @llvm.used
列表中,则编译器、汇编器和链接器必须将该符号视为好像存在对它的引用,而它看不到该引用(这就是为什么它们必须被命名)。例如,如果一个变量具有内部链接,并且除了来自 @llvm.used
列表的引用之外没有其他引用,则不能删除它。这通常用于表示来自内联汇编和其他编译器“看不到”的东西的引用,并且对应于 GNU C 中的 “attribute((used))
”。
在某些目标上,代码生成器必须发出指令给汇编器或对象文件,以防止汇编器和链接器删除该符号。
‘llvm.compiler.used
’ 全局变量¶
@llvm.compiler.used
指令与 @llvm.used
指令相同,不同之处在于它仅阻止编译器接触该符号。在支持它的目标上,这允许智能链接器优化对符号的引用,而不会像 @llvm.used
那样受到阻碍。
这是一个罕见的构造,仅应在罕见的情况下使用,并且不应暴露给源语言。
‘llvm.global_ctors
’ 全局变量¶
%0 = type { i32, ptr, ptr }
@llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, ptr @ctor, ptr @data }]
@llvm.global_ctors
数组包含构造函数、优先级和关联的全局变量或函数的列表。当模块加载时,将按优先级升序(即最低优先级先调用)调用此数组引用的函数。未定义具有相同优先级的函数的顺序。
如果第三个字段为非空,并且指向全局变量或函数,则只有在当前模块中的关联数据未被丢弃时,才会运行初始化函数。在 ELF 上,引用的全局变量或函数必须在 comdat 中。
‘llvm.global_dtors
’ 全局变量¶
%0 = type { i32, ptr, ptr }
@llvm.global_dtors = appending global [1 x %0] [%0 { i32 65535, ptr @dtor, ptr @data }]
@llvm.global_dtors
数组包含析构函数、优先级和关联的全局变量或函数的列表。当模块卸载时,将按优先级降序(即最高优先级先调用)调用此数组引用的函数。未定义具有相同优先级的函数的顺序。
如果第三个字段为非空,并且指向全局变量或函数,则只有在当前模块中的关联数据未被丢弃时,才会运行析构函数。在 ELF 上,引用的全局变量或函数必须在 comdat 中。
指令参考¶
LLVM 指令集由几个不同分类的指令组成:终止符指令、二进制指令、位运算二进制指令、内存指令 和 其他指令。还有 调试记录,它们本身不是指令,但与指令交错打印,以描述程序执行的每个位置的程序调试信息状态的变化。
终止符指令¶
如先前所述,程序中的每个基本块都以“终止符”指令结尾,该指令指示在当前块完成后应执行哪个块。这些终止符指令通常产生 ‘void
’ 值:它们产生控制流,而不是值(唯一的例外是 ‘invoke’ 指令)。
终止符指令包括:‘ret’、‘br’、‘switch’、‘indirectbr’、‘invoke’、‘callbr’ ‘resume’、‘catchswitch’、‘catchret’、‘cleanupret’ 和 ‘unreachable’。
‘ret
’ 指令¶
语法:¶
ret <type> <value> ; Return a value from a non-void function
ret void ; Return from void function
概述:¶
‘ret
’ 指令用于从函数返回控制流(并可选择返回值)到调用者。
‘ret
’ 指令有两种形式:一种返回一个值然后引起控制流,另一种仅引起控制流的发生。
参数:¶
‘ret
’ 指令可以选择接受一个参数,即返回值。返回值的类型必须是 ‘first class’ 类型。
如果函数具有非 void 返回类型,但包含没有返回值的 ‘ret
’ 指令,或返回值的类型与其类型不匹配,或者如果函数具有 void 返回类型,但包含带有返回值的 ‘ret
’ 指令,则该函数不是 well formed 的。
语义:¶
当 ‘ret
’ 指令被执行时,控制流返回到调用函数的上下文。如果调用者是 “call” 指令,则执行在 call 指令之后继续。如果调用者是 “invoke” 指令,则执行在 “normal” 目标块的开头继续。如果该指令返回一个值,则该值应设置 call 或 invoke 指令的返回值。
示例:¶
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
’ 指令¶
语法:¶
br i1 <cond>, label <iftrue>, label <iffalse>
br label <dest> ; Unconditional branch
概述:¶
‘br
’ 指令用于使控制流转移到当前函数中的不同基本块。此指令有两种形式,分别对应于条件分支和无条件分支。
参数:¶
‘br
’ 指令的条件分支形式接受一个 ‘i1
’ 值和两个 ‘label
’ 值。‘br
’ 指令的无条件形式接受一个 ‘label
’ 值作为目标。
语义:¶
在执行条件 ‘br
’ 指令时,将评估 ‘i1
’ 参数。如果值为 true
,则控制流转到 ‘iftrue
’ label
参数。如果 “cond” 为 false
,则控制流转到 ‘iffalse
’ label
参数。如果 ‘cond
’ 是 poison
或 undef
,则此指令具有未定义的行为。
示例:¶
Test:
%cond = icmp eq i32 %a, %b
br i1 %cond, label %IfEqual, label %IfUnequal
IfEqual:
ret i32 1
IfUnequal:
ret i32 0
‘switch
’ 指令¶
语法:¶
switch <intty> <value>, label <defaultdest> [ <intty> <val>, label <dest> ... ]
概述:¶
‘switch
’ 指令用于将控制流转移到多个不同的位置之一。它是 ‘br
’ 指令的泛化,允许分支到多个可能的目的地之一。
参数:¶
‘switch
’ 指令使用三个参数:一个整数比较值 ‘value
’,一个默认 ‘label
’ 目的地,以及一个比较值常量和 ‘label
’ 对的数组。该表不允许包含重复的常量条目。
语义:¶
switch
指令指定一个值和目的地的表。当执行 ‘switch
’ 指令时,将搜索此表以查找给定的值。如果找到该值,则控制流转移到相应的目的地;否则,控制流转移到默认目的地。如果 ‘value
’ 是 poison
或 undef
,则此指令具有未定义的行为。
实现:¶
根据目标机器的属性和特定的 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
’ 指令¶
语法:¶
indirectbr ptr <address>, [ label <dest1>, label <dest2>, ... ]
概述:¶
‘indirectbr
’ 指令实现到当前函数内标签的间接分支,其地址由 “address
” 指定。地址必须从 blockaddress 常量派生而来。
参数:¶
‘address
’ 参数是要跳转到的标签的地址。其余参数指示地址可能指向的完整的目标集合。块可以多次出现在目标列表中,尽管这没什么用处。
需要此目标列表,以便数据流分析可以准确地了解 CFG。
语义:¶
控制转移到 address 参数中指定的块。所有可能的目标块都必须在标签列表中列出,否则此指令具有未定义的行为。这意味着跳转到其他函数中定义的标签也具有未定义的行为。如果 ‘address
’ 是 poison
或 undef
,则此指令具有未定义的行为。
实现:¶
这通常通过寄存器跳转来实现。
示例:¶
indirectbr ptr %Addr, [ label %bb1, label %bb2, label %bb3 ]
‘invoke
’ 指令¶
语法:¶
<result> = invoke [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
[operand bundles] to label <normal label> unwind label <exception label>
概述:¶
‘invoke
’ 指令使控制转移到指定的函数,并有可能将控制流转移到 ‘normal
’ 标签或 ‘exception
’ 标签。如果被调用函数通过 “ret
” 指令返回,则控制流将返回到 “normal” 标签。如果被调用者(或任何间接被调用者)通过 “resume” 指令或其他异常处理机制返回,则控制被中断并在动态最近的 “exception” 标签处继续。
‘exception
’ 标签是异常的 着陆点。因此,‘exception
’ 标签需要具有 “landingpad” 指令,该指令包含有关在展开发生后程序行为的信息,作为其第一个非 PHI 指令。“landingpad
” 指令的限制将其与 “invoke
” 指令紧密耦合,以便包含在 “landingpad
” 指令中的重要信息不会因正常的代码移动而丢失。
参数:¶
此指令需要多个参数
可选的 “cconv” 标记指示调用应使用的 调用约定。如果未指定,则调用默认为使用 C 调用约定。
返回值的可选 参数属性 列表。此处仅 ‘
zeroext
’、‘signext
’、‘noext
’ 和 ‘inreg
’ 属性有效。可选的 addrspace 属性可用于指示被调用函数的地址空间。如果未指定,将使用来自 datalayout 字符串 的程序地址空间。
‘
ty
’:调用指令本身的类型,也是返回值的类型。不返回值的函数标记为void
。‘
fnty
’:应是被调用函数的签名。参数类型必须与此签名暗示的类型匹配。如果函数不是 varargs,则可以省略此类型。‘
fnptrval
’:包含指向要调用的函数的指针的 LLVM 值。在大多数情况下,这是直接函数调用,但间接invoke
也是可能的,调用指向函数值的任意指针。‘
function args
’:参数列表,其类型与函数签名参数类型和参数属性匹配。所有参数都必须是 first class 类型。如果函数签名指示函数接受可变数量的参数,则可以指定额外的参数。‘
normal label
’:当被调用函数执行 ‘ret
’ 指令时到达的标签。‘
exception label
’:当被调用者通过 resume 指令或其他异常处理机制返回时到达的标签。可选的 函数属性 列表。
可选的 操作数包 列表。
语义:¶
此指令旨在在大多数方面作为标准 ‘call
’ 指令运行。主要区别在于它与一个标签建立关联,运行时库使用该标签来展开堆栈。
此指令用于具有析构函数的语言,以确保在发生 longjmp
或抛出异常的情况下执行适当的清理。此外,这对于实现支持它们的更高级语言中的 ‘catch
’ 子句非常重要。
为了 SSA 形式的目的,‘invoke
’ 指令返回值的定义被认为发生在从当前块到 “normal” 标签的边上。如果被调用者展开,则没有返回值可用。
示例:¶
%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
’ 指令¶
语法:¶
<result> = callbr [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
[operand bundles] to label <fallthrough label> [indirect labels]
概述:¶
‘callbr
’ 指令使控制转移到指定的函数,并有可能将控制流转移到 ‘fallthrough
’ 标签或 ‘indirect
’ 标签之一。
此指令应仅用于实现 gcc 风格内联汇编的 “goto” 功能。任何其他用法在 IR 验证器中都是错误的。
请注意,为了支持沿间接边的输出,LLVM 可能需要拆分关键边,这可能需要为 indirect labels
合成替换块。因此,另一个 callbr
指令看到的标签地址,或 blockaddress 常量的标签地址,可能不等于为此指令的 indirect labels
操作数提供的同一块的地址。汇编代码可能仅将控制转移到通过此指令的 indirect labels
提供的地址。
参数:¶
此指令需要多个参数
可选的 “cconv” 标记指示调用应使用的 调用约定。如果未指定,则调用默认为使用 C 调用约定。
返回值的可选 参数属性 列表。此处仅 ‘
zeroext
’、‘signext
’、‘noext
’ 和 ‘inreg
’ 属性有效。可选的 addrspace 属性可用于指示被调用函数的地址空间。如果未指定,将使用来自 datalayout 字符串 的程序地址空间。
‘
ty
’:调用指令本身的类型,也是返回值的类型。不返回值的函数标记为void
。‘
fnty
’:应是被调用函数的签名。参数类型必须与此签名暗示的类型匹配。如果函数不是 varargs,则可以省略此类型。‘
fnptrval
’:包含指向要调用的函数的指针的 LLVM 值。在大多数情况下,这是直接函数调用,但其他callbr
也是可能的,调用指向函数值的任意指针。‘
function args
’:参数列表,其类型与函数签名参数类型和参数属性匹配。所有参数都必须是 first class 类型。如果函数签名指示函数接受可变数量的参数,则可以指定额外的参数。‘
fallthrough label
’:当内联汇编的执行退出底部时到达的标签。‘
indirect labels
’:当被调用者将控制转移到 ‘fallthrough label
’ 以外的位置时到达的标签。标签约束引用这些目的地。可选的 函数属性 列表。
可选的 操作数包 列表。
语义:¶
此指令旨在在大多数方面作为标准 ‘call
’ 指令运行。主要区别在于它与附加标签建立关联,以定义调用后控制流的去向。
‘callbr
’ 指令的输出值在 ‘fallthrough
’ 块和任何 ‘indirect
’ 块中都可用。
今天这唯一的用途是实现 gcc 内联汇编的 “goto” 功能,其中可以提供额外的标签作为内联汇编跳转到的位置。
示例:¶
; "asm goto" without output constraints.
callbr void asm "", "r,!i"(i32 %x)
to label %fallthrough [label %indirect]
; "asm goto" with output constraints.
<result> = callbr i32 asm "", "=r,r,!i"(i32 %x)
to label %fallthrough [label %indirect]
‘resume
’ 指令¶
语法:¶
resume <type> <value>
概述:¶
‘resume
’ 指令是一个终止符指令,没有后继者。
参数:¶
‘resume
’ 指令需要一个参数,该参数的类型必须与同一函数中任何 ‘landingpad
’ 指令的结果类型相同。
语义:¶
‘resume
’ 指令恢复传播现有的(正在进行的)异常,其展开被 landingpad 指令中断。
示例:¶
resume { ptr, i32 } %exn
‘catchswitch
’ 指令¶
语法:¶
<resultval> = catchswitch within <parent> [ label <handler1>, label <handler2>, ... ] unwind to caller
<resultval> = catchswitch within <parent> [ label <handler1>, label <handler2>, ... ] unwind label <default>
概述:¶
‘catchswitch
’ 指令被 LLVM 的异常处理系统 用于描述可能由 EH personality routine 执行的一组可能的 catch 处理程序。
参数:¶
parent
参数是包含 catchswitch
指令的 funclet 的令牌。如果 catchswitch
不在 funclet 内部,则此操作数可以是令牌 none
。
default
参数是以 cleanuppad
或 catchswitch
指令开头的另一个基本块的标签。此展开目的地必须是关于 parent
链接的合法目标,如 异常处理文档 中所述。
handlers
是以 catchpad 指令开头的后继块的非空列表。
语义:¶
执行此指令会将控制转移到 handlers
中的后继者之一(如果适用),或者如果存在展开标签,则继续展开。
catchswitch
既是终止符指令,又是 “pad” 指令,这意味着它必须既是基本块中的第一个非 phi 指令,又是最后一个指令。因此,它必须是块中唯一的非 phi 指令。
示例:¶
dispatch1:
%cs1 = catchswitch within none [label %handler0, label %handler1] unwind to caller
dispatch2:
%cs2 = catchswitch within %parenthandler [label %handler0] unwind label %cleanup
‘catchret
’ 指令¶
语法:¶
catchret from <token> to label <normal>
概述:¶
‘catchret
’ 指令是一个终止符指令,它有一个后继者。
参数:¶
‘catchret
’ 的第一个参数指示它退出的 catchpad
。它必须是一个 catchpad。‘catchret
’ 的第二个参数指定控制将转移到的下一个位置。
语义:¶
‘catchret
’ 指令结束现有的(正在进行的)异常,其展开被 catchpad 指令中断。Personality 函数 有机会执行任意代码,例如,销毁活动异常。然后控制转移到 normal
。
token
参数必须是由 catchpad
指令生成的令牌。如果指定的 catchpad
不是最近进入的尚未退出的 funclet pad(如 EH 文档 中所述),则 catchret
的行为是未定义的。
示例:¶
catchret from %catch to label %continue
‘cleanupret
’ 指令¶
语法:¶
cleanupret from <value> unwind label <continue>
cleanupret from <value> unwind to caller
概述:¶
‘cleanupret
’ 指令是一个终止符指令,它有一个可选的后继者。
参数:¶
‘cleanupret
’ 指令需要一个参数,该参数指示它退出的 cleanuppad
,并且必须是一个 cleanuppad。如果指定的 cleanuppad
不是最近进入的尚未退出的 funclet pad(如 EH 文档 中所述),则 cleanupret
的行为是未定义的。
‘cleanupret
’ 指令还有一个可选的后继者 continue
,它必须是以 cleanuppad
或 catchswitch
指令开头的另一个基本块的标签。此展开目的地必须是关于 parent
链接的合法目标,如 异常处理文档 中所述。
语义:¶
‘cleanupret
’ 指令向 personality 函数 指示它转移控制的一个 cleanuppad 已结束。它将控制转移到 continue
或从函数中展开。
示例:¶
cleanupret from %cleanup unwind to caller
cleanupret from %cleanup unwind label %continue
‘unreachable
’ 指令¶
语法:¶
unreachable
概述:¶
‘unreachable
’ 指令没有定义的语义。此指令用于通知优化器代码的特定部分不可达。这可以用来指示在无返回函数之后的代码无法到达,以及其他事实。
语义:¶
‘unreachable
’ 指令没有定义的语义。
一元运算¶
一元运算符需要单个操作数,对其执行操作,并生成单个值。操作数可能表示多个数据,就像 vector 数据类型的情况一样。结果值与操作数类型相同。
‘fneg
’ 指令¶
语法:¶
<result> = fneg [fast-math flags]* <ty> <op1> ; yields ty:result
概述:¶
‘fneg
’ 指令返回其操作数的 negation (取反/负数)。
参数:¶
语义:¶
产生的值是操作数的副本,其符号位被翻转。该值在其他方面完全相同;特别地,如果输入是 NaN,则 quiet/signaling 位和有效负载将完全保留。
此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用原本不安全的浮点优化。
示例:¶
<result> = fneg float %val ; yields float:result = -%var
二元运算¶
二元运算符用于执行程序中的大多数计算。它们需要两个相同类型的操作数,对其执行操作,并生成单个值。操作数可能表示多个数据,就像 vector 数据类型的情况一样。结果值与操作数类型相同。
有几种不同的二元运算符
‘add
’ 指令¶
语法:¶
<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
概述:¶
‘add
’ 指令返回其两个操作数的和。
参数:¶
语义:¶
产生的值是两个操作数的整数和。
如果和具有无符号溢出,则返回的结果是数学结果模 2n,其中 n 是结果的位宽。
由于 LLVM 整数使用补码表示,因此此指令适用于有符号和无符号整数。
nuw
和 nsw
分别代表 “No Unsigned Wrap” 和 “No Signed Wrap”。如果存在 nuw
和/或 nsw
关键字,则如果分别发生无符号和/或有符号溢出,则 add
的结果值是 poison value。
示例:¶
<result> = add i32 4, %var ; yields i32:result = 4 + %var
‘fadd
’ 指令¶
语法:¶
<result> = fadd [fast-math flags]* <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘fadd
’ 指令返回其两个操作数的和。
参数:¶
语义:¶
产生的值是两个操作数的浮点和。假定此指令在默认 浮点环境 中执行。此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用原本不安全的浮点优化。
示例:¶
<result> = fadd float 4.0, %var ; yields float:result = 4.0 + %var
‘sub
’ 指令¶
语法:¶
<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
概述:¶
‘sub
’ 指令返回其两个操作数的差。
请注意,‘sub
’ 指令用于表示大多数其他中间表示中存在的 ‘neg
’ 指令。
参数:¶
语义:¶
产生的值是两个操作数的整数差。
如果差具有无符号溢出,则返回的结果是数学结果模 2n,其中 n 是结果的位宽。
由于 LLVM 整数使用补码表示,因此此指令适用于有符号和无符号整数。
nuw
和 nsw
分别代表 “No Unsigned Wrap”(无符号溢出回绕)和 “No Signed Wrap”(有符号溢出回绕)。如果存在 nuw
和/或 nsw
关键字,则当分别发生无符号和/或有符号溢出时,sub
的结果值将是一个 poison 值。
示例:¶
<result> = sub i32 4, %var ; yields i32:result = 4 - %var
<result> = sub i32 0, %val ; yields i32:result = -%var
‘fsub
’ 指令¶
语法:¶
<result> = fsub [fast-math flags]* <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘fsub
’ 指令返回其两个操作数的差值。
参数:¶
语义:¶
产生的值是两个操作数的浮点差值。此指令假定在默认的 浮点环境 中执行。此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用通常不安全的浮点优化。
示例:¶
<result> = fsub float 4.0, %var ; yields float:result = 4.0 - %var
<result> = fsub float -0.0, %val ; yields float:result = -%var
‘mul
’ 指令¶
语法:¶
<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
概述:¶
‘mul
’ 指令返回其两个操作数的乘积。
参数:¶
语义:¶
产生的值是两个操作数的整数乘积。
如果乘法结果发生无符号溢出,则返回的结果是数学结果模 2n,其中 n 是结果的位宽。
由于 LLVM 整数使用补码表示,并且结果的宽度与操作数相同,因此此指令为有符号和无符号整数返回正确的结果。如果需要完整的乘积(例如 i32
* i32
-> i64
),则应根据需要对操作数进行符号扩展或零扩展,以达到完整乘积的宽度。
nuw
和 nsw
分别代表 “No Unsigned Wrap”(无符号溢出回绕)和 “No Signed Wrap”(有符号溢出回绕)。如果存在 nuw
和/或 nsw
关键字,则当分别发生无符号和/或有符号溢出时,mul
的结果值将是一个 poison 值。
示例:¶
<result> = mul i32 4, %var ; yields i32:result = 4 * %var
‘fmul
’ 指令¶
语法:¶
<result> = fmul [fast-math flags]* <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘fmul
’ 指令返回其两个操作数的乘积。
参数:¶
语义:¶
产生的值是两个操作数的浮点乘积。此指令假定在默认的 浮点环境 中执行。此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用通常不安全的浮点优化。
示例:¶
<result> = fmul float 4.0, %var ; yields float:result = 4.0 * %var
‘udiv
’ 指令¶
语法:¶
<result> = udiv <ty> <op1>, <op2> ; yields ty:result
<result> = udiv exact <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘udiv
’ 指令返回其两个操作数的商。
参数:¶
语义:¶
产生的值是两个操作数的无符号整数商。
请注意,无符号整数除法和有符号整数除法是不同的运算;对于有符号整数除法,请使用 ‘sdiv
’。
除以零是未定义行为。对于向量,如果除数的任何元素为零,则该运算具有未定义行为。
如果存在 exact
关键字,则如果 %op1 不是 %op2 的倍数(因此,“((a udiv exact b) mul b) == a”),则 udiv
的结果值将是一个 poison 值。
示例:¶
<result> = udiv i32 4, %var ; yields i32:result = 4 / %var
‘sdiv
’ 指令¶
语法:¶
<result> = sdiv <ty> <op1>, <op2> ; yields ty:result
<result> = sdiv exact <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘sdiv
’ 指令返回其两个操作数的商。
参数:¶
语义:¶
产生的值是两个操作数的有符号整数商,结果向零舍入。
请注意,有符号整数除法和无符号整数除法是不同的运算;对于无符号整数除法,请使用 ‘udiv
’。
除以零是未定义行为。对于向量,如果除数的任何元素为零,则该运算具有未定义行为。溢出也会导致未定义行为;这是一种罕见的情况,但可能会发生,例如,对 -2147483648 除以 -1 进行 32 位除法。
如果存在 exact
关键字,则如果结果将被舍入,则 sdiv
的结果值将是一个 poison 值。
示例:¶
<result> = sdiv i32 4, %var ; yields i32:result = 4 / %var
‘fdiv
’ 指令¶
语法:¶
<result> = fdiv [fast-math flags]* <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘fdiv
’ 指令返回其两个操作数的商。
参数:¶
语义:¶
产生的值是两个操作数的浮点商。此指令假定在默认的 浮点环境 中执行。此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用通常不安全的浮点优化。
示例:¶
<result> = fdiv float 4.0, %var ; yields float:result = 4.0 / %var
‘urem
’ 指令¶
语法:¶
<result> = urem <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘urem
’ 指令返回其两个参数的无符号除法运算的余数。
参数:¶
语义:¶
此指令返回除法的无符号整数余数。此指令始终执行无符号除法以获得余数。
请注意,无符号整数余数和有符号整数余数是不同的运算;对于有符号整数余数,请使用 ‘srem
’。
对零取余是未定义行为。对于向量,如果除数的任何元素为零,则该运算具有未定义行为。
示例:¶
<result> = urem i32 4, %var ; yields i32:result = 4 % %var
‘srem
’ 指令¶
语法:¶
<result> = srem <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘srem
’ 指令返回其两个操作数的有符号除法运算的余数。此指令还可以接受值的 向量 版本,在这种情况下,元素必须是整数。
参数:¶
语义:¶
此指令返回除法的余数(结果为零或与被除数 op1
的符号相同),而不是值的模运算符(结果为零或与除数 op2
的符号相同)。有关差异的更多信息,请参阅 The Math Forum。有关如何在各种语言中实现此操作的表格,请参阅 Wikipedia: modulo operation。
请注意,有符号整数余数和无符号整数余数是不同的运算;对于无符号整数余数,请使用 ‘urem
’。
对零取余是未定义行为。对于向量,如果除数的任何元素为零,则该运算具有未定义行为。溢出也会导致未定义行为;这是一种罕见的情况,但可能会发生,例如,对 -2147483648 除以 -1 进行 32 位除法取余。(余数实际上不会溢出,但此规则允许使用返回除法结果和余数的指令来实现 srem。)
示例:¶
<result> = srem i32 4, %var ; yields i32:result = 4 % %var
‘frem
’ 指令¶
语法:¶
<result> = frem [fast-math flags]* <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘frem
’ 指令返回其两个操作数除法运算的余数。
注意
该指令实现为对 libm 的 ‘fmod
’ 的调用,对于某些目标,使用该指令可能因此需要链接 libm。
参数:¶
语义:¶
产生的值是两个操作数的浮点余数。这与 libm ‘fmod
’ 函数的输出相同,但不具有设置 errno
的任何可能性。余数与被除数具有相同的符号。此指令假定在默认的 浮点环境 中执行。此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用通常不安全的浮点优化。
示例:¶
<result> = frem float 4.0, %var ; yields float:result = 4.0 % %var
按位二进制运算¶
按位二进制运算符用于在程序中进行各种形式的位操作。它们通常是非常高效的指令,并且通常可以从其他指令中进行强度缩减。它们需要两个相同类型的操作数,对它们执行运算,并产生单个值。结果值的类型与其操作数的类型相同。
‘shl
’ 指令¶
语法:¶
<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
概述:¶
‘shl
’ 指令返回第一个操作数向左移动指定位数的结果。
参数:¶
语义:¶
产生的值是 op1
* 2op2 mod 2n,其中 n
是结果的位宽。如果 op2
(静态或动态地)等于或大于 op1
中的位数,则此指令返回一个 poison 值。如果参数是向量,则 op1
的每个向量元素都按 op2
中对应的移位量进行移位。
如果存在 nuw
关键字,则如果移出任何非零位,则移位产生 poison 值。 如果存在 nsw
关键字,则如果移出任何与结果符号位不一致的位,则移位产生 poison 值。
示例:¶
<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
’ 指令¶
语法:¶
<result> = lshr <ty> <op1>, <op2> ; yields ty:result
<result> = lshr exact <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘lshr
’ 指令(逻辑右移)返回第一个操作数向右移动指定位数的结果,并用零填充。
参数:¶
语义:¶
此指令始终执行逻辑右移操作。结果的最高有效位将在移位后用零位填充。如果 op2
(静态或动态地)等于或大于 op1
中的位数,则此指令返回一个 poison 值。如果参数是向量,则 op1
的每个向量元素都按 op2
中对应的移位量进行移位。
如果存在 exact
关键字,则如果任何移出的位为非零,则 lshr
的结果值将是一个 poison 值。
示例:¶
<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
’ 指令¶
语法:¶
<result> = ashr <ty> <op1>, <op2> ; yields ty:result
<result> = ashr exact <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘ashr
’ 指令(算术右移)返回第一个操作数向右移动指定位数的结果,并进行符号扩展。
参数:¶
语义:¶
此指令始终执行算术右移操作。结果的最高有效位将用 op1
的符号位填充。如果 op2
(静态或动态地)等于或大于 op1
中的位数,则此指令返回一个 poison 值。如果参数是向量,则 op1
的每个向量元素都按 op2
中对应的移位量进行移位。
如果存在 exact
关键字,则如果任何移出的位为非零,则 ashr
的结果值将是一个 poison 值。
示例:¶
<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
’ 指令¶
语法:¶
<result> = and <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘and
’ 指令返回其两个操作数的按位逻辑与。
参数:¶
语义:¶
‘and
’ 指令使用的真值表是
In0 |
In1 |
Out |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
示例:¶
<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
’ 指令¶
语法:¶
<result> = or <ty> <op1>, <op2> ; yields ty:result
<result> = or disjoint <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘or
’ 指令返回其两个操作数的按位逻辑或。
参数:¶
语义:¶
‘or
’ 指令使用的真值表是
In0 |
In1 |
Out |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
disjoint
意味着对于每个位,该位在至少一个输入中为零。这允许将 Or 视为 Add,因为任何位都不会发生进位。如果存在 disjoint 关键字,则如果两个输入在同一位位置都为 1,则 or
的结果值将是一个 poison 值。对于向量,只有包含该位的元素是 poison 值。
示例:¶
<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
’ 指令¶
语法:¶
<result> = xor <ty> <op1>, <op2> ; yields ty:result
概述:¶
‘xor
’ 指令返回其两个操作数的按位逻辑异或。 xor
用于实现“ones' complement”(反码)运算,即 C 语言中的 “~” 运算符。
参数:¶
语义:¶
‘xor
’ 指令使用的真值表是
In0 |
In1 |
Out |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
示例:¶
<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
向量运算¶
LLVM 支持多种指令,以目标无关的方式表示向量运算。这些指令涵盖了有效处理向量所需的元素访问和特定于向量的操作。虽然 LLVM 直接支持这些向量运算,但许多复杂的算法将希望使用特定于目标的 intrinsic 函数来充分利用特定目标。
‘extractelement
’ 指令¶
语法:¶
<result> = extractelement <n x <ty>> <val>, <ty2> <idx> ; yields <ty>
<result> = extractelement <vscale x n x <ty>> <val>, <ty2> <idx> ; yields <ty>
概述:¶
‘extractelement
’ 指令从向量中提取指定索引处的单个标量元素。
参数:¶
‘extractelement
’ 指令的第一个操作数是 向量 类型的值。第二个操作数是一个索引,指示要从中提取元素的位置。索引可以是任何整数类型的变量,并将被视为无符号整数。
语义:¶
结果是与 val
的元素类型相同的标量。它的值是 val
中位置 idx
处的值。如果对于固定长度向量,idx
超出 val
的长度,则结果是一个 poison 值。对于可伸缩向量,如果 idx
的值超过向量的运行时长度,则结果是一个 poison 值。
示例:¶
<result> = extractelement <4 x i32> %vec, i32 0 ; yields i32
‘insertelement
’ 指令¶
语法:¶
<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>>
概述:¶
‘insertelement
’ 指令将标量元素插入到向量的指定索引处。
参数:¶
‘insertelement
’ 指令的第一个操作数是 向量 类型的值。第二个操作数是标量值,其类型必须与第一个操作数的元素类型相同。第三个操作数是一个索引,指示要插入值的位置。索引可以是任何整数类型的变量,并将被视为无符号整数。
语义:¶
结果是与 val
类型相同的向量。它的元素值是 val
的元素值,除了位置 idx
处,它获得值 elt
。如果对于固定长度向量,idx
超出 val
的长度,则结果是一个 poison 值。对于可伸缩向量,如果 idx
的值超过向量的运行时长度,则结果是一个 poison 值。
示例:¶
<result> = insertelement <4 x i32> %vec, i32 1, i32 0 ; yields <4 x i32>
‘shufflevector
’ 指令¶
语法:¶
<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>>
概述:¶
‘shufflevector
’ 指令从两个输入向量构造元素的排列,返回一个与输入具有相同元素类型且长度与混洗掩码相同的向量。
参数:¶
‘shufflevector
’ 指令的前两个操作数是具有相同类型的向量。第三个参数是混洗掩码向量常量,其元素类型为 i32
。掩码向量元素必须是常量整数或 poison
值。指令的结果是一个向量,其长度与混洗掩码相同,其元素类型与前两个操作数的元素类型相同。
语义:¶
两个输入向量的元素从左到右跨越两个向量编号。对于结果向量的每个元素,混洗掩码从输入向量之一中选择一个元素复制到结果。掩码中的非负元素表示连接的输入向量对的索引。
掩码向量中的 poison
元素指定结果元素为 poison
。出于向后兼容性的原因,LLVM 暂时也接受 undef
掩码元素,这些元素将被解释为与 poison
元素相同的方式。如果混洗掩码从输入向量之一中选择一个 undef
元素,则结果元素为 undef
。
对于可伸缩向量,目前唯一有效的掩码值是 zeroinitializer
、undef
和 poison
,因为对于在编译时长度未知的向量,我们无法将所有索引都写成字面量。
示例:¶
<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> poison,
<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> poison,
<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>
聚合操作¶
LLVM 支持多条指令来处理 聚合 值。
“extractvalue
” 指令¶
语法:¶
<result> = extractvalue <aggregate type> <val>, <idx>{, <idx>}*
概述:¶
“extractvalue
” 指令从 聚合 值中提取成员字段的值。
参数:¶
“extractvalue
” 指令的第一个操作数是 struct 或 array 类型的值。其他操作数是常量索引,用于指定要提取的值,其方式类似于 “getelementptr
” 指令中的索引。
getelementptr
索引的主要区别在于
由于被索引的值不是指针,因此第一个索引被省略并假定为零。
必须至少指定一个索引。
不仅结构体索引,而且数组索引也必须在界限内。
语义:¶
结果是由索引操作数指定的聚合位置的值。
示例:¶
<result> = extractvalue {i32, float} %agg, 0 ; yields i32
“insertvalue
” 指令¶
语法:¶
<result> = insertvalue <aggregate type> <val>, <ty> <elt>, <idx>{, <idx>}* ; yields <aggregate type>
概述:¶
“insertvalue
” 指令将值插入到 聚合 值中的成员字段。
参数:¶
“insertvalue
” 指令的第一个操作数是 struct 或 array 类型的值。第二个操作数是要插入的 first-class 值。以下操作数是常量索引,指示要插入值的位置,其方式类似于 “extractvalue
” 指令中的索引。要插入的值必须与索引标识的值具有相同的类型。
语义:¶
结果是与 val
相同类型的聚合。它的值与 val
的值相同,只是由索引指定的位置的值是 elt
的值。
示例:¶
%agg1 = insertvalue {i32, float} poison, i32 1, 0 ; yields {i32 1, float poison}
%agg2 = insertvalue {i32, float} %agg1, float %val, 1 ; yields {i32 1, float %val}
%agg3 = insertvalue {i32, {float}} poison, float %val, 1, 0 ; yields {i32 poison, {float %val}}
内存访问和寻址操作¶
基于 SSA 表示的关键设计点是如何表示内存。在 LLVM 中,没有内存位置采用 SSA 形式,这使得事情变得非常简单。本节介绍如何在 LLVM 中读取、写入和分配内存。
“alloca
” 指令¶
语法:¶
<result> = alloca [inalloca] <type> [, <ty> <NumElements>] [, align <alignment>] [, addrspace(<num>)] ; yields type addrspace(num)*:result
概述:¶
“alloca
” 指令在当前正在执行的函数的堆栈帧上分配内存,以便在该函数返回到其调用者时自动释放。如果未显式指定地址空间,则对象将在来自 datalayout 字符串 的 alloca 地址空间中分配。
参数:¶
“alloca
” 指令在运行时堆栈上分配 sizeof(<type>)*NumElements
字节的内存,并向程序返回适当类型的指针。如果指定了 “NumElements”,则它是分配的元素数量,否则 “NumElements” 默认为 1。
如果指定了常量对齐,则分配的值结果保证至少与该边界对齐。对齐方式不能大于 1 << 32
。
仅当解析文本 IR 时,对齐是可选的;对于内存中的 IR,它始终存在。如果未指定,则目标可以选择将分配与与类型兼容的任何方便边界对齐。
“type
” 可以是任何大小的类型。
除非所有字段都是相同的可伸缩向量类型(例如,{<vscale x 2 x i32>, <vscale x 2 x i32>}
包含相同的类型,而 {<vscale x 2 x i32>, <vscale x 2 x i64>}
则不包含),否则包含可伸缩向量的结构体不能在 allocas 中使用。
语义:¶
内存已分配;返回一个指针。分配的内存未初始化,并且从未初始化的内存加载会产生未定义的值。如果堆栈空间不足以进行分配,则操作本身是未定义的。“alloca
” 分配的内存在函数返回时自动释放。“alloca
” 指令通常用于表示必须具有可用地址的自动变量。当函数返回时(使用 ret
或 resume
指令),内存将被回收。分配零字节是合法的,但返回的指针可能不是唯一的。内存分配的顺序(即堆栈增长的方向)未指定。
请注意,仅当目标为其分配了语义时,在来自 datalayout 字符串 的 alloca 地址空间之外使用 “alloca
” 才有意义。
如果返回的指针被 llvm.lifetime.start 使用,则返回的对象最初是死区。有关生命周期操作内在函数的精确语义,请参见 llvm.lifetime.start 和 llvm.lifetime.end。
示例:¶
%ptr = alloca i32 ; yields ptr
%ptr = alloca i32, i32 4 ; yields ptr
%ptr = alloca i32, i32 4, align 1024 ; yields ptr
%ptr = alloca i32, align 1024 ; yields ptr
“load
” 指令¶
语法:¶
<result> = load [volatile] <ty>, ptr <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>, ptr <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> }
概述:¶
“load
” 指令用于从内存中读取数据。
参数:¶
load
指令的参数指定要从中加载数据的内存地址。指定的类型必须是已知大小的 first class 类型(即,不包含 opaque 结构类型)。如果 load
标记为 volatile
,则优化器不允许修改此 load
与其他 volatile 操作 的执行次数或执行顺序。
如果 load
标记为 atomic
,则它需要一个额外的 ordering 和可选的 syncscope("<target-scope>")
参数。release
和 acq_rel
排序在 load
指令上无效。当原子加载可能看到多个原子存储时,它们会产生 defined 结果。pointee 的类型必须是整数、指针或浮点类型,其位宽是大于或等于 8 且小于或等于目标特定大小限制的 2 的幂。align
必须在原子加载上显式指定。注意:如果对齐方式不大于或等于 <value> 类型的大小,则原子操作可能需要锁并具有较差的性能。!nontemporal
对于原子加载没有任何定义的语义。
可选的常量 align
参数指定操作的对齐方式(即,内存地址的对齐方式)。代码发射器有责任确保对齐信息正确。高估对齐方式会导致未定义的行为。低估对齐方式可能会产生效率较低的代码。对齐方式 1 始终是安全的。最大可能的对齐方式是 1 << 32
。大于加载类型大小的对齐值意味着可以安全地加载内存,直到对齐值字节,而不会在默认地址空间中陷入困境。访问高字节可能会干扰调试工具,因此如果函数具有 sanitize_thread
或 sanitize_address
属性,则不应访问高字节。
仅当解析文本 IR 时,对齐是可选的;对于内存中的 IR,它始终存在。省略的 align
参数表示操作具有目标的 ABI 对齐方式。
可选的 !nontemporal
元数据必须引用单个元数据名称 <nontemp_node>
,该名称对应于具有一个值为 1 的 i32
条目的元数据节点。指令上 !nontemporal
元数据的存在告诉优化器和代码生成器,此加载预计不会在缓存中重复使用。代码生成器可以选择特殊指令来节省缓存带宽,例如 x86 上的 MOVNT
指令。
可选的 !invariant.load
元数据必须引用单个元数据名称 <empty_node>
,该名称对应于没有条目的元数据节点。如果执行带有 !invariant.load
元数据标记的加载指令,则加载引用的内存位置必须在程序中内存位置可解引用的所有点都包含相同的值;否则,行为是未定义的。
- 可选的
!invariant.group
元数据必须引用单个元数据名称 <empty_node>
,该名称对应于没有条目的元数据节点。请参阅invariant.group
元数据 invariant.group。
可选的 !nonnull
元数据必须引用单个元数据名称 <empty_node>
,该名称对应于没有条目的元数据节点。指令上 !nonnull
元数据的存在告诉优化器,加载的值已知永远不会为 null。如果该值在运行时为 null,则会返回 poison 值。这类似于参数和返回值上的 nonnull
属性。此元数据只能应用于指针类型的加载。
可选的 !dereferenceable
元数据必须引用单个元数据名称 <deref_bytes_node>
,该名称对应于具有一个 i64
条目的元数据节点。请参阅 dereferenceable
元数据 dereferenceable。
可选的 !dereferenceable_or_null
元数据必须引用单个元数据名称 <deref_bytes_node>
,该名称对应于具有一个 i64
条目的元数据节点。请参阅 dereferenceable_or_null
元数据 dereferenceable_or_null。
可选的 !align
元数据必须引用单个元数据名称 <align_node>
,该名称对应于具有一个 i64
条目的元数据节点。指令上 !align
元数据的存在告诉优化器,加载的值已知与元数据节点中整数值指定的边界对齐。对齐方式必须是 2 的幂。这类似于参数和返回值上的 “align” 属性。此元数据只能应用于指针类型的加载。如果返回的值在运行时未正确对齐,则会返回 poison 值。
可选的 !noundef
元数据必须引用单个元数据名称 <empty_node>
,该名称对应于没有条目的节点。指令上 !noundef
元数据的存在告诉优化器,加载的值已知是 well defined。如果该值不是 well defined,则行为是未定义的。如果 !noundef
元数据与生成 poison 的元数据(如 !nonnull
)组合使用,则违反该元数据约束也将导致未定义的行为。
语义:¶
加载指向的内存位置。如果加载的值是标量类型,则读取的字节数不超过容纳类型的所有位所需的最小字节数。例如,加载 i24
最多读取三个字节。当加载大小不是字节整数倍的类型(如 i20
)的值时,如果该值最初不是使用相同类型的存储写入的,则结果是未定义的。如果加载的值是聚合类型,则可能会访问与填充对应的字节,但会被忽略,因为无法从加载的聚合值中观察到填充。如果 <pointer>
不是 well-defined 值,则行为是未定义的。
示例:¶
%ptr = alloca i32 ; yields ptr
store i32 3, ptr %ptr ; yields void
%val = load i32, ptr %ptr ; yields i32:val = i32 3
“store
” 指令¶
语法:¶
store [volatile] <ty> <value>, ptr <pointer>[, align <alignment>][, !nontemporal !<nontemp_node>][, !invariant.group !<empty_node>] ; yields void
store atomic [volatile] <ty> <value>, ptr <pointer> [syncscope("<target-scope>")] <ordering>, align <alignment> [, !invariant.group !<empty_node>] ; yields void
!<nontemp_node> = !{ i32 1 }
!<empty_node> = !{}
概述:¶
“store
” 指令用于写入内存。
参数:¶
store
指令有两个参数:要存储的值和要存储该值的地址。<pointer>
操作数的类型必须是指向 <value>
操作数的 first class 类型的指针。如果 store
标记为 volatile
,则优化器不允许修改此 store
与其他 volatile 操作 的执行次数或执行顺序。只有已知大小的 first class 类型(即,不包含 opaque 结构类型)的值可以存储。
如果 store
标记为 atomic
,则它需要一个额外的 ordering 和可选的 syncscope("<target-scope>")
参数。acquire
和 acq_rel
排序在 store
指令上无效。当原子加载可能看到多个原子存储时,它们会产生 defined 结果。pointee 的类型必须是整数、指针或浮点类型,其位宽是大于或等于 8 且小于或等于目标特定大小限制的 2 的幂。align
必须在原子存储上显式指定。注意:如果对齐方式不大于或等于 <value> 类型的大小,则原子操作可能需要锁并具有较差的性能。!nontemporal
对于原子存储没有任何定义的语义。
可选的常量 align
参数指定操作的对齐方式(即,内存地址的对齐方式)。代码发射器有责任确保对齐信息正确。高估对齐方式会导致未定义的行为。低估对齐方式可能会产生效率较低的代码。对齐方式 1 始终是安全的。最大可能的对齐方式是 1 << 32
。大于加载类型大小的对齐值意味着可以安全地加载内存,直到对齐值字节,而不会在默认地址空间中陷入困境。访问高字节可能会干扰调试工具,因此如果函数具有 sanitize_thread
或 sanitize_address
属性,则不应访问高字节。
仅当解析文本 IR 时,对齐是可选的;对于内存中的 IR,它始终存在。省略的 align
参数表示操作具有目标的 ABI 对齐方式。
可选的 !nontemporal
元数据必须引用单个元数据名称 <nontemp_node>
,该名称对应于具有一个值为 1 的 i32
条目的元数据节点。指令上 !nontemporal
元数据的存在告诉优化器和代码生成器,此加载预计不会在缓存中重复使用。代码生成器可以选择特殊指令来节省缓存带宽,例如 x86 上的 MOVNT
指令。
可选的 !invariant.group
元数据必须引用单个元数据名称 <empty_node>
。请参阅 invariant.group
元数据。
语义:¶
内存的内容将更新为在 <pointer>
操作数指定的位置包含 <value>
。如果 <value>
是标量类型,则写入的字节数不超过容纳类型的所有位所需的最小字节数。例如,存储 i24
最多写入三个字节。当写入大小不是字节整数倍的类型(如 i20
)的值时,未指定不属于该类型的额外位会发生什么情况,但它们通常会被覆盖。如果 <value>
是聚合类型,则填充会用 undef 填充。如果 <pointer>
不是 well-defined 值,则行为是未定义的。
示例:¶
%ptr = alloca i32 ; yields ptr
store i32 3, ptr %ptr ; yields void
%val = load i32, ptr %ptr ; yields i32:val = i32 3
“fence
” 指令¶
语法:¶
fence [syncscope("<target-scope>")] <ordering> ; yields void
概述:¶
“fence
” 指令用于在操作之间引入先发生于 (happens-before) 边。
参数:¶
“fence
” 指令采用 ordering 参数,该参数定义它们添加的同步于 (synchronizes-with) 边。它们只能被赋予 acquire
、release
、acq_rel
和 seq_cst
排序。
语义:¶
如果且仅当存在原子操作 X 和 Y,两者都在某个原子对象 M 上操作,使得 A 在 X 之前排序,X 修改 M(直接或通过 X 开头的序列的某些副作用),Y 在 B 之前排序,并且 Y 观察到 M,则具有(至少)release
排序语义的 fence A 同步于 具有(至少)acquire
排序语义的 fence B。这提供了 A 和 B 之间的先发生于 (happens-before) 依赖关系。原子操作 X 或 Y 之一(但不是两者)可能提供 release
或 acquire
(分别)排序约束,并且仍然 同步于 显式的 fence
并建立 先发生于 (happens-before) 边,而不是显式的 fence
。
具有 seq_cst
排序的 fence
除了具有上面指定的 acquire
和 release
语义外,还参与其他 seq_cst
操作和/或 fence 的全局程序顺序。此外,由 seq_cst
fence 创建的全局排序必须与在此类 fence 之前和之后发生的 monotonic
(或更强)内存访问的各个总顺序兼容。这种交互的确切语义有些复杂,有关更多详细信息,请参见 C++ 标准的 [atomics.order] 部分。
fence
指令还可以采用可选的 “syncscope” 参数。
示例:¶
fence acquire ; yields void
fence syncscope("singlethread") seq_cst ; yields void
fence syncscope("agent") seq_cst ; yields void
“cmpxchg
” 指令¶
语法:¶
cmpxchg [weak] [volatile] ptr <pointer>, <ty> <cmp>, <ty> <new> [syncscope("<target-scope>")] <success ordering> <failure ordering>[, align <alignment>] ; yields { ty, i1 }
概述:¶
“cmpxchg
” 指令用于原子地修改内存。它加载内存中的值,并将其与给定的值进行比较。如果它们相等,它会尝试将新值存储到内存中。
参数:¶
“cmpxchg
” 指令有三个参数:要操作的地址,要与该地址当前值进行比较的值,以及如果比较值相等则放置在该地址的新值。“<cmp>” 的类型必须是整数或指针类型,其位宽是大于或等于 8 且小于或等于目标特定大小限制的 2 的幂。“<cmp>” 和 “<new>” 必须具有相同的类型,并且 “<pointer>” 的类型必须是指向该类型的指针。如果 cmpxchg
标记为 volatile
,则优化器不允许修改此 cmpxchg
与其他 volatile 操作 的执行次数或执行顺序。
成功和失败 ordering 参数指定此 cmpxchg
如何与其他原子操作同步。两个排序参数都必须至少为 monotonic
,失败排序不能是 release
或 acq_rel
。
cmpxchg
指令还可以采用可选的 “syncscope” 参数。
注意:如果对齐方式不大于或等于 <value> 类型的大小,则原子操作可能需要锁并具有较差的性能。
仅当解析文本 IR 时,对齐是可选的;对于内存中的 IR,它始终存在。如果未指定,则假定对齐方式等于 “<value>” 类型的大小。请注意,此默认对齐方式假定与未指定 align 时用于加载/存储指令的对齐方式不同。
传递到 cmpxchg 的指针必须具有大于或等于操作数内存大小的对齐方式。
语义:¶
读取由 “<pointer>
” 操作数指定的内存位置的内容,并将其与 “<cmp>
” 进行比较;如果值相等,则将 “<new>
” 写入该位置。返回该位置的原始值,以及指示成功 (true) 或失败 (false) 的标志。
如果 cmpxchg 操作标记为 weak
,则允许出现伪失败:即使比较匹配,该操作也可能不写入 <new>
。
如果 cmpxchg 操作是强类型(默认),则当且仅当加载的值等于 cmp
时,i1 的值为 1。
成功的 cmpxchg
是一个读-修改-写指令,用于识别释放序列。失败的 cmpxchg
等同于一个原子加载,其排序参数由第二个排序参数决定。
示例:¶
entry:
%orig = load atomic i32, ptr %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 ptr %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 [volatile] <operation> ptr <pointer>, <ty> <value> [syncscope("<target-scope>")] <ordering>[, align <alignment>] ; yields ty
概述:¶
‘atomicrmw
’ 指令用于原子性地修改内存。
参数:¶
‘atomicrmw
’ 指令有三个参数:要应用的操作、要修改其值的地址,以及操作的参数。操作必须是以下关键字之一
xchg
add
sub
and
nand
or
xor
max
min
umax
umin
fadd
fsub
fmax
fmin
uinc_wrap
udec_wrap
usub_cond
usub_sat
对于大多数这些操作,‘<value>’ 的类型必须是整数类型,其位宽是大于或等于 8 且小于或等于目标特定大小限制的 2 的幂。对于 xchg,这也可以是浮点类型或指针类型,其大小约束与整数相同。对于 fadd/fsub/fmax/fmin,这必须是浮点类型或浮点类型的固定向量。 ‘<pointer>
’ 操作数的类型必须是指向该类型的指针。如果 atomicrmw
被标记为 volatile
,则优化器不允许修改此 atomicrmw
与其他 volatile operations 的执行次数或顺序。
注意:如果对齐方式不大于或等于 <value> 类型的大小,则原子操作可能需要锁并具有较差的性能。
仅当解析文本 IR 时,对齐是可选的;对于内存中的 IR,它始终存在。如果未指定,则假定对齐方式等于 “<value>” 类型的大小。请注意,此默认对齐方式假定与未指定 align 时用于加载/存储指令的对齐方式不同。
atomicrmw
指令也可以接受一个可选的 “syncscope” 参数。
语义:¶
位于 ‘<pointer>
’ 操作数指定的内存位置的内容会被原子性地读取、修改和写回。返回该位置的原始值。修改由操作参数指定
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
(使用有符号比较)min:
*ptr = *ptr < val ? *ptr : val
(使用有符号比较)umax:
*ptr = *ptr > val ? *ptr : val
(使用无符号比较)umin:
*ptr = *ptr < val ? *ptr : val
(使用无符号比较)fadd:
*ptr = *ptr + val
(使用浮点运算)fsub:
*ptr = *ptr - val
(使用浮点运算)fmax:
*ptr = maxnum(*ptr, val)
(匹配 `llvm.maxnum.*` intrinsic)fmin:
*ptr = minnum(*ptr, val)
(匹配 `llvm.minnum.*` intrinsic)uinc_wrap:
*ptr = (*ptr u>= val) ? 0 : (*ptr + 1)
(当递增到高于输入值时,值会回绕为零)udec_wrap:
*ptr = ((*ptr == 0) || (*ptr u> val)) ? val : (*ptr - 1)
(当递减到低于零时,值会回绕为输入值)。usub_cond:
*ptr = (*ptr u>= val) ? *ptr - val : *ptr
(仅在没有无符号溢出的情况下减法)。usub_sat:
*ptr = (*ptr u>= val) ? *ptr - val : 0
(减法运算,无符号钳位到零)。
示例:¶
%old = atomicrmw add ptr %ptr, i32 1 acquire ; yields i32
‘getelementptr
’ 指令¶
语法:¶
<result> = getelementptr <ty>, ptr <ptrval>{, <ty> <idx>}*
<result> = getelementptr inbounds <ty>, ptr <ptrval>{, <ty> <idx>}*
<result> = getelementptr nusw <ty>, ptr <ptrval>{, <ty> <idx>}*
<result> = getelementptr nuw <ty>, ptr <ptrval>{, <ty> <idx>}*
<result> = getelementptr inrange(S,E) <ty>, ptr <ptrval>{, <ty> <idx>}*
<result> = getelementptr <ty>, <N x ptr> <ptrval>, <vector index type> <idx>
概述:¶
‘getelementptr
’ 指令用于获取 聚合 数据结构的子元素的地址。它仅执行地址计算,不访问内存。该指令也可用于计算此类地址的向量。
参数:¶
第一个参数始终是用作计算基础的类型。第二个参数始终是指针或指针向量,并且是起始的基地址。其余参数是索引,指示聚合对象的哪些元素被索引。每个索引的解释取决于被索引的类型。第一个索引始终索引作为第二个参数给出的指针值,第二个索引索引指向的类型的值(不一定是直接指向的值,因为第一个索引可以是非零的),依此类推。第一个被索引的类型必须是指针值,后续类型可以是数组、向量和结构体。请注意,后续被索引的类型永远不能是指针,因为这将需要在继续计算之前加载指针。
每个索引参数的类型取决于它索引的类型。当索引到(可选的 packed)结构体时,只允许 i32
整数常量(当使用索引向量时,它们必须都是相同的 i32
整数常量)。当索引到数组、指针或向量时,允许任何位宽的整数,并且它们不需要是常量。这些整数在相关情况下被视为有符号值。
例如,让我们考虑一个 C 代码片段以及它如何编译为 LLVM
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];
}
Clang 生成的 LLVM 代码大致如下
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }
define ptr @foo(ptr %s) {
entry:
%arrayidx = getelementptr inbounds %struct.ST, ptr %s, i64 1, i32 2, i32 1, i64 5, i64 13
ret ptr %arrayidx
}
语义:¶
在上面的示例中,第一个索引索引到 ‘%struct.ST*
’ 类型,它是一个指针,产生 ‘%struct.ST
’ = ‘{ i32, double, %struct.RT }
’ 类型,一个结构体。第二个索引索引到结构体的第三个元素,产生 ‘%struct.RT
’ = ‘{ i8 , [10 x [20 x i32]], i8 }
’ 类型,另一个结构体。第三个索引索引到结构体的第二个元素,产生 ‘[10 x [20 x i32]]
’ 类型,一个数组。数组的两个维度被下标化,产生 ‘i32
’ 类型。 ‘getelementptr
’ 指令返回指向此元素的指针。
请注意,部分索引结构体,返回指向内部元素的指针是完全合法的。因此,给定测试用例的 LLVM 代码等效于
define ptr @foo(ptr %s) {
%t1 = getelementptr %struct.ST, ptr %s, i32 1
%t2 = getelementptr %struct.ST, ptr %t1, i32 0, i32 2
%t3 = getelementptr %struct.RT, ptr %t2, i32 0, i32 1
%t4 = getelementptr [10 x [20 x i32]], ptr %t3, i32 0, i32 5
%t5 = getelementptr [20 x i32], ptr %t4, i32 0, i32 13
ret ptr %t5
}
索引首先被转换为指针索引类型中的偏移量。如果当前索引的类型是结构体类型,则与索引对应的结构体偏移量会被符号扩展或截断为指针索引类型。否则,索引本身会被符号扩展或截断,然后乘以当前索引类型的类型分配大小(即,向上舍入到 ABI 对齐的大小)。
然后,偏移量被添加到基地址的低位,直到索引类型宽度,使用静默回绕的二补算术。如果指针大小大于索引大小,这意味着索引类型宽度之外的位将不受影响。
getelementptr
的结果值可能在基指针指向的对象之外。然而,即使结果值碰巧指向已分配的存储器,也不一定用于访问内存。有关更多信息,请参阅 指针别名规则 部分。
getelementptr
指令可能具有许多属性,这些属性会施加额外的规则。如果违反任何规则,则结果值是 poison value。在基地址是指针向量的情况下,这些属性逐元素地应用于每个计算。
对于 nusw
(无无符号有符号回绕)
如果索引的类型大于指针索引类型,则截断为指针索引类型会保留有符号值 (`
trunc nsw
`)。索引乘以类型大小不会以有符号方式回绕指针索引类型 (`
mul nsw
`)。每个偏移量的连续相加(不添加基地址)不会以有符号方式回绕指针索引类型 (`
add nsw
`)。当前地址(截断为指针索引类型并解释为无符号数)与每个偏移量(解释为有符号数)的连续相加不会回绕指针索引类型。
对于 nuw
(无无符号回绕)
如果索引的类型大于指针索引类型,则截断为指针索引类型会保留无符号值 (`
trunc nuw
`)。索引乘以类型大小不会以无符号方式回绕指针索引类型 (`
mul nuw
`)。每个偏移量的连续相加(不添加基地址)不会以无符号方式回绕指针索引类型 (`
add nuw
`)。当前地址(截断为指针索引类型并解释为无符号数)与每个偏移量(也解释为无符号数)的连续相加不会回绕指针索引类型 (`
add nuw
`)。
对于 inbounds
,nusw
属性的所有规则都适用。此外,如果 getelementptr
有任何非零索引,则适用以下规则
请注意,即使基指针未指向已分配的对象,具有全零索引的 getelementptr
也始终被认为是 inbounds
。作为推论,默认地址空间中空指针的 in bounds 范围内的唯一指针是空指针本身。
如果 inbounds
出现在 getelementptr
指令上,则 nusw
属性也将自动设置。因此,如果 inbounds
已经存在,则 nusw
也不会在文本 IR 中打印。
如果存在 inrange(Start, End)
属性,则从 getelementptr
派生的任何指针加载或存储都具有未定义的行为,如果加载或存储将访问 getelementptr
表达式结果的半开区间 [Start, End)
之外的内存。涉及从带有 inrange
关键字的 getelementptr
派生的指针的指针比较或 ptrtoint
(包括涉及内存的类似 ptrtoint
的操作)的结果是未定义的,但当两个操作数都在闭区间 [Start, End]
内的情况下的比较除外。请注意,inrange
关键字目前仅允许在常量 getelementptr
表达式中使用。
getelementptr 指令通常令人困惑。有关其工作原理的更多见解,请参阅 getelementptr FAQ。
示例:¶
%aptr = getelementptr {i32, [12 x i8]}, ptr %saptr, i64 0, i32 1
%vptr = getelementptr {i32, <2 x i8>}, ptr %svptr, i64 0, i32 1, i32 1
%eptr = getelementptr [12 x i8], ptr %aptr, i64 0, i32 1
%iptr = getelementptr [10 x i32], ptr @arr, i16 0, i16 0
指针向量:¶
当 getelementptr
的一个或多个参数是向量时,它会返回指针向量,而不是单个地址。在这种情况下,所有向量参数应具有相同数量的元素,并且每个标量参数将在地址计算期间有效地广播到向量中。
; All arguments are vectors:
; A[i] = ptrs[i] + offsets[i]*sizeof(i8)
%A = getelementptr i8, <4 x i8*> %ptrs, <4 x i64> %offsets
; Add the same scalar offset to each pointer of a vector:
; A[i] = ptrs[i] + offset*sizeof(i8)
%A = getelementptr i8, <4 x ptr> %ptrs, i64 %offset
; Add distinct offsets to the same pointer:
; A[i] = ptr + offsets[i]*sizeof(i8)
%A = getelementptr i8, ptr %ptr, <4 x i64> %offsets
; In all cases described above the type of the result is <4 x ptr>
以下两条指令是等效的
getelementptr %struct.ST, <4 x ptr> %s, <4 x i64> %ind1,
<4 x i32> <i32 2, i32 2, i32 2, i32 2>,
<4 x i32> <i32 1, i32 1, i32 1, i32 1>,
<4 x i32> %ind4,
<4 x i64> <i64 13, i64 13, i64 13, i64 13>
getelementptr %struct.ST, <4 x ptr> %s, <4 x i64> %ind1,
i32 2, i32 1, <4 x i32> %ind4, i64 13
让我们看一下 C 代码,其中向量版本的 getelementptr
才有意义
// Let's assume that we vectorize the following loop:
double *A, *B; int *C;
for (int i = 0; i < size; ++i) {
A[i] = B[C[i]];
}
; get pointers for 8 elements from array B
%ptrs = getelementptr double, ptr %B, <8 x i32> %C
; load 8 elements from array B into A
%A = call <8 x double> @llvm.masked.gather.v8f64.v8p0f64(<8 x ptr> %ptrs,
i32 8, <8 x i1> %mask, <8 x double> %passthru)
转换操作¶
此类别中的指令是转换指令(类型转换),它们都接受单个操作数和一个类型。它们对操作数执行各种位转换。
‘trunc .. to
’ 指令¶
语法:¶
<result> = trunc <ty> <value> to <ty2> ; yields ty2
<result> = trunc nsw <ty> <value> to <ty2> ; yields ty2
<result> = trunc nuw <ty> <value> to <ty2> ; yields ty2
<result> = trunc nuw nsw <ty> <value> to <ty2> ; yields ty2
概述:¶
‘trunc
’ 指令将其操作数截断为 ty2
类型。
参数:¶
‘trunc
’ 指令接受一个要截断的值和一个要截断成的类型。两种类型都必须是 整数 类型,或相同数量整数的向量。 value
的位大小必须大于目标类型 ty2
的位大小。不允许大小相等的类型。
语义:¶
‘trunc
’ 指令截断 value
中的高位,并将剩余位转换为 ty2
。由于源大小必须大于目标大小,因此 trunc
不能是 *no-op cast*。它总是会截断位。
如果存在 nuw
关键字,并且任何被截断的位为非零,则结果为 poison value。如果存在 nsw
关键字,并且任何被截断的位与截断结果的最高位不同,则结果为 poison value。
示例:¶
%X = trunc i32 257 to i8 ; yields i8:1
%Y = trunc i32 123 to i1 ; yields i1:true
%Z = trunc i32 122 to i1 ; yields i1:false
%W = trunc <2 x i16> <i16 8, i16 7> to <2 x i8> ; yields <i8 8, i8 7>
‘zext .. to
’ 指令¶
语法:¶
<result> = zext <ty> <value> to <ty2> ; yields ty2
概述:¶
‘zext
’ 指令将其操作数零扩展为 ty2
类型。
nneg
(非负) 标志(如果存在)指定操作数是非负的。此属性可被优化过程使用,以便稍后将 zext
转换为 sext
。
参数:¶
‘zext
’ 指令接受一个要转换的值和一个要转换成的类型。两种类型都必须是 整数 类型,或相同数量整数的向量。 value
的位大小必须小于目标类型 ty2
的位大小。
语义:¶
zext
用零位填充 value
的高位,直到其达到目标类型 ty2
的大小。
当从 i1 进行零扩展时,结果将始终为 0 或 1。
如果设置了 nneg
标志,并且 zext
参数为负数,则结果为 poison value。
示例:¶
%X = zext i32 257 to i64 ; yields i64:257
%Y = zext i1 true to i32 ; yields i32:1
%Z = zext <2 x i16> <i16 8, i16 7> to <2 x i32> ; yields <i32 8, i32 7>
%a = zext nneg i8 127 to i16 ; yields i16 127
%b = zext nneg i8 -1 to i16 ; yields i16 poison
‘sext .. to
’ 指令¶
语法:¶
<result> = sext <ty> <value> to <ty2> ; yields ty2
概述:¶
‘sext
’ 将 value
符号扩展为 ty2
类型。
参数:¶
‘sext
’ 指令接受一个要转换的值和一个要转换成的类型。两种类型都必须是 整数 类型,或相同数量整数的向量。 value
的位大小必须小于目标类型 ty2
的位大小。
语义:¶
‘sext
’ 指令通过复制 value
的符号位(最高位)执行符号扩展,直到其达到 ty2
类型的位大小。
当从 i1 进行符号扩展时,扩展始终导致 -1 或 0。
示例:¶
%X = sext i8 -1 to i16 ; yields i16 :65535
%Y = sext i1 true to i32 ; yields i32:-1
%Z = sext <2 x i16> <i16 8, i16 7> to <2 x i32> ; yields <i32 8, i32 7>
‘fptrunc .. to
’ 指令¶
语法:¶
<result> = fptrunc [fast-math flags]* <ty> <value> to <ty2> ; yields ty2
概述:¶
‘fptrunc
’ 指令将 value
截断为 ty2
类型。
参数:¶
‘fptrunc
’ 指令接受一个要转换的 浮点 值和一个要转换成的 浮点 类型。 value
的大小必须大于 ty2
的大小。这意味着 fptrunc
不能用于进行 *no-op cast*。
语义:¶
‘fptrunc
’ 指令将 value
从较大的 浮点 类型转换为较小的 浮点 类型。此指令假定在默认的 浮点环境 中执行。
NaN 值遵循通常的 NaN 行为,除非 _如果_ NaN 载荷从输入传播(“静默 NaN 传播”或“不变 NaN 传播”情况),则丢弃不适合结果类型的 NaN 载荷的低位。请注意,如果丢弃低位导致全 0 载荷,则不能将其表示为 signaling NaN(它将表示无穷大),因此在这种情况下,“不变 NaN 传播”是不可能的。
此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用其他不安全的浮点优化。
示例:¶
%X = fptrunc double 16777217.0 to float ; yields float:16777216.0
%Y = fptrunc double 1.0E+300 to half ; yields half:+infinity
‘fpext .. to
’ 指令¶
语法:¶
<result> = fpext [fast-math flags]* <ty> <value> to <ty2> ; yields ty2
概述:¶
‘fpext
’ 将浮点 value
扩展为更大的浮点值。
参数:¶
语义:¶
‘fpext
’ 指令将 value
从较小的 浮点 类型扩展为较大的 浮点 类型。 fpext
不能用于进行 *no-op cast*,因为它总是会更改位。使用 bitcast
对浮点类型转换进行 *no-op cast*。
NaN 值遵循通常的 NaN 行为,除非 _如果_ NaN 载荷从输入传播(“静默 NaN 传播”或“不变 NaN 传播”情况),则将其复制到结果载荷的高位,其余低位为零。
此指令还可以接受任意数量的 快速数学标志,这些标志是优化提示,用于启用其他不安全的浮点优化。
示例:¶
%X = fpext float 3.125 to double ; yields double:3.125000e+00
%Y = fpext double %X to fp128 ; yields fp128:0xL00000000000000004000900000000000
‘fptoui .. to
’ 指令¶
语法:¶
<result> = fptoui <ty> <value> to <ty2> ; yields ty2
概述:¶
‘fptoui
’ 将浮点 value
转换为其 ty2
类型的无符号整数等效值。
参数:¶
‘fptoui
’ 指令接受一个要转换的值,该值必须是标量或向量浮点值,以及要将其转换成的类型 ty2
,该类型必须是整数类型。 如果 ty
是向量浮点类型,则 ty2
必须是具有与 ty
相同元素数量的向量整数类型。
语义:¶
‘fptoui
’ 指令将其浮点操作数转换为最接近的(向零舍入)无符号整数值。 如果该值无法容纳在 ty2
中,则结果为 poison 值。
示例:¶
%X = fptoui double 123.0 to i32 ; yields i32:123
%Y = fptoui float 1.0E+300 to i1 ; yields undefined:1
%Z = fptoui float 1.04E+17 to i8 ; yields undefined:1
‘fptosi .. to
’ 指令¶
语法:¶
<result> = fptosi <ty> <value> to <ty2> ; yields ty2
概述:¶
‘fptosi
’ 指令将浮点 value
转换为 ty2
类型。
参数:¶
‘fptosi
’ 指令接受一个要转换的值,该值必须是标量或向量浮点值,以及要将其转换成的类型 ty2
,该类型必须是整数类型。 如果 ty
是向量浮点类型,则 ty2
必须是具有与 ty
相同元素数量的向量整数类型。
语义:¶
‘fptosi
’ 指令将其浮点操作数转换为最接近的(向零舍入)有符号整数值。 如果该值无法容纳在 ty2
中,则结果为 poison 值。
示例:¶
%X = fptosi double -123.0 to i32 ; yields i32:-123
%Y = fptosi float 1.0E-247 to i1 ; yields undefined:1
%Z = fptosi float 1.04E+17 to i8 ; yields undefined:1
‘uitofp .. to
’ 指令¶
语法:¶
<result> = uitofp <ty> <value> to <ty2> ; yields ty2
概述:¶
‘uitofp
’ 指令将 value
视为无符号整数,并将该值转换为 ty2
类型。
nneg
(非负)标志(如果存在)指定操作数为非负数。 优化过程可能会使用此属性稍后将 uitofp
转换为 sitofp
。
参数:¶
‘uitofp
’ 指令接受一个要转换的值,该值必须是标量或向量整数值,以及要将其转换成的类型 ty2
,该类型必须是浮点类型。 如果 ty
是向量整数类型,则 ty2
必须是具有与 ty
相同元素数量的向量浮点类型。
语义:¶
‘uitofp
’ 指令将其操作数解释为无符号整数值,并将其转换为相应的浮点值。 如果该值无法精确表示,则会使用默认舍入模式进行舍入。
如果设置了 nneg
标志,并且 uitofp
参数为负数,则结果为 poison 值。
示例:¶
%X = uitofp i32 257 to float ; yields float:257.0
%Y = uitofp i8 -1 to double ; yields double:255.0
%a = uitofp nneg i32 256 to i32 ; yields float:256.0
%b = uitofp nneg i32 -256 to i32 ; yields i32 poison
‘sitofp .. to
’ 指令¶
语法:¶
<result> = sitofp <ty> <value> to <ty2> ; yields ty2
概述:¶
‘sitofp
’ 指令将 value
视为有符号整数,并将该值转换为 ty2
类型。
参数:¶
‘sitofp
’ 指令接受一个要转换的值,该值必须是标量或向量整数值,以及要将其转换成的类型 ty2
,该类型必须是浮点类型。 如果 ty
是向量整数类型,则 ty2
必须是具有与 ty
相同元素数量的向量浮点类型。
语义:¶
‘sitofp
’ 指令将其操作数解释为有符号整数值,并将其转换为相应的浮点值。 如果该值无法精确表示,则会使用默认舍入模式进行舍入。
示例:¶
%X = sitofp i32 257 to float ; yields float:257.0
%Y = sitofp i8 -1 to double ; yields double:-1.0
‘ptrtoint .. to
’ 指令¶
语法:¶
<result> = ptrtoint <ty> <value> to <ty2> ; yields ty2
概述:¶
‘ptrtoint
’ 指令将指针或指针向量 value
转换为整数(或整数向量)类型 ty2
。
参数:¶
‘ptrtoint
’ 指令接受一个要转换的 value
,该值必须是指针类型的值或指针向量,以及要将其转换成的类型 ty2
,该类型必须是整数或整数向量类型。
语义:¶
‘ptrtoint
’ 指令通过将指针值解释为整数,并将该值截断或零扩展到整数类型的大小,从而将 value
转换为整数类型 ty2
。 如果 value
小于 ty2
,则执行零扩展。 如果 value
大于 ty2
,则执行截断。 如果它们的大小相同,则除了类型更改之外,不执行任何操作(空操作转换)。
示例:¶
%X = ptrtoint ptr %P to i8 ; yields truncation on 32-bit architecture
%Y = ptrtoint ptr %P to i64 ; yields zero extension on 32-bit architecture
%Z = ptrtoint <4 x ptr> %P to <4 x i64>; yields vector zero extension for a vector of addresses on 32-bit architecture
‘inttoptr .. to
’ 指令¶
语法:¶
<result> = inttoptr <ty> <value> to <ty2>[, !dereferenceable !<deref_bytes_node>][, !dereferenceable_or_null !<deref_bytes_node>] ; yields ty2
概述:¶
‘inttoptr
’ 指令将整数 value
转换为指针类型 ty2
。
参数:¶
‘inttoptr
’ 指令接受一个要转换的整数值,以及要将其转换成的类型,该类型必须是指针类型。
可选的 !dereferenceable
元数据必须引用单个元数据名称 <deref_bytes_node>
,该名称对应于具有一个 i64
条目的元数据节点。 请参阅 dereferenceable
元数据。
可选的 !dereferenceable_or_null
元数据必须引用单个元数据名称 <deref_bytes_node>
,该名称对应于具有一个 i64
条目的元数据节点。 请参阅 dereferenceable_or_null
元数据。
语义:¶
‘inttoptr
’ 指令通过应用零扩展或截断(取决于整数 value
的大小)将 value
转换为 ty2
类型。 如果 value
大于指针的大小,则执行截断。 如果 value
小于指针的大小,则执行零扩展。 如果它们的大小相同,则不执行任何操作(空操作转换)。
示例:¶
%X = inttoptr i32 255 to ptr ; yields zero extension on 64-bit architecture
%Y = inttoptr i32 255 to ptr ; yields no-op on 32-bit architecture
%Z = inttoptr i64 0 to ptr ; yields truncation on 32-bit architecture
%Z = inttoptr <4 x i32> %G to <4 x ptr>; yields truncation of vector G to four pointers
‘bitcast .. to
’ 指令¶
语法:¶
<result> = bitcast <ty> <value> to <ty2> ; yields ty2
概述:¶
‘bitcast
’ 指令将 value
转换为 ty2
类型,而不更改任何位。
参数:¶
‘bitcast
’ 指令接受一个要转换的值,该值必须是非聚合的 first class 值,以及要将其转换成的类型,该类型也必须是非聚合的first class 类型。 value
和目标类型 ty2
的位大小必须相同。 如果源类型是指针,则目标类型也必须是相同大小的指针。 此指令支持向量到位和到其他类型向量的按位转换(只要它们具有相同的大小)。
语义:¶
‘bitcast
’ 指令将 value
转换为 ty2
类型。 它始终是空操作转换,因为此转换不会更改任何位。 此转换的执行方式就好像 value
已存储到内存中并作为 ty2
类型读回一样。 指针(或指针向量)类型只能通过此指令转换为具有相同地址空间的其他指针(或指针向量)类型。 要将指针转换为其他类型,请首先使用 inttoptr 或 ptrtoint 指令。
对于涉及向量类型和字节序的位cast,有一个注意事项。 例如,bitcast <2 x i8> <value> to i16
对于小端序,将向量的零号元素放在 i16 的最低有效位中,而对于大端序,零号元素最终会放在最高有效位中。
示例:¶
%X = bitcast i8 255 to i8 ; yields i8 :-1
%Y = bitcast i32* %x to i16* ; yields i16*:%x
%Z = bitcast <2 x i32> %V to i64; ; yields i64: %V (depends on endianness)
%Z = bitcast <2 x i32*> %V to <2 x i64*> ; yields <2 x i64*>
‘addrspacecast .. to
’ 指令¶
语法:¶
<result> = addrspacecast <pty> <ptrval> to <pty2> ; yields pty2
概述:¶
‘addrspacecast
’ 指令将地址空间 n
中 pty
类型的 ptrval
转换为地址空间 m
中 pty2
类型。
参数:¶
‘addrspacecast
’ 指令接受一个要转换的指针或指针向量值,以及要将其转换成的指针类型,该指针类型必须具有不同的地址空间。
语义:¶
‘addrspacecast
’ 指令将指针值 ptrval
转换为 pty2
类型。 它可以是空操作转换或复杂的值修改,具体取决于目标和地址空间对。 同一地址空间内的指针转换必须使用 bitcast
指令执行。 请注意,如果地址空间转换产生可解引用的结果,则结果和操作数都指向相同的内存位置。 转换必须没有副作用,并且不得捕获指针的值。
如果源不是 poison,并且源和目标都是整数指针,并且结果指针是可解引用的,则假定转换是可逆的(即,将结果转换回原始地址空间应产生原始位模式)。
示例:¶
%X = addrspacecast ptr %x to ptr addrspace(1)
%Y = addrspacecast ptr addrspace(1) %y to ptr addrspace(2)
%Z = addrspacecast <4 x ptr> %z to <4 x ptr addrspace(3)>
其他操作¶
此类别中的指令是“杂项”指令,它们难以进行更好的分类。
‘icmp
’ 指令¶
语法:¶
<result> = icmp <cond> <ty> <op1>, <op2> ; yields i1 or <N x i1>:result
<result> = icmp samesign <cond> <ty> <op1>, <op2> ; yields i1 or <N x i1>:result
概述:¶
‘icmp
’ 指令根据其两个整数、整数向量、指针或指针向量操作数的比较,返回布尔值或布尔值向量。
参数:¶
‘icmp
’ 指令接受三个操作数。 第一个操作数是条件代码,指示要执行的比较类型。 它不是值,只是一个关键字。 可能的条件代码是
eq
:等于ne
:不等于ugt
:无符号大于uge
:无符号大于或等于ult
:无符号小于ule
:无符号小于或等于sgt
:有符号大于sge
:有符号大于或等于slt
:有符号小于sle
:有符号小于或等于
语义:¶
‘icmp
’ 根据作为 cond
给出的条件代码比较 op1
和 op2
。 执行的比较始终产生 i1 或 i1
向量结果,如下所示
eq
:如果操作数相等,则产生true
,否则产生false
。 无需或不执行符号解释。ne
:如果操作数不相等,则产生true
,否则产生false
。 无需或不执行符号解释。ugt
:将操作数解释为无符号值,如果op1
大于op2
,则产生true
。uge
:将操作数解释为无符号值,如果op1
大于或等于op2
,则产生true
。ult
:将操作数解释为无符号值,如果op1
小于op2
,则产生true
。ule
:将操作数解释为无符号值,如果op1
小于或等于op2
,则产生true
。sgt
:将操作数解释为有符号值,如果op1
大于op2
,则产生true
。sge
:将操作数解释为有符号值,如果op1
大于或等于op2
,则产生true
。slt
:将操作数解释为有符号值,如果op1
小于op2
,则产生true
。sle
:将操作数解释为有符号值,如果op1
小于或等于op2
,则产生true
。
如果操作数是指针类型,则指针值的比较方式如同它们是整数一样。
如果操作数是整数向量,则它们将逐元素进行比较。 结果是一个 i1
向量,其元素数量与要比较的值相同。 否则,结果为 i1
。
如果存在 samesign
关键字,并且操作数不是相同的符号,则结果为 poison 值。
示例:¶
<result> = icmp eq i32 4, 5 ; yields: result=false
<result> = icmp ne ptr %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
’ 指令¶
语法:¶
<result> = fcmp [fast-math flags]* <cond> <ty> <op1>, <op2> ; yields i1 or <N x i1>:result
概述:¶
‘fcmp
’ 指令根据其操作数的比较,返回布尔值或布尔值向量。
如果操作数是浮点标量,则结果类型为布尔值 (i1)。
如果操作数是浮点向量,则结果类型是布尔向量,其元素数量与要比较的操作数相同。
参数:¶
‘fcmp
’ 指令接受三个操作数。 第一个操作数是条件代码,指示要执行的比较类型。 它不是值,只是一个关键字。 可能的条件代码是
false
:不比较,始终返回 falseoeq
:有序且等于ogt
:有序且大于oge
:有序且大于或等于olt
:有序且小于ole
:有序且小于或等于one
:有序且不等于ord
:有序 (无 NaN)ueq
:无序或等于ugt
:无序或大于uge
:无序或大于或等于ult
:无序或小于ule
:无序或小于或等于une
:无序或不等于uno
:无序(任一为 NaN)true
:不比较,始终返回 true
有序表示操作数都不是 QNAN,而无序表示操作数中的任一操作数都可能是 QNAN。
语义:¶
‘fcmp
’ 指令根据作为 cond
给出的条件代码比较 op1
和 op2
。 如果操作数是向量,则向量将逐元素进行比较。 每次执行的比较始终产生 i1 结果,如下所示
false
:始终产生false
,与操作数无关。oeq
:如果两个操作数都不是 QNAN 且op1
等于op2
,则产生true
。ogt
:如果两个操作数都不是 QNAN 且op1
大于op2
,则产生true
。oge
:如果两个操作数都不是 QNAN 且op1
大于或等于op2
,则产生true
。olt
:如果两个操作数都不是 QNAN 且op1
小于op2
,则产生true
。ole
:如果两个操作数都不是 QNAN 且op1
小于或等于op2
,则产生true
。one
:如果两个操作数都不是 QNAN 且op1
不等于op2
,则产生true
。ord
:如果两个操作数都不是 QNAN,则产生true
。ueq
:如果任一操作数是 QNAN 或op1
等于op2
,则产生true
。ugt
:如果任一操作数是 QNAN 或op1
大于op2
,则产生true
。uge
:如果任一操作数是 QNAN 或op1
大于或等于op2
,则产生true
。ult
:如果任一操作数是 QNAN 或op1
小于op2
,则产生true
。ule
:如果任一操作数是 QNAN 或op1
小于或等于op2
,则产生true
。une
:如果任一操作数是 QNAN 或op1
不等于op2
,则产生true
。uno
:如果任一操作数是 QNAN,则产生true
。true
:始终产生true
,与操作数无关。
fcmp
指令还可以选择性地接受任意数量的快速数学标志,这些标志是优化提示,用于启用原本不安全的浮点优化。
任何快速数学标志集都可以在 fcmp
指令上合法使用,但对其语义产生任何影响的标志仅限于那些允许对输入参数值进行假设的标志;即 nnan
、ninf
和 reassoc
。有关更多信息,请参阅快速数学标志。
示例:¶
<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
’ 指令¶
语法:¶
<result> = phi [fast-math-flags] <ty> [ <val0>, <label0>], ...
概述:¶
‘phi
’ 指令用于实现表示函数 SSA 图中的 φ 节点。
参数:¶
传入值的类型通过第一个类型字段指定。在此之后,‘phi
’ 指令接受成对列表作为参数,当前块的每个前驱基本块对应一对。只有一等类型的值可以用作 PHI 节点的 value 参数。只有标签可以用作 label 参数。
在基本块的开始和 PHI 指令之间不得有非 phi 指令:即,PHI 指令必须是基本块中的第一个指令。
出于 SSA 形式的目的,每个传入值的使用都被认为发生在从相应前驱块到当前块的边上(但在同一边上定义 ‘invoke
’ 指令的返回值之后)。
可选的 fast-math-flags
标记指示 phi 具有一个或多个快速数学标志。这些标志是优化提示,用于启用原本不安全的浮点优化。快速数学标志仅对返回受支持的浮点类型的 phi 有效。
语义:¶
在运行时,‘phi
’ 指令逻辑上采用与紧接在当前块之前执行的前驱基本块对应的对指定的值。
示例:¶
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
’ 指令¶
语法:¶
<result> = select [fast-math flags] selty <cond>, <ty> <val1>, <ty> <val2> ; yields ty
selty is either i1 or {<N x i1>}
概述:¶
‘select
’ 指令用于根据条件选择一个值,而无需 IR 级别的分支。
参数:¶
‘select
’ 指令需要一个 ‘i1’ 值或 ‘i1’ 值向量来指示条件,以及两个相同一等类型的值。
语义:¶
如果条件是 i1 且求值为 1,则指令返回第一个 value 参数;否则,它返回第二个 value 参数。
如果条件是 i1 向量,则 value 参数必须是相同大小的向量,并且选择是逐元素完成的。
如果条件是 i1 且 value 参数是相同大小的向量,则选择整个向量。
示例:¶
%X = select i1 true, i8 17, i8 42 ; yields i8:17
‘freeze
’ 指令¶
语法:¶
<result> = freeze ty <val> ; yields ty:result
概述:¶
参数:¶
‘freeze
’ 指令接受单个参数。
语义:¶
如果参数是 undef
或 poison
,‘freeze
’ 返回类型为 ‘ty
’ 的任意但固定的值。否则,此指令是空操作,并返回输入参数。对同一 ‘freeze
’ 指令返回的值的所有使用都保证始终观察到相同的值,而不同的 ‘freeze
’ 指令可能会产生不同的值。
虽然可以冻结 undef
和 poison
指针,但结果是不可解引用的指针。有关更多信息,请参阅指针别名规则部分。如果聚合值或向量被冻结,则操作数是逐元素冻结的。聚合的填充不被考虑,因为它在不将其存储到内存中并使用不同类型加载的情况下是不可见的。
示例:¶
%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
’ 指令¶
语法:¶
<result> = [tail | musttail | notail ] call [fast-math flags] [cconv] [ret attrs] [addrspace(<num>)]
<ty>|<fnty> <fnptrval>(<function args>) [fn attrs] [ operand bundles ]
概述:¶
‘call
’ 指令表示一个简单的函数调用。
参数:¶
此指令需要多个参数
可选的
tail
和musttail
标记指示优化器应执行尾调用优化。tail
标记是一个提示,可以忽略。musttail
标记表示为了程序的正确性,必须对调用进行尾调用优化。即使存在诸如 “disable-tail-calls” 之类的属性,情况也是如此。musttail
标记提供以下保证如果调用是调用图中的递归循环的一部分,则不会导致无界堆栈增长。
具有inalloca 或 preallocated 属性的参数会被就地转发。
如果 musttail 调用出现在具有
"thunk"
属性的函数中,并且调用者和被调用者都具有 varargs,那么任何在寄存器或内存中未原型化的参数都会转发给被调用者。同样,被调用者的返回值将返回给调用者的调用者,即使正在使用 void 返回类型。
这两个标记都暗示被调用者不会访问调用者的 allocas、va_args 或 byval 参数。作为例外,alloca 或 byval 参数可以作为 byval 参数传递给被调用者,这可以在被调用者内部解引用。例如
declare void @take_byval(ptr byval(i64)) declare void @take_ptr(ptr) ; Invalid (assuming @take_ptr dereferences the pointer), because %local ; may be de-allocated before the call to @take_ptr. define void @invalid_alloca() { entry: %local = alloca i64 tail call void @take_ptr(ptr %local) ret void } ; Valid, the byval attribute causes the memory allocated by %local to be ; copied into @take_byval's stack frame. define void @byval_alloca() { entry: %local = alloca i64 tail call void @take_byval(ptr byval(i64) %local) ret void } ; Invalid, because @use_global_va_list uses the variadic arguments from ; @invalid_va_list. %struct.va_list = type { ptr } @va_list = external global %struct.va_list define void @use_global_va_list() { entry: %arg = va_arg ptr @va_list, i64 ret void } define void @invalid_va_list(i32 %a, ...) { entry: call void @llvm.va_start.p0(ptr @va_list) tail call void @use_global_va_list() ret void } ; Valid, byval argument forwarded to tail call as another byval argument. define void @forward_byval(ptr byval(i64) %x) { entry: tail call void @take_byval(ptr byval(i64) %x) ret void } ; Invalid (assuming @take_ptr dereferences the pointer), byval argument ; passed to tail callee as non-byval ptr. define void @invalid_byval(ptr byval(i64) %x) { entry: tail call void @take_ptr(ptr %x) ret void }
标记为
musttail
的调用必须遵守以下附加规则调用必须紧接在 ret 指令之前,或者是指针位广播,后跟 ret 指令。
ret 指令必须返回由调用、undef 或 void 生成的(可能经过位广播的)值。
调用者和被调用者的调用约定必须匹配。
当且仅当调用者是 varargs 时,被调用者必须是 varargs。只要非 varargs 前缀遵守其他规则,将非 varargs 函数位广播到适当的 varargs 类型是合法的。
返回类型不得自动转换为 sret 指针。
此外,如果调用约定不是 swifttailcc 或 tailcc
所有影响 ABI 的函数属性,例如 sret、byval、inreg、returned 和 inalloca,都必须匹配。
调用者和被调用者的原型必须匹配。参数或返回类型的指针类型在被指向类型上可能不同,但在地址空间上不得不同。
另一方面,如果调用约定是 swifttailcc 或 tailcc
仅允许这些影响 ABI 的属性:sret、byval、swiftself 和 swiftasync。
原型不需要匹配。
如果满足以下条件,则保证对标记为
tail
的调用进行尾调用优化调用者和被调用者都具有调用约定
fastcc
或tailcc
。调用位于尾部位置(ret 紧接在 call 之后,并且 ret 使用 call 的值或为 void)。
选项
-tailcallopt
已启用,llvm::GuaranteedTailCallOpt
为true
,或者调用约定为tailcc
。
可选的
notail
标记指示优化器不应向调用添加tail
或musttail
标记。它用于防止对调用执行尾调用优化。可选的
fast-math flags
标记指示调用具有一个或多个快速数学标志,这些标志是优化提示,用于启用原本不安全的浮点优化。快速数学标志仅对返回受支持的浮点类型的调用有效。可选的 “cconv” 标记指示调用应使用的调用约定。如果未指定,则调用默认为使用 C 调用约定。调用的调用约定必须与目标函数的调用约定匹配,否则行为未定义。
返回值的可选 参数属性 列表。此处仅 ‘
zeroext
’、‘signext
’、‘noext
’ 和 ‘inreg
’ 属性有效。可选的 addrspace 属性可用于指示被调用函数的地址空间。如果未指定,将使用来自 datalayout 字符串 的程序地址空间。
‘
ty
’:调用指令本身的类型,也是返回值的类型。不返回值的函数标记为void
。‘
fnty
’:应是被调用函数的签名。参数类型必须与此签名暗示的类型匹配。如果函数不是 varargs,则可以省略此类型。‘
fnptrval
’:包含指向要调用的函数的指针的 LLVM 值。在大多数情况下,这是一个直接函数调用,但间接call
也是可能的,调用指向函数值的任意指针。‘
function args
’:参数列表,其类型与函数签名参数类型和参数属性匹配。所有参数都必须是 first class 类型。如果函数签名指示函数接受可变数量的参数,则可以指定额外的参数。可选的 函数属性 列表。
可选的 操作数包 列表。
语义:¶
‘call
’ 指令用于使控制流转移到指定的函数,其传入参数绑定到指定的值。在被调用函数中的 ‘ret
’ 指令上,控制流继续执行函数调用之后的指令,并且函数的返回值绑定到 result 参数。
示例:¶
%retval = call i32 @test(i32 %argc)
call i32 (ptr, ...) @printf(ptr %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
llvm 将对某些函数(其名称和参数与标准 C99 库匹配)的调用视为 C99 库函数,并且可能会在该假设下执行优化或生成代码。这是我们将来希望更改的内容,以便为独立环境和非基于 C 的语言提供更好的支持。
‘va_arg
’ 指令¶
语法:¶
<resultval> = va_arg <va_list*> <arglist>, <argty>
概述:¶
‘va_arg
’ 指令用于访问通过函数调用的“可变参数”区域传递的参数。它用于实现 C 中的 va_arg
宏。
参数:¶
此指令接受一个 va_list*
值和参数的类型。它返回指定参数类型的值,并将 va_list
递增以指向下一个参数。va_list
的实际类型是目标特定的。
语义:¶
‘va_arg
’ 指令从指定的 va_list
加载指定类型的参数,并使 va_list
指向下一个参数。有关更多信息,请参阅可变参数处理内联函数。
在此指令在不接受可变数量参数的函数中调用是合法的,例如 vfprintf
函数。
va_arg
是一个 LLVM 指令,而不是内联函数,因为它接受一个类型作为参数。
示例:¶
请参阅可变参数处理部分。
请注意,代码生成器尚未完全支持许多目标上的 va_arg。此外,它目前不支持任何目标上具有聚合类型的 va_arg。
‘landingpad
’ 指令¶
语法:¶
<resultval> = landingpad <resultty> <clause>+
<resultval> = landingpad <resultty> cleanup <clause>*
<clause> := catch <type> <value>
<clause> := filter <array constant type> <array constant>
概述:¶
‘landingpad
’ 指令由 LLVM 的异常处理系统使用,以指定基本块是着陆点——异常着陆的地方,并对应于 try
/catch
序列的 catch
部分中找到的代码。它定义了在重新进入函数时由人格函数提供的值。resultval
的类型为 resultty
。
参数:¶
可选的 cleanup
标志指示着陆点块是一个清理程序。
clause
以子句类型开头——catch
或 filter
——并包含表示可能被捕获或分别过滤的“类型”的全局变量。与 catch
子句不同,filter
子句以数组常量作为其参数。对于无法抛出的过滤器,请使用 “[0 x ptr] undef
”。‘landingpad
’ 指令必须包含至少一个 clause
或 cleanup
标志。
语义:¶
‘landingpad
’ 指令定义了在重新进入函数时由人格函数设置的值,因此也是 ‘landingpad
’ 指令的“结果类型”。与调用约定一样,人格函数结果在 LLVM IR 中的表示方式是目标特定的。
子句按从上到下的顺序应用。如果两个 landingpad
指令通过内联合并在一起,则来自调用函数的子句将附加到子句列表的末尾。当调用堆栈由于抛出异常而正在展开时,会将异常与每个 clause
依次进行比较。如果它与任何子句都不匹配,并且未设置 cleanup
标志,则展开将继续沿着调用堆栈向上进行。
landingpad
指令有几个限制
着陆点块是一个基本块,它是 ‘
invoke
’ 指令的展开目标。着陆点块必须将 ‘
landingpad
’ 指令作为其第一个非 PHI 指令。在一个着陆点块中只能有一个 ‘
landingpad
’ 指令。不是着陆点块的基本块不得包含 ‘
landingpad
’ 指令。
示例:¶
;; A landing pad which can catch an integer.
%res = landingpad { ptr, i32 }
catch ptr @_ZTIi
;; A landing pad that is a cleanup.
%res = landingpad { ptr, i32 }
cleanup
;; A landing pad which can catch an integer and can only throw a double.
%res = landingpad { ptr, i32 }
catch ptr @_ZTIi
filter [1 x ptr] [ptr @_ZTId]
‘catchpad
’ 指令¶
语法:¶
<resultval> = catchpad within <catchswitch> [<args>*]
概述:¶
‘catchpad
’ 指令由 LLVM 的异常处理系统使用,以指定基本块开始一个 catch 处理程序——人格例程尝试将控制权转移到此处以捕获异常。
参数:¶
catchswitch
操作数必须始终是由前驱块中的 catchswitch 指令生成的令牌。这确保了每个 catchpad
恰好有一个前驱块,并且它始终以 catchswitch
终止。
args
对应于人格例程需要的任何信息,以了解这是否是异常的适当处理程序。如果这是异常的第一个适当处理程序,则控制权将转移到 catchpad
。
resultval
的类型为 token,用于将 catchpad
与相应的 catchrets 和其他嵌套的 EH pad 匹配。
语义:¶
当调用堆栈由于抛出异常而正在展开时,会将异常与 args
进行比较。如果它不匹配,则控制权将不会到达 catchpad
指令。args
的表示完全是特定于目标和人格函数的。
与landingpad 指令类似,catchpad
指令必须是其父基本块的第一个非 phi 指令。
由 catchpad
和其他 “pad” 指令生成和使用的令牌的含义在 Windows 异常处理文档中描述。
当 catchpad
已“进入”但尚未“退出”(如 EH 文档中所述)时,执行不带有适当的 “funclet” 捆绑的 call 或 invoke 是未定义的行为。
示例:¶
dispatch:
%cs = catchswitch within none [label %handler0] unwind to caller
;; A catch block which can catch an integer.
handler0:
%tok = catchpad within %cs [ptr @_ZTIi]
‘cleanuppad
’ 指令¶
语法:¶
<resultval> = cleanuppad within <parent> [<args>*]
概述:¶
‘cleanuppad
’ 指令由 LLVM 的异常处理系统使用,以指定基本块是清理块——人格例程尝试将控制权转移到此处以运行清理操作。args
对应于 人格函数执行清理所需的任何附加信息。resultval
的类型为 token,用于将 cleanuppad
与相应的 cleanuprets 匹配。parent
参数是包含 cleanuppad
指令的 funclet 的令牌。如果 cleanuppad
不在 funclet 内部,则此操作数可以是令牌 none
。
参数:¶
该指令接受由人格函数解释的任意值列表。
语义:¶
当调用堆栈由于抛出异常而正在展开时,人格函数借助特定于人格的参数将控制权转移到 cleanuppad
。与调用约定一样,人格函数结果在 LLVM IR 中的表示方式是目标特定的。
cleanuppad
指令有几个限制
清理块是一个基本块,它是异常指令的展开目标。
清理块必须将 ‘
cleanuppad
’ 指令作为其第一个非 PHI 指令。在一个清理块中只能有一个 ‘
cleanuppad
’ 指令。不是清理块的基本块不得包含 ‘
cleanuppad
’ 指令。
当 cleanuppad
已“进入”但尚未“退出”(如 EH 文档中所述)时,执行不带有适当的 “funclet” 捆绑的 call 或 invoke 是未定义的行为。
示例:¶
%tok = cleanuppad within %cs []
调试记录¶
调试记录与指令交错出现,但不是指令;它们仅用于定义调试信息,对生成的代码没有影响。它们通过使用前导 # 和额外的缩进级别与指令区分开来。例如
%inst1 = op1 %a, %b
#dbg_value(%inst1, !10, !DIExpression(), !11)
%inst2 = op2 %inst1, %c
这些调试记录取代了之前的调试内联函数。如果将 --write-experimental-debuginfo=false
传递给 LLVM,则调试记录将被禁用;记录和内联函数都出现在同一模块中是错误的。有关调试记录的更多信息,请参见LLVM 源代码级调试文档。
内联函数¶
LLVM 支持“内联函数”的概念。这些函数具有众所周知的名称和语义,并且需要遵循某些限制。总的来说,这些内联函数代表 LLVM 语言的扩展机制,当向语言添加内容时,不需要更改 LLVM 中的所有转换(或 bitcode 读取器/写入器、解析器等...)。
内联函数名称都必须以 “llvm.
” 前缀开头。此前缀在 LLVM 中为内联名称保留;因此,函数名称不得以此前缀开头。内联函数必须始终是外部函数:您不能定义内联函数的主体。内联函数只能在 call 或 invoke 指令中使用:获取内联函数的地址是非法的。此外,由于内联函数是 LLVM 语言的一部分,因此如果添加任何内联函数,则必须在此处记录它们。
某些内联函数可以重载,即,内联函数表示执行相同操作但在不同数据类型上执行的函数族。由于 LLVM 可以表示超过 800 万种不同的整数类型,因此通常使用重载来允许内联函数在任何整数类型上运行。一个或多个参数类型或结果类型可以重载以接受任何整数类型。参数类型也可以定义为与先前参数的类型或结果类型完全匹配。这允许接受多个参数但需要所有参数都具有相同类型的内联函数仅针对单个参数或结果进行重载。
重载的内联函数会将其重载的参数类型名称编码到其函数名称中,每个类型名称前都有一个句点。只有那些被重载的类型会导致名称后缀。类型与其他类型匹配的参数则不会。例如,llvm.ctpop
函数可以接受任何宽度的整数,并返回宽度完全相同的整数。 这就产生了一系列函数,例如 i8 @llvm.ctpop.i8(i8 %val)
和 i29 @llvm.ctpop.i29(i29 %val)
。只有一个类型(返回类型)被重载,因此只需要一个类型后缀。由于参数的类型与返回类型匹配,因此不需要自己的名称后缀。
未命名类型 被编码为 s_s
。重载的内联函数如果其重载的参数类型之一依赖于未命名类型,则会获得额外的 .<number>
后缀。 这允许区分具有不同未命名类型参数的内联函数。(例如:llvm.ssa.copy.p0s_s.2(%42*)
)该数字在 LLVM 模块中被跟踪,并确保模块中的名称唯一性。当链接两个模块时,仍然可能发生名称冲突。在这种情况下,其中一个名称将被更改,获得一个新的数字。
对于正在为后端代码生成定义内联函数的目标开发者来说,不应依赖仅基于整数或浮点类型之间区别的任何内联函数重载来实现正确的代码生成。在这种情况下,目标维护者在定义内联函数时,推荐的方法是创建单独的整数和 FP 内联函数,而不是依赖重载。 例如,如果 llvm.target.foo(<4 x i32>)
和 llvm.target.foo(<4 x float>)
需要不同的代码生成,那么应该将它们拆分为不同的内联函数。
要了解如何添加内联函数,请参阅 扩展 LLVM 指南。
可变参数处理内联函数¶
LLVM 中使用 va_arg 指令和以下三个内联函数定义了可变参数支持。 这些函数与 <stdarg.h>
头文件中定义的同名宏相关。
所有这些函数都将指向目标特定值类型 “va_list
” 的指针作为参数。 LLVM 汇编语言参考手册没有定义此类型是什么,因此所有转换都应准备好处理这些函数,而无需考虑使用的类型。 这些内联函数被重载,可以用于指向不同地址空间的指针。
此示例显示了如何使用 va_arg 指令和可变参数处理内联函数。
; This struct is different for every platform. For most platforms,
; it is merely a ptr.
%struct.va_list = type { ptr }
; For Unix x86_64 platforms, va_list is the following struct:
; %struct.va_list = type { i32, i32, ptr, ptr }
define i32 @test(i32 %X, ...) {
; Initialize variable argument processing
%ap = alloca %struct.va_list
call void @llvm.va_start.p0(ptr %ap)
; Read a single integer argument
%tmp = va_arg ptr %ap, i32
; Demonstrate usage of llvm.va_copy and llvm.va_end
%aq = alloca ptr
call void @llvm.va_copy.p0(ptr %aq, ptr %ap)
call void @llvm.va_end.p0(ptr %aq)
; Stop processing of arguments.
call void @llvm.va_end.p0(ptr %ap)
ret i32 %tmp
}
declare void @llvm.va_start.p0(ptr)
declare void @llvm.va_copy.p0(ptr, ptr)
declare void @llvm.va_end.p0(ptr)
‘llvm.va_start
’ 内联函数¶
语法:¶
declare void @llvm.va_start.p0(ptr <arglist>)
declare void @llvm.va_start.p5(ptr addrspace(5) <arglist>)
概述:¶
‘llvm.va_start
’ 内联函数初始化 <arglist>
,以便后续被 va_arg
使用。
参数:¶
参数是指向要初始化的 va_list
元素的指针。
语义:¶
‘llvm.va_start
’ 内联函数的工作方式与 C 中可用的 va_start
宏完全相同。 它以目标相关的方式初始化参数指向的 va_list
元素,以便下次调用 va_arg
将产生传递给函数的第一个可变参数。 与 C va_start
宏不同,此内联函数不需要知道函数的最后一个参数,因为编译器可以计算出来。
‘llvm.va_end
’ 内联函数¶
语法:¶
declare void @llvm.va_end.p0(ptr <arglist>)
declare void @llvm.va_end.p5(ptr addrspace(5) <arglist>)
概述:¶
‘llvm.va_end
’ 内联函数销毁 <arglist>
,该 <arglist>
之前已使用 llvm.va_start
或 llvm.va_copy
初始化。
参数:¶
参数是指向要销毁的 va_list
的指针。
语义:¶
‘llvm.va_end
’ 内联函数的工作方式与 C 中可用的 va_end
宏完全相同。 它以目标相关的方式销毁参数指向的 va_list
元素。 对 llvm.va_start 和 llvm.va_copy 的调用必须与对 llvm.va_end
的调用完全匹配。
‘llvm.va_copy
’ 内联函数¶
语法:¶
declare void @llvm.va_copy.p0(ptr <destarglist>, ptr <srcarglist>)
declare void @llvm.va_copy.p5(ptr addrspace(5) <destarglist>, ptr addrspace(5) <srcarglist>)
概述:¶
‘llvm.va_copy
’ 内联函数将源参数列表中的当前参数位置复制到目标参数列表。
参数:¶
第一个参数是指向要初始化的 va_list
元素的指针。 第二个参数是指向要从中复制的 va_list
元素的指针。 两个参数的地址空间必须匹配。
语义:¶
‘llvm.va_copy
’ 内联函数的工作方式与 C 中可用的 va_copy
宏完全相同。 它以目标相关的方式将源 va_list
元素复制到目标 va_list
元素中。 此内联函数是必要的,因为 `` llvm.va_start`` 内联函数可能非常复杂,例如,需要内存分配。
精确垃圾回收内联函数¶
LLVM 对 精确垃圾回收 (GC) 的支持要求前端生成包含适当内联函数调用的代码,并选择一种合适的 GC 策略,该策略知道如何以适合目标收集器的方式降低这些内联函数。
这些内联函数允许识别 栈上的 GC 根,以及需要 读取 和 写入 屏障的垃圾收集器实现。 类型安全的垃圾回收语言的前端应生成这些内联函数,以利用 LLVM 垃圾收集器。 有关更多详细信息,请参阅 LLVM 的垃圾回收。
LLVM 提供了第二组实验性内联函数,用于描述已编译代码中的垃圾回收安全点。 这些内联函数是 llvm.gcroot
内联函数的替代方案,但与 读取 和 写入 屏障的内联函数兼容。 LLVM 的垃圾回收 文档中介绍了方法上的差异。 内联函数本身在 LLVM 中的垃圾回收安全点 中描述。
‘llvm.gcroot
’ 内联函数¶
语法:¶
declare void @llvm.gcroot(ptr %ptrloc, ptr %metadata)
概述:¶
‘llvm.gcroot
’ 内联函数向代码生成器声明 GC 根的存在,并允许将一些元数据与其关联。
参数:¶
第一个参数指定包含根指针的栈对象的地址。 第二个指针(必须是常量或全局值地址)包含要与根关联的元数据。
语义:¶
在运行时,对此内联函数的调用会将空指针存储到 “ptrloc” 位置。 在编译时,代码生成器生成信息,以允许运行时在 GC 安全点找到指针。 ‘llvm.gcroot
’ 内联函数只能在 指定 GC 算法 的函数中使用。
‘llvm.gcread
’ 内联函数¶
语法:¶
declare ptr @llvm.gcread(ptr %ObjPtr, ptr %Ptr)
概述:¶
‘llvm.gcread
’ 内联函数标识从堆位置读取引用的操作,允许需要读取屏障的垃圾收集器实现。
参数:¶
第二个参数是要从中读取的地址,该地址应该是从垃圾收集器分配的地址。 第一个对象是指向被引用对象起点的指针,如果语言运行时需要(否则为空)。
语义:¶
‘llvm.gcread
’ 内联函数具有与 load 指令相同的语义,但可以根据需要被垃圾收集器运行时替换为更复杂的代码。 ‘llvm.gcread
’ 内联函数只能在 指定 GC 算法 的函数中使用。
‘llvm.gcwrite
’ 内联函数¶
语法:¶
declare void @llvm.gcwrite(ptr %P1, ptr %Obj, ptr %P2)
概述:¶
‘llvm.gcwrite
’ 内联函数标识对堆位置的引用写入操作,允许需要写入屏障的垃圾收集器实现(例如,分代或引用计数收集器)。
参数:¶
第一个参数是要存储的引用,第二个参数是要存储到其中的对象的起点,第三个参数是要存储到的 Obj 字段的地址。 如果运行时不需要指向对象的指针,则 Obj 可以为空。
语义:¶
‘llvm.gcwrite
’ 内联函数具有与 store 指令相同的语义,但可以根据需要被垃圾收集器运行时替换为更复杂的代码。 ‘llvm.gcwrite
’ 内联函数只能在 指定 GC 算法 的函数中使用。
‘llvm.experimental.gc.statepoint
’ 内联函数¶
语法:¶
declare token
@llvm.experimental.gc.statepoint(i64 <id>, i32 <num patch bytes>,
ptr elementtype(func_type) <target>,
i64 <#call args>, i64 <flags>,
... (call parameters),
i64 0, i64 0)
概述:¶
statepoint 内联函数表示运行时可解析的调用。
操作数:¶
‘id’ 操作数是一个常量整数,在生成的 stackmap 中报告为 ID 字段。 LLVM 不以任何方式解释此参数,其含义由 statepoint 用户决定。 请注意,LLVM 可以自由复制包含 statepoint 调用的代码,这可能会将每个词法调用 statepoint 都有唯一 ‘id’ 的 IR 转换为没有唯一 ‘id’ 的 IR。
如果 ‘num patch bytes’ 非零,则不会发出与 statepoint 对应的 call 指令,LLVM 会在其位置发出 ‘num patch bytes’ 字节的 nops。 LLVM 将发出代码来准备函数参数并根据调用约定检索函数返回值;前者在 nop 序列之前,后者在 nop 序列之后。 预计用户将在执行生成的机器代码之前,使用特定于其运行时的调用序列来修补 ‘num patch bytes’ 字节的 nops。 不保证 nop 序列的对齐方式。 与 LLVM 中的 Stack map 和补丁点 不同,statepoint 没有阴影字节的概念。 请注意,在语义上,statepoint 仍然表示对 ‘target’ 的调用或调用指令,并且修补后的 nop 序列预计表示等效于对 ‘target’ 的调用或调用指令的操作。
‘target’ 操作数是实际被调用的函数。 该操作数必须具有 elementtype 属性,指定目标的函数类型。 目标可以指定为符号 LLVM 函数,也可以指定为指针类型的任意 Value。 请注意,函数类型必须与被调用者的签名和 ‘call parameters’ 参数的类型匹配。
‘#call args’ 操作数是实际调用的参数数量。 它必须与 ‘call parameters’ 可变长度部分中传递的参数数量完全匹配。
‘flags’ 操作数用于指定有关 statepoint 的额外信息。 目前,它仅用于将某些 statepoint 标记为 GC 转换。 此操作数是一个 64 位整数,具有以下布局,其中位 0 是最低有效位
位 #
用途
0
如果 statepoint 是 GC 转换,则设置;否则清除。
1-63
保留供将来使用;必须清除。
‘call parameters’ 参数只是需要传递给调用目标的参数。 它们将根据指定的调用约定进行降低,并在其他方面像正常的 call 指令一样处理。 参数的数量必须与 ‘# call args’ 中指定的数量完全匹配。 类型必须与 ‘target’ 的签名匹配。
‘call parameter’ 属性之后必须跟两个 ‘i64 0’ 常量。 这些最初是 ‘gc transition parameter’ 和 ‘deopt parameter’ 参数的长度前缀,但是这些参数集的作用已完全被相应的操作数捆绑包取代。 在未来的修订版中,这些现在冗余的参数将被删除。
语义:¶
statepoint 被假定为读取和写入所有内存。 因此,内存操作不能在 statepoint 之后重新排序。 将 statepoint 标记为 ‘readonly’ 或 ‘readnone’ 是非法的。
请注意,合法的 IR 不能在静态可从 statepoint 访问的位置对 statepoint 的 ‘gc pointer’ 参数执行任何内存操作。 相反,必须使用显式重定位的值(来自 gc.relocate
)。
‘llvm.experimental.gc.result
’ 内联函数¶
语法:¶
declare type
@llvm.experimental.gc.result(token %statepoint_token)
概述:¶
gc.result
提取被 gc.statepoint
替换的原始 call 指令的结果。 由于实现限制,gc.result
内联函数实际上是三个内联函数的族。 除了返回值类型之外,语义是相同的。
操作数:¶
第一个也是唯一的参数是 gc.statepoint
,它启动了安全点序列,而此 gc.result
是该序列的一部分。 尽管将其类型化为通用令牌,但只有由 gc.statepoint
定义的值在此处是合法的。
语义:¶
gc.result
表示 statepoint
的调用目标的返回值。 gc.result
的类型必须与目标的类型完全匹配。 如果调用目标返回 void,则不会有 gc.result
。
gc.result
被建模为 ‘readnone’ 纯函数。 它没有副作用,因为它只是由 gc.statepoint
表示的先前调用的返回值的投影。
‘llvm.experimental.gc.relocate
’ 内联函数¶
语法:¶
declare <pointer type>
@llvm.experimental.gc.relocate(token %statepoint_token,
i32 %base_offset,
i32 %pointer_offset)
概述:¶
gc.relocate
返回安全点处指针的可能重定位值。
操作数:¶
第一个参数是 gc.statepoint
,它启动了安全点序列,而此 gc.relocation
是该序列的一部分。 尽管将其类型化为通用令牌,但只有由 gc.statepoint
定义的值在此处是合法的。
第二个和第三个参数都是索引,索引到相应 statepoint 的 gc-live 操作数捆绑包的操作数中。
第二个参数是一个索引,用于指定正在重定位的指针的分配。 关联的值必须在与正在重定位的指针关联的对象内。 优化器可以自由更改哪个内部派生指针被报告,前提是它不会将实际的基指针替换为另一个内部派生指针。 收集器可以依赖于基指针操作数保持为实际的基指针(如果这样构建)。
第三个参数是一个索引,用于指定正在重定位的(可能)派生指针。 如果且仅当正在重定位基指针时,此索引才可以与第二个参数相同。
语义:¶
gc.relocate
的返回值是由其参数指定的指针的可能重定位值。 返回指针的值如何与 gc.statepoint
的参数相关是未指定的,除了 a) 它指向具有相同偏移量的同一源语言对象,以及 b) 新重定位指针的 ‘based-on’ 关系是未重定位指针的投影。 特别是,返回指针的整数值是未指定的。
gc.relocate
被建模为 readnone
纯函数。 它没有副作用,因为它只是一种提取有关由 gc.statepoint
建模的实际调用期间完成的工作的信息的方式。
‘llvm.experimental.gc.get.pointer.base
’ 内联函数¶
语法:¶
declare <pointer type>
@llvm.experimental.gc.get.pointer.base(
<pointer type> readnone captures(none) %derived_ptr)
nounwind willreturn memory(none)
概述:¶
对于派生指针,gc.get.pointer.base
返回其基指针。
操作数:¶
唯一的参数是指针,该指针基于某个对象,并且与该对象的基址具有未知的偏移量。
语义:¶
此内联函数在 GC 的抽象机模型中使用,以表示任意派生指针的基指针。
此内联函数由 RewriteStatepointsForGC pass 内联,方法是将对此调用站点的所有使用替换为派生指针与其基指针值的偏移量。 替换作为降低到显式 statepoint 模型的一部分完成。
返回指针类型必须与参数的类型相同。
‘llvm.experimental.gc.get.pointer.offset
’ 内联函数¶
语法:¶
declare i64
@llvm.experimental.gc.get.pointer.offset(
<pointer type> readnone captures(none) %derived_ptr)
nounwind willreturn memory(none)
概述:¶
对于派生指针,gc.get.pointer.offset
返回其基指针的偏移量。
操作数:¶
唯一的参数是指针,该指针基于某个对象,并且与该对象的基址具有未知的偏移量。
语义:¶
此内联函数在 GC 的抽象机模型中使用,以表示任意派生指针与其基指针的偏移量。
此内联函数由 RewriteStatepointsForGC pass 内联,方法是将对此调用站点的所有使用替换为派生指针与其基指针值的偏移量。 替换作为降低到显式 statepoint 模型的一部分完成。
基本上,此调用计算派生指针与其基指针之间的差值(请参阅 ‘llvm.experimental.gc.get.pointer.base’ 内联函数),两者都进行了 ptrtoint 转换。 但是,在 RewriteStatepointsForGC pass 之外完成的此转换可能会导致指针丢失,从而无法进一步从抽象模型降低到显式物理模型。
代码生成器内联函数¶
LLVM 提供了这些内联函数,以公开可能只能通过代码生成器支持实现的特殊功能。
‘llvm.returnaddress
’ 内联函数¶
语法:¶
declare ptr @llvm.returnaddress(i32 <level>)
概述:¶
‘llvm.returnaddress
’ 内联函数尝试计算目标特定的值,该值指示当前函数或其调用者之一的返回地址。
参数:¶
此内联函数的参数指示要返回哪个函数的地址。 零表示调用函数,一表示其调用者,依此类推。 参数必须是常量整数值。
语义:¶
‘llvm.returnaddress
’ 内联函数返回一个指针,指示指定调用帧的返回地址;如果无法识别,则返回零。 对于零以外的参数,此内联函数返回的值可能不正确或为 0,因此它仅应用于调试目的。
请注意,调用此内联函数不会阻止函数内联或其他激进的转换,因此返回的值可能不是显式的源语言调用者的值。
‘llvm.addressofreturnaddress
’ 内联函数¶
语法:¶
declare ptr @llvm.addressofreturnaddress()
概述:¶
‘llvm.addressofreturnaddress
’ 内联函数返回一个目标特定的指针,该指针指向栈帧中存储当前函数的返回地址的位置。
语义:¶
请注意,调用此内联函数不会阻止函数内联或其他激进的转换,因此返回的值可能不是显式的源语言调用者的值。
此内联函数仅针对 x86 和 aarch64 实现。
‘llvm.sponentry
’ 内联函数¶
语法:¶
declare ptr @llvm.sponentry()
概述:¶
‘llvm.sponentry
’ 内联函数返回调用此内联函数的当前函数入口处的栈指针值。
语义:¶
请注意,此内联函数仅在 AArch64 和 ARM 上验证。
‘llvm.frameaddress
’ 内联函数¶
语法:¶
declare ptr @llvm.frameaddress(i32 <level>)
概述:¶
‘llvm.frameaddress
’ 内联函数尝试返回指定栈帧的目标特定帧指针值。
参数:¶
此内联函数的参数指示要返回哪个函数的帧指针。 零表示调用函数,一表示其调用者,依此类推。 参数必须是常量整数值。
语义:¶
‘llvm.frameaddress
’ 内联函数返回一个指针,指示指定调用帧的帧地址;如果无法识别,则返回零。 对于零以外的参数,此内联函数返回的值可能不正确或为 0,因此它仅应用于调试目的。
请注意,调用此内联函数不会阻止函数内联或其他激进的转换,因此返回的值可能不是显式的源语言调用者的值。
‘llvm.swift.async.context.addr
’ 内联函数¶
语法:¶
declare ptr @llvm.swift.async.context.addr()
概述:¶
‘llvm.swift.async.context.addr
’ 内联函数返回一个指针,该指针指向扩展帧记录中包含 Swift 执行的异步上下文的部分。
语义:¶
如果调用者具有 swiftasync
参数,则该参数最初将存储在返回的地址。 否则,它将被初始化为空。
‘llvm.localescape
’ 和 ‘llvm.localrecover
’ 内联函数¶
语法:¶
declare void @llvm.localescape(...)
declare ptr @llvm.localrecover(ptr %func, ptr %fp, i32 %idx)
概述:¶
‘llvm.localescape
’ 内部函数会逃逸出一组静态 alloca 的偏移量,而 ‘llvm.localrecover
’ 内部函数会将这些偏移量应用于活动帧指针,以恢复分配的地址。偏移量是在 ‘llvm.localescape
’ 调用者的帧布局期间计算的。
参数:¶
‘llvm.localescape
’ 的所有参数必须是指向静态 alloca 的指针或静态 alloca 的强制类型转换。每个函数只能调用 ‘llvm.localescape
’ 一次,且只能从入口块调用。
‘llvm.localrecover
’ 的 func
参数必须是指向当前模块中定义的函数的常量位转换指针。代码生成器无法确定在其他模块中定义的函数的帧分配偏移量。
‘llvm.localrecover
’ 的 fp
参数必须是当前活动的调用帧的帧指针。‘llvm.localaddress
’ 的返回值是生成此类值的一种方法,但各种运行时环境也以平台特定的方式公开合适的指针。
‘llvm.localrecover
’ 的 idx
参数指示要恢复传递给 ‘llvm.localescape
’ 的哪个 alloca。它是从零开始索引的。
语义:¶
这些内部函数允许一组函数共享对一个父函数的本地堆栈分配集合的访问权限。父函数可以从函数入口块调用 ‘llvm.localescape
’ 内部函数一次,而子函数可以使用 ‘llvm.localrecover
’ 来访问逃逸的 alloca。‘llvm.localescape
’ 内部函数会阻止内联,因为内联会更改逃逸的 alloca 的分配位置,这将破坏使用 ‘llvm.localrecover
’ 的尝试。
‘llvm.seh.try.begin
’ 和 ‘llvm.seh.try.end
’ 内部函数¶
语法:¶
declare void @llvm.seh.try.begin()
declare void @llvm.seh.try.end()
概述:¶
‘llvm.seh.try.begin
’ 和 ‘llvm.seh.try.end
’ 内部函数标记 Windows SEH 异步异常处理的 _try 区域的边界。
语义:¶
当使用 Windows SEH 异步异常选项 -feh_asynch(又名 MSVC -EHa)编译 C 函数时,会注入这两个内部函数以标记 _try 边界,并防止潜在的异常跨越边界移动。然后,可以通过 volatile 加载读取它们的叶输入,并通过 volatile 存储写入它们的根输出,将任何操作集限制在该区域内。
‘llvm.seh.scope.begin
’ 和 ‘llvm.seh.scope.end
’ 内部函数¶
语法:¶
declare void @llvm.seh.scope.begin()
declare void @llvm.seh.scope.end()
概述:¶
‘llvm.seh.scope.begin
’ 和 ‘llvm.seh.scope.end
’ 内部函数标记 Windows SEH 异步异常处理 (MSVC 选项 -EHa) 的 CPP 对象生命周期的边界。
语义:¶
LLVM 的普通异常处理表示仅将 EH 清理程序和处理程序与 invoke
相关联,而 invoke
通常仅对应于调用站点。为了支持任意的故障指令,必须能够为任何指令恢复当前的 EH 作用域。将 LLVM 中每个可能发生故障的操作都转换为新的、可能抛出异常的内部函数的 invoke
将需要添加大量的内部函数,阻碍这些操作的优化,并通过引入许多额外的基本块来降低编译速度。这些内部函数可以用来标记受清理程序保护的区域,例如具有非平凡析构函数的本地 C++ 对象。llvm.seh.scope.begin
用于标记区域的开始;它始终与 invoke
一起调用,其中 unwind 块是区域内任何可能抛出异常的指令的所需 unwind 目标。llvm.seh.scope.end 用于标记作用域何时结束以及不再需要 EH 清理程序(例如,因为正在调用析构函数)。
‘llvm.read_register
’、‘llvm.read_volatile_register
’ 和 ‘llvm.write_register
’ 内部函数¶
语法:¶
declare i32 @llvm.read_register.i32(metadata)
declare i64 @llvm.read_register.i64(metadata)
declare i32 @llvm.read_volatile_register.i32(metadata)
declare i64 @llvm.read_volatile_register.i64(metadata)
declare void @llvm.write_register.i32(metadata, i32 @value)
declare void @llvm.write_register.i64(metadata, i64 @value)
!0 = !{!"sp\00"}
概述:¶
‘llvm.read_register
’、‘llvm.read_volatile_register
’ 和 ‘llvm.write_register
’ 内部函数提供对命名寄存器的访问。寄存器必须在要编译到的架构上有效。类型需要与正在读取的寄存器兼容。
语义:¶
‘llvm.read_register
’ 和 ‘llvm.read_volatile_register
’ 内部函数返回寄存器的当前值(如果可能)。‘llvm.write_register
’ 内部函数设置寄存器的当前值(如果可能)。
调用 ‘llvm.read_volatile_register
’ 假定具有副作用,并且每次可能返回不同的值(例如,对于定时器寄存器)。
这对于实现需要始终映射到特定寄存器的命名寄存器全局变量非常有用,这在包括 OS 内核的裸机程序中是很常见的做法。
编译器不检查寄存器的可用性或周围代码(包括内联汇编)中使用的寄存器。因此,不支持可分配寄存器。
警告:到目前为止,它仅适用于选定架构(ARM、AArch64、PowerPC 和 x86_64)上的堆栈指针。需要大量工作来支持其他寄存器,甚至更多的是可分配寄存器。
‘llvm.stacksave
’ 内部函数¶
语法:¶
declare ptr @llvm.stacksave.p0()
declare ptr addrspace(5) @llvm.stacksave.p5()
概述:¶
‘llvm.stacksave
’ 内部函数用于记住函数堆栈的当前状态,以供 llvm.stackrestore 使用。这对于实现 C99 中作用域自动变量大小数组等语言特性非常有用。
语义:¶
此内部函数返回一个不透明的指针值,该值可以传递给 llvm.stackrestore。当使用从 llvm.stacksave
保存的值执行 llvm.stackrestore
内部函数时,它有效地将堆栈状态恢复到执行 llvm.stacksave
内部函数时的状态。实际上,这会从堆栈中弹出在执行 llvm.stacksave
之后分配的任何 alloca 块。地址空间通常应该是 alloca 地址空间。
‘llvm.stackrestore
’ 内部函数¶
语法:¶
declare void @llvm.stackrestore.p0(ptr %ptr)
declare void @llvm.stackrestore.p5(ptr addrspace(5) %ptr)
概述:¶
‘llvm.stackrestore
’ 内部函数用于将函数堆栈的状态恢复到执行相应 llvm.stacksave 内部函数时的状态。这对于实现 C99 中作用域自动变量大小数组等语言特性非常有用。地址空间通常应该是 alloca 地址空间。
语义:¶
请参阅 llvm.stacksave 的描述。
‘llvm.get.dynamic.area.offset
’ 内部函数¶
语法:¶
declare i32 @llvm.get.dynamic.area.offset.i32()
declare i64 @llvm.get.dynamic.area.offset.i64()
概述:¶
‘
llvm.get.dynamic.area.offset.*
’ 内部函数族用于获取从本机堆栈指针到调用者堆栈上最近动态 alloca 的地址的偏移量。这些内部函数旨在与 llvm.stacksave 结合使用,以获取指向最近动态 alloca 的指针。例如,这对于 AddressSanitizer 的堆栈取消中毒例程非常有用。
语义:¶
这些内部函数返回一个非负整数值,该值可用于获取调用者堆栈上 alloca 分配的最近动态 alloca 的地址。特别是,对于堆栈向下增长的目标,将此偏移量添加到本机堆栈指针将获得最近动态 alloca 的地址。对于堆栈向上增长的目标,情况会稍微复杂一些,因为从堆栈指针中减去此值将获得超出最近动态 alloca 末尾一个字节的地址。
尽管对于大多数目标,llvm.get.dynamic.area.offset <int_get_dynamic_area_offset> 仅返回零,但对于其他目标(例如 PowerPC 和 PowerPC64),它返回编译时已知的常量值。
llvm.get.dynamic.area.offset 的返回值类型必须与目标的默认地址空间(地址空间 0)的指针类型匹配。
‘llvm.prefetch
’ 内部函数¶
语法:¶
declare void @llvm.prefetch(ptr <address>, i32 <rw>, i32 <locality>, i32 <cache type>)
概述:¶
‘llvm.prefetch
’ 内部函数是代码生成器的提示,用于在支持的情况下插入预取指令;否则,它是一个空操作。预取对程序的行为没有影响,但可以改变其性能特征。
参数:¶
address
是要预取的地址,rw
是确定提取应为读取 (0) 还是写入 (1) 的说明符,locality
是时间局部性说明符,范围从 (0) - 无局部性到 (3) - 极局部性,保留在缓存中。cache type
指定预取是在数据 (1) 缓存还是指令 (0) 缓存上执行。rw
、locality
和 cache type
参数必须是常量整数。
语义:¶
此内部函数不会修改程序的行为。特别是,预取不能陷入,也不会产生值。在支持此内部函数的目标上,预取可以为处理器缓存提供提示,以获得更好的性能。
‘llvm.pcmarker
’ 内部函数¶
语法:¶
declare void @llvm.pcmarker(i32 <id>)
概述:¶
‘llvm.pcmarker
’ 内部函数是一种在代码区域中将程序计数器 (PC) 导出到模拟器和其他工具的方法。该方法是目标特定的,但预计标记将使用导出的符号来传输标记的 PC。标记不保证在优化后它将保留在任何特定指令中。标记的存在可能会抑制优化。预期用途是在优化后插入,以允许模拟运行的相关性。
参数:¶
id
是标识标记的数字 ID。
语义:¶
此内部函数不会修改程序的行为。不支持此内部函数的后端可能会忽略它。
‘llvm.readcyclecounter
’ 内部函数¶
语法:¶
declare i64 @llvm.readcyclecounter()
概述:¶
‘llvm.readcyclecounter
’ 内部函数提供对支持它的目标上的周期计数器寄存器(或类似的低延迟、高精度时钟)的访问。在 X86 上,它应映射到 RDTSC。在 Alpha 上,它应映射到 RPCC。由于后备计数器溢出速度很快(在 alpha 上约为 9 秒),因此这仅应用于小型计时。
语义:¶
当直接支持时,读取周期计数器不应修改任何内存。允许实现返回应用程序特定的值或系统范围的值。在不支持的后端上,这将降低为常量 0。
请注意,运行时支持可能取决于代码运行的特权级别和主机平台。
‘llvm.clear_cache
’ 内部函数¶
语法:¶
declare void @llvm.clear_cache(ptr, ptr)
概述:¶
‘llvm.clear_cache
’ 内部函数确保指定范围内修改对处理器执行单元的可见性。在具有非统一指令和数据缓存的目标上,实现会刷新指令缓存。
语义:¶
在具有一致指令和数据缓存的平台(例如 x86)上,此内部函数是空操作。在具有非一致指令和数据缓存的平台(例如 ARM、MIPS)上,如果缓存刷新需要特殊权限,则内部函数会降低为适当的指令或系统调用。
默认行为是发出对运行时库中的 __clear_cache
的调用。
此内部函数不清空指令流水线。当前函数的修改超出了内部函数的范围。
‘llvm.instrprof.increment
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.increment(ptr <name>, i64 <hash>,
i32 <num-counters>, i32 <index>)
概述:¶
‘llvm.instrprof.increment
’ 内部函数可以由前端发出,以用于基于插桩的性能分析。这些将由 -instrprof
传递降低,以生成程序在运行时的执行计数。
参数:¶
第一个参数是指向全局变量的指针,该全局变量包含正在检测的实体的名称。对于一组计数器,这通常应该是(经过名称修饰的)函数名称。
第二个参数是一个哈希值,配置文件数据的使用者可以使用该哈希值来检测对检测源的更改,第三个参数是与 name
关联的计数器数量。如果引用同一名称的 instrprof.increment
的两个实例之间的 hash
或 num-counters
不同,则会发生错误。
最后一个参数指的是应递增 name
的哪个计数器。它应该是一个介于 0 和 num-counters
之间的值。
语义:¶
此内部函数表示性能分析计数器的递增。它将导致 -instrprof
传递生成适当的数据结构和代码,以递增适当的值,格式可以通过编译器运行时写出,并通过 llvm-profdata
工具使用。
内部函数通过 -ctx-instr-lower
传递针对上下文性能分析以不同的方式降低。这里
入口基本块递增计数器被降低为对 compiler-rt 的调用,可以是
__llvm_ctx_profile_start_context
或__llvm_ctx_profile_get_context
。两者都返回指向上下文对象的指针,该上下文对象包含一个缓冲区,计数器递增可以在其中发生。请注意,compiler-rt 返回的指针值可能设置了 LSB - 计数器递增发生在地址偏移处,LSB 已清除。所有其他
llvm.instrprof.increment[.step]
的降低都发生在上下文中。上下文被假定为函数的局部值,LLVM 不需要处理任何并发问题。
‘llvm.instrprof.increment.step
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.increment.step(ptr <name>, i64 <hash>,
i32 <num-counters>,
i32 <index>, i64 <step>)
概述:¶
‘llvm.instrprof.increment.step
’ 内部函数是 ‘llvm.instrprof.increment
’ 内部函数的扩展,具有额外的第五个参数来指定递增的步长。
参数:¶
前四个参数与 ‘llvm.instrprof.increment
’ 内部函数相同。
最后一个参数指定计数器变量的增量值。
语义:¶
请参阅 ‘llvm.instrprof.increment
’ 内部函数的描述。
‘llvm.instrprof.callsite
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.callsite(ptr <name>, i64 <hash>,
i32 <num-counters>,
i32 <index>, ptr <callsite>)
概述:¶
‘llvm.instrprof.callsite
’ 内部函数应在调用点之前发出,该调用点不是针对“伪”被调用者(如另一个内部函数或 asm)。它由上下文性能分析使用,并且具有副作用。它的降低发生在 IR 中,目标特定的后端永远不应遇到它。
参数:¶
前 4 个参数类似于 llvm.instrprof.increment
。索引特定于调用点,这意味着调用点从 0 开始索引,独立于其他内部函数(例如 llvm.instrprof.increment[.step]
)使用的索引。
最后一个参数是此内部函数之前的调用点的被调用值。
语义:¶
这通过上下文性能分析降低。在上下文性能分析中,函数从 compiler-rt 获取指向上下文对象的指针。上下文对象由 LLVM 可以用来执行计数器递增的缓冲区组成(即 llvm.instrprof.increment[.step]
的降低)。计数器缓冲区后面的地址范围,大小为 <num-counters>
x sizeof(ptr)
,预计包含从此函数调用的函数的上下文指针(“子上下文”)。LLVM 不会取消引用到该内存区域,只是计算 GEP。
llvm.instrprof.callsite
的降低包括
写入
__llvm_ctx_profile_expected_callee
的<callsite>
值;写入
__llvm_ctx_profile_callsite
的地址到此函数的上下文中,该地址是子上下文区域中<index>
位置的地址。
__llvm_ctx_profile_{expected_callee|callsite}
由 compiler-rt 初始化,并且是 TLS。它们都是大小为 2 的指针向量。每个向量的索引在当前函数从 compiler-rt 获取指向其上下文的指针时确定。指针的 LSB 给出索引。
‘llvm.instrprof.timestamp
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.timestamp(i8* <name>, i64 <hash>,
i32 <num-counters>, i32 <index>)
概述:¶
‘llvm.instrprof.timestamp
’ 内部函数用于实现时间性能分析。
参数:¶
参数与 ‘llvm.instrprof.increment
’ 相同。index
预计始终为零。
语义:¶
类似于 ‘llvm.instrprof.increment
’ 内部函数,但它存储一个时间戳,表示首次执行此函数的时间。
‘llvm.instrprof.cover
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.cover(ptr <name>, i64 <hash>,
i32 <num-counters>, i32 <index>)
概述:¶
‘llvm.instrprof.cover
’ 内部函数用于实现覆盖率检测。
参数:¶
参数与 ‘llvm.instrprof.increment
’ 的前四个参数相同。
语义:¶
类似于 ‘llvm.instrprof.increment
’ 内部函数,但它将零存储到性能分析变量,以表示该函数已被覆盖。我们存储零是因为这在某些目标上更有效。
‘llvm.instrprof.value.profile
’ 内部函数¶
语法:¶
declare void @llvm.instrprof.value.profile(ptr <name>, i64 <hash>,
i64 <value>, i32 <value_kind>,
i32 <index>)
概述:¶
‘llvm.instrprof.value.profile
’ 内部函数可以由前端发出,以用于基于插桩的性能分析。这将由 -instrprof
传递降低,以找出目标值,检测表达式在程序运行时获取。
参数:¶
第一个参数是指向全局变量的指针,该变量包含被检测实体的名称。对于一组计数器,name
通常应该是(经过名称修饰的)函数名。
第二个参数是一个哈希值,profile 数据的消费者可以使用它来检测被检测源的更改。如果引用相同名称的两个 llvm.instrprof.*
实例之间的 hash
不同,则会发生错误。
第三个参数是被 profile 表达式的值。被 profile 表达式的值应可表示为 64 位无符号值。第四个参数表示正在执行的值 profile 的类型。支持的值 profile 类型通过在 <include/llvm/ProfileData/InstrProf.h>
头文件中声明的 InstrProfValueKind
类型进行枚举。最后一个参数是 name
中被检测表达式的索引。它应该 >= 0。
语义:¶
此 intrinsic 表示应插入对运行时例程调用的点,以用于目标表达式的值 profile。-instrprof
pass 将生成适当的数据结构,并将 llvm.instrprof.value.profile
intrinsic 替换为对带有正确参数的 profile 运行时库的调用。
‘llvm.instrprof.mcdc.parameters
’ Intrinsic¶
语法:¶
declare void @llvm.instrprof.mcdc.parameters(ptr <name>, i64 <hash>,
i32 <bitmap-bits>)
概述:¶
‘llvm.instrprof.mcdc.parameters
’ intrinsic 用于启动函数的 MC/DC 代码覆盖率检测。
参数:¶
第一个参数是指向全局变量的指针,该全局变量包含正在检测的实体的名称。对于一组计数器,这通常应该是(经过名称修饰的)函数名称。
第二个参数是一个哈希值,profile 数据的消费者可以使用它来检测被检测源的更改。
第三个参数是函数记录每个布尔表达式执行的测试向量数量所需的位图位数。
语义:¶
此 intrinsic 表示基本的 MC/DC 参数,用于启动函数中的一个或多个 MC/DC 检测序列。它将导致 -instrprof
pass 生成适当的数据结构和代码,以检测 MC/DC 测试向量,格式可以通过编译器运行时写出,并通过 llvm-profdata
工具使用。
‘llvm.instrprof.mcdc.tvbitmap.update
’ Intrinsic¶
语法:¶
declare void @llvm.instrprof.mcdc.tvbitmap.update(ptr <name>, i64 <hash>,
i32 <bitmap-index>,
ptr <mcdc-temp-addr>)
概述:¶
‘llvm.instrprof.mcdc.tvbitmap.update
’ intrinsic 用于在每个布尔表达式完全执行后跟踪 MC/DC 测试向量的执行。条件位图的整体值(在用每个条件的真或假评估连续更新后)唯一地标识已执行的 MC/DC 测试向量,并用作全局测试向量位图的位索引。
参数:¶
第一个参数是指向全局变量的指针,该全局变量包含正在检测的实体的名称。对于一组计数器,这通常应该是(经过名称修饰的)函数名称。
第二个参数是一个哈希值,profile 数据的消费者可以使用它来检测被检测源的更改。
第三个参数是函数对应的全局测试向量位图的位索引。
第四个参数是条件位图的地址,其中包含表示已执行 MC/DC 测试向量的值。它被加载并用作测试向量位图的位索引。
语义:¶
此 intrinsic 表示 MC/DC 检测序列的最终操作,并将导致 -instrprof
pass 生成代码,以检测函数全局测试向量位图的更新,以指示已执行测试向量。全局测试向量位图可以被 llvm-profdata
和 llvm-cov
工具使用。
‘llvm.thread.pointer
’ Intrinsic¶
语法:¶
declare ptr @llvm.thread.pointer()
概述:¶
‘llvm.thread.pointer
’ intrinsic 返回线程指针的值。
语义:¶
‘llvm.thread.pointer
’ intrinsic 返回指向当前线程 TLS 区域的指针。此值的确切语义是目标特定的:它可能指向 TLS 区域的开始、结束或中间的某个位置。根据目标的不同,此 intrinsic 可能读取寄存器、调用辅助函数、从备用内存空间读取或执行定位 TLS 区域所需的其他操作。并非所有目标都支持此 intrinsic。
‘llvm.call.preallocated.setup
’ Intrinsic¶
语法:¶
declare token @llvm.call.preallocated.setup(i32 %num_args)
概述:¶
‘llvm.call.preallocated.setup
’ intrinsic 返回一个 token,该 token 可以与调用的 "preallocated"
操作数 bundle 一起使用,以指示某些参数在调用之前已分配和初始化。
语义:¶
‘llvm.call.preallocated.setup
’ intrinsic 返回一个 token,该 token 最多与一个调用关联。该 token 可以传递给 ‘@llvm.call.preallocated.arg
’ 以获取指向相应参数的指针。该 token 必须是相应调用的 "preallocated"
操作数 bundle 的参数。
允许嵌套调用 ‘llvm.call.preallocated.setup
’,但必须正确嵌套。例如:
:: code-block:: llvm
%t1 = call token @llvm.call.preallocated.setup(i32 0) %t2 = call token @llvm.call.preallocated.setup(i32 0) call void foo() [“preallocated”(token %t2)] call void foo() [“preallocated”(token %t1)]
是允许的,但不允许
:: code-block:: llvm
%t1 = call token @llvm.call.preallocated.setup(i32 0) %t2 = call token @llvm.call.preallocated.setup(i32 0) call void foo() [“preallocated”(token %t1)] call void foo() [“preallocated”(token %t2)]
‘llvm.call.preallocated.arg
’ Intrinsic¶
语法:¶
declare ptr @llvm.call.preallocated.arg(token %setup_token, i32 %arg_index)
概述:¶
‘llvm.call.preallocated.arg
’ intrinsic 返回指向预分配调用的相应预分配参数的指针。
语义:¶
‘llvm.call.preallocated.arg
’ intrinsic 返回指向带有 %arg_index
预分配属性的第 ``%arg_index``th argument with the ``preallocated
参数的指针,该参数与 %setup_token
关联的调用,%setup_token
必须来自 ‘llvm.call.preallocated.setup
’。
对 ‘llvm.call.preallocated.arg
’ 的调用必须具有调用站点 preallocated
属性。preallocated
属性的类型必须与预分配调用中相应参数的 preallocated
属性使用的类型匹配。该类型用于 llvm.call.preallocated.setup
没有相应调用的情况(例如,由于 DCE),否则我们无法知道参数有多大。
如果使用来自 ‘llvm.call.preallocated.setup
’ 的 token 调用此函数,而另一个 ‘llvm.call.preallocated.setup
’ 已被调用,或者如果与 ‘llvm.call.preallocated.setup
’ 对应的预分配调用已被调用,则行为未定义。
‘llvm.call.preallocated.teardown
’ Intrinsic¶
语法:¶
declare ptr @llvm.call.preallocated.teardown(token %setup_token)
概述:¶
‘llvm.call.preallocated.teardown
’ intrinsic 清理由 ‘llvm.call.preallocated.setup
’ 创建的堆栈。
语义:¶
token 参数必须是 ‘llvm.call.preallocated.setup
’。
‘llvm.call.preallocated.teardown
’ intrinsic 清理由相应的 ‘llvm.call.preallocated.setup
’ 分配的堆栈。必须调用此 intrinsic 或预分配调用中的恰好一个,以防止堆栈泄漏。对于给定的 ‘llvm.call.preallocated.setup
’,同时调用 ‘llvm.call.preallocated.teardown
’ 和预分配调用是未定义的行为。
例如,如果堆栈是由 ‘llvm.call.preallocated.setup
’ 为预分配调用分配的,那么在分配的参数上调用的初始化函数抛出异常,则异常处理程序中应有一个 ‘llvm.call.preallocated.teardown
’ 以防止堆栈泄漏。
遵循 ‘llvm.call.preallocated.setup
’ 中的嵌套规则,允许嵌套调用 ‘llvm.call.preallocated.setup
’ 和 ‘llvm.call.preallocated.teardown
’,但必须正确嵌套。
示例:¶
%cs = call token @llvm.call.preallocated.setup(i32 1)
%x = call ptr @llvm.call.preallocated.arg(token %cs, i32 0) preallocated(i32)
invoke void @constructor(ptr %x) to label %conta unwind label %contb
conta:
call void @foo1(ptr preallocated(i32) %x) ["preallocated"(token %cs)]
ret void
contb:
%s = catchswitch within none [label %catch] unwind to caller
catch:
%p = catchpad within %s []
call void @llvm.call.preallocated.teardown(token %cs)
ret void
标准 C/C++ 库 Intrinsics¶
LLVM 为一些重要的标准 C/C++ 库函数提供了 intrinsics。这些 intrinsics 允许源语言前端传递关于指针参数对齐的信息给代码生成器,从而为更高效的代码生成提供机会。
‘llvm.abs.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 llvm.abs
。
declare i32 @llvm.abs.i32(i32 <src>, i1 <is_int_min_poison>)
declare <4 x i32> @llvm.abs.v4i32(<4 x i32> <src>, i1 <is_int_min_poison>)
概述:¶
‘llvm.abs
’ intrinsic 函数族返回参数的绝对值。
参数:¶
第一个参数是要返回绝对值的值。此参数可以是任何整数类型或具有整数元素类型的向量。返回类型必须与第一个参数类型匹配。
第二个参数必须是常量,并且是一个标志,指示如果第一个参数静态或动态地是 INT_MIN
值,则 ‘llvm.abs
’ intrinsic 的结果值是否为 poison value。
语义:¶
‘llvm.abs
’ intrinsic 返回第一个参数或向量参数的每个元素的幅度(始终为正)。如果第一个参数是 INT_MIN
,则如果 is_int_min_poison == 0
,结果也是 INT_MIN
,否则为 poison
。
‘llvm.smax.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.smax
。
declare i32 @llvm.smax.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.smax.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
返回 %a
和 %b
中较大的那个,将值作为有符号整数进行比较。向量 intrinsic 在逐元素的基础上操作。在给定索引处,%a
和 %b
的较大元素将为该索引返回。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须与参数类型匹配。
‘llvm.smin.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.smin
。
declare i32 @llvm.smin.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.smin.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
返回 %a
和 %b
中较小的那个,将值作为有符号整数进行比较。向量 intrinsic 在逐元素的基础上操作。在给定索引处,%a
和 %b
的较小元素将为该索引返回。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须与参数类型匹配。
‘llvm.umax.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.umax
。
declare i32 @llvm.umax.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.umax.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
返回 %a
和 %b
中较大的那个,将值作为无符号整数进行比较。向量 intrinsic 在逐元素的基础上操作。在给定索引处,%a
和 %b
的较大元素将为该索引返回。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须与参数类型匹配。
‘llvm.umin.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.umin
。
declare i32 @llvm.umin.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.umin.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
返回 %a
和 %b
中较小的那个,将值作为无符号整数进行比较。向量 intrinsic 在逐元素的基础上操作。在给定索引处,%a
和 %b
的较小元素将为该索引返回。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须与参数类型匹配。
‘llvm.scmp.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.scmp
。
declare i2 @llvm.scmp.i2.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.scmp.v4i32.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
如果 %a
有符号小于 %b
,则返回 -1
;如果它们相等,则返回 0
;如果 %a
有符号大于 %b
,则返回 1
。向量 intrinsic 在逐元素的基础上操作。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须至少与 i2
一样宽,才能容纳三个可能的返回值。
‘llvm.ucmp.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽或任何整数元素向量上使用 @llvm.ucmp
。
declare i2 @llvm.ucmp.i2.i32(i32 %a, i32 %b)
declare <4 x i32> @llvm.ucmp.v4i32.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
如果 %a
无符号小于 %b
,则返回 -1
;如果它们相等,则返回 0
;如果 %a
无符号大于 %b
,则返回 1
。向量 intrinsic 在逐元素的基础上操作。
参数:¶
参数(%a
和 %b
)可以是任何整数类型或具有整数元素类型的向量。参数类型必须相互匹配,并且返回类型必须至少与 i2
一样宽,才能容纳三个可能的返回值。
‘llvm.memcpy
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽和不同的地址空间上使用 llvm.memcpy
。但是,并非所有目标都支持所有位宽。
declare void @llvm.memcpy.p0.p0.i32(ptr <dest>, ptr <src>,
i32 <len>, i1 <isvolatile>)
declare void @llvm.memcpy.p0.p0.i64(ptr <dest>, ptr <src>,
i64 <len>, i1 <isvolatile>)
概述:¶
‘llvm.memcpy.*
’ intrinsics 将内存块从源位置复制到目标位置。
请注意,与标准 libc 函数不同,llvm.memcpy.*
intrinsics 不返回值,采用额外的 isvolatile 参数,并且指针可以在指定的地址空间中。
参数:¶
第一个参数是指向目标的指针,第二个参数是指向源的指针。第三个参数是指定要复制的字节数的整数参数,第四个参数是指示易失性访问的布尔值。
可以为第一个和第二个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.memcpy
调用是 volatile operation。详细的访问行为没有非常清晰地指定,不建议依赖它。
语义:¶
‘llvm.memcpy.*
’ intrinsics 将内存块从源位置复制到目标位置,目标位置必须与源位置相等或不重叠。它复制 “len” 字节的内存。如果已知参数与某个边界对齐,则可以将其指定为参数的属性。
如果 <len>
为 0,则它是空操作,模数附加到参数的属性的行为。如果 <len>
不是定义明确的值,则行为未定义。如果 <len>
不为零,则 <dest>
和 <src>
都应该是定义明确的,否则行为未定义。
‘llvm.memcpy.inline
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽和不同的地址空间上使用 llvm.memcpy.inline
。但是,并非所有目标都支持所有位宽。
declare void @llvm.memcpy.inline.p0.p0.i32(ptr <dest>, ptr <src>,
i32 <len>, i1 <isvolatile>)
declare void @llvm.memcpy.inline.p0.p0.i64(ptr <dest>, ptr <src>,
i64 <len>, i1 <isvolatile>)
概述:¶
‘llvm.memcpy.inline.*
’ intrinsics 将内存块从源位置复制到目标位置,并保证不调用外部函数。
请注意,与标准 libc 函数不同,llvm.memcpy.inline.*
intrinsics 不返回值,采用额外的 isvolatile 参数,并且指针可以在指定的地址空间中。
参数:¶
第一个参数是指向目标的指针,第二个参数是指向源的指针。第三个参数是指定要复制的字节数的整数参数,第四个参数是指示易失性访问的布尔值。
可以为第一个和第二个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.memcpy.inline
调用是 volatile operation。详细的访问行为没有非常清晰地指定,不建议依赖它。
语义:¶
‘llvm.memcpy.inline.*
’ intrinsics 将内存块从源位置复制到目标位置,不允许它们重叠。它复制 “len” 字节的内存。如果已知参数与某个边界对齐,则可以将其指定为参数的属性。‘llvm.memcpy.inline.*
’ 的行为等效于 ‘llvm.memcpy.*
’ 的行为,但生成的代码保证不调用任何外部函数。
‘llvm.memmove
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽和不同的地址空间上使用 llvm.memmove。但是,并非所有目标都支持所有位宽。
declare void @llvm.memmove.p0.p0.i32(ptr <dest>, ptr <src>,
i32 <len>, i1 <isvolatile>)
declare void @llvm.memmove.p0.p0.i64(ptr <dest>, ptr <src>,
i64 <len>, i1 <isvolatile>)
概述:¶
‘llvm.memmove.*
’ intrinsics 将内存块从源位置移动到目标位置。它类似于 ‘llvm.memcpy
’ intrinsic,但允许两个内存位置重叠。
请注意,与标准 libc 函数不同,llvm.memmove.*
intrinsics 不返回值,采用额外的 isvolatile 参数,并且指针可以在指定的地址空间中。
参数:¶
第一个参数是指向目标的指针,第二个参数是指向源的指针。第三个参数是指定要复制的字节数的整数参数,第四个参数是指示易失性访问的布尔值。
可以为第一个和第二个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.memmove
调用是 volatile operation。详细的访问行为没有非常清晰地指定,不建议依赖它。
语义:¶
‘llvm.memmove.*
’ intrinsics 将内存块从源位置复制到目标位置,它们可能重叠。它复制 “len” 字节的内存。如果已知参数与某个边界对齐,则可以将其指定为参数的属性。
如果 <len>
为 0,则它是空操作,模数附加到参数的属性的行为。如果 <len>
不是定义明确的值,则行为未定义。如果 <len>
不为零,则 <dest>
和 <src>
都应该是定义明确的,否则行为未定义。
‘llvm.memset.*
’ Intrinsics¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽和不同的地址空间上使用 llvm.memset。但是,并非所有目标都支持所有位宽。
declare void @llvm.memset.p0.i32(ptr <dest>, i8 <val>,
i32 <len>, i1 <isvolatile>)
declare void @llvm.memset.p0.i64(ptr <dest>, i8 <val>,
i64 <len>, i1 <isvolatile>)
概述:¶
‘llvm.memset.*
’ intrinsics 用特定的字节值填充内存块。
请注意,与标准 libc 函数不同,llvm.memset
intrinsic 不返回值,并且采用额外的 volatile 参数。此外,目标可以在任意地址空间中。
参数:¶
第一个参数是指向要填充的目标的指针,第二个参数是要填充的字节值,第三个参数是指定要填充的字节数的整数参数,第四个参数是指示易失性访问的布尔值。
可以为第一个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.memset
调用是 volatile operation。详细的访问行为没有非常清晰地指定,不建议依赖它。
语义:¶
‘llvm.memset.*
’ intrinsics 从目标位置开始填充 “len” 字节的内存。如果已知参数与某个边界对齐,则可以将其指定为参数的属性。
如果 <len>
为 0,则它是空操作,模数附加到参数的属性的行为。如果 <len>
不是定义明确的值,则行为未定义。如果 <len>
不为零,则 <dest>
应该是定义明确的,否则行为未定义。
‘llvm.memset.inline
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。您可以在任何整数位宽和不同的地址空间上使用 llvm.memset.inline
。但是,并非所有目标都支持所有位宽。
declare void @llvm.memset.inline.p0.p0i8.i32(ptr <dest>, i8 <val>,
i32 <len>, i1 <isvolatile>)
declare void @llvm.memset.inline.p0.p0.i64(ptr <dest>, i8 <val>,
i64 <len>, i1 <isvolatile>)
概述:¶
‘llvm.memset.inline.*
’ intrinsics 用特定的字节值填充内存块,并保证不调用外部函数。
请注意,与标准 libc 函数不同,llvm.memset.inline.*
intrinsics 不返回值,采用额外的 isvolatile 参数,并且指针可以在指定的地址空间中。
参数:¶
第一个参数是指向要填充的目标位置的指针,第二个参数是要填充的字节值,第三个参数是指定要填充的字节数的常量整数参数,第四个参数是表示易失性访问的布尔值。
可以为第一个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.memset.inline
调用是 volatile operation(易失性操作)。详细的访问行为没有非常清晰地指定,因此依赖它是不明智的。
语义:¶
‘llvm.memset.inline.*
’ intrinsic 从目标位置开始填充 “len” 字节的内存。如果已知参数与某个边界对齐,则可以将其指定为参数的属性。
如果 <len>
为 0,则它是空操作,模数附加到参数的属性的行为。如果 <len>
不是定义明确的值,则行为未定义。如果 <len>
不为零,则 <dest>
应该是定义明确的,否则行为未定义。
‘llvm.memset.inline.*
’ 的行为等同于 ‘llvm.memset.*
’ 的行为,但生成的代码保证不会调用任何外部函数。
‘llvm.experimental.memset.pattern
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何整数位宽和不同的地址空间上使用 llvm.experimental.memset.pattern
。 但是,并非所有目标都支持所有位宽。
declare void @llvm.experimental.memset.pattern.p0.i128.i64(ptr <dest>, i128 <val>,
i64 <count>, i1 <isvolatile>)
概述:¶
‘llvm.experimental.memset.pattern.*
’ intrinsic 使用特定值填充内存块。 这可能会扩展为内联循环、一系列存储操作或库调用,具体取决于目标平台的可用功能以及预期的性能和代码大小影响。
参数:¶
第一个参数是指向要填充的目标位置的指针,第二个参数是要填充的值,第三个参数是指定填充值的次数的整数参数,第四个参数是表示易失性访问的布尔值。
可以为第一个参数提供 align 参数属性。
如果 isvolatile
参数为 true
,则 llvm.experimental.memset.pattern
调用是 volatile operation(易失性操作)。详细的访问行为没有非常清晰地指定,因此依赖它是不明智的。
语义:¶
‘llvm.experimental.memset.pattern*
’ intrinsic 从目标位置开始,使用给定的模式填充内存 <count>
次,每次递增类型分配的大小。 存储操作遵循存储指令的常用语义,包括字节序和填充。 如果已知参数与某个边界对齐,则可以将其指定为参数的属性。
如果 <count>
为 0,则它是空操作,但会考虑附加到参数的属性的行为。 如果 <count>
不是明确定义的值,则行为是未定义的。 如果 <count>
不为零,则 <dest>
应该是明确定义的,否则行为是未定义的。
‘llvm.sqrt.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.sqrt
。 但是,并非所有目标都支持所有类型。
declare float @llvm.sqrt.f32(float %Val)
declare double @llvm.sqrt.f64(double %Val)
declare x86_fp80 @llvm.sqrt.f80(x86_fp80 %Val)
declare fp128 @llvm.sqrt.f128(fp128 %Val)
declare ppc_fp128 @llvm.sqrt.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.sqrt
’ intrinsic 返回指定值的平方根。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘sqrt
’ 函数相同,但不发生陷阱或设置 errno
。 对于 IEEE-754 指定的类型,结果与符合标准的 libm 实现相匹配。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.powi.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.powi
。 但是,并非所有目标都支持所有类型。
通常,指数唯一支持的类型是与 C 类型 int
匹配的类型。
declare float @llvm.powi.f32.i32(float %Val, i32 %power)
declare double @llvm.powi.f64.i16(double %Val, i16 %power)
declare x86_fp80 @llvm.powi.f80.i32(x86_fp80 %Val, i32 %power)
declare fp128 @llvm.powi.f128.i32(fp128 %Val, i32 %power)
declare ppc_fp128 @llvm.powi.ppcf128.i32(ppc_fp128 %Val, i32 %power)
概述:¶
‘llvm.powi.*
’ intrinsic 返回第一个操作数的指定(正或负)次方。 乘法运算的求值顺序未定义。 当使用浮点向量类型时,第二个参数仍然是标量整数值。
参数:¶
第二个参数是整数幂,第一个参数是要提升到该幂的值。
语义:¶
此函数返回第一个值提升到第二个幂的结果,其中包含未指定的舍入操作序列。
‘llvm.sin.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.sin
。 但是,并非所有目标都支持所有类型。
declare float @llvm.sin.f32(float %Val)
declare double @llvm.sin.f64(double %Val)
declare x86_fp80 @llvm.sin.f80(x86_fp80 %Val)
declare fp128 @llvm.sin.f128(fp128 %Val)
declare ppc_fp128 @llvm.sin.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.sin.*
’ intrinsic 返回操作数的正弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘sin
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.cos.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.cos
。 但是,并非所有目标都支持所有类型。
declare float @llvm.cos.f32(float %Val)
declare double @llvm.cos.f64(double %Val)
declare x86_fp80 @llvm.cos.f80(x86_fp80 %Val)
declare fp128 @llvm.cos.f128(fp128 %Val)
declare ppc_fp128 @llvm.cos.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.cos.*
’ intrinsic 返回操作数的余弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘cos
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.tan.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.tan
。 但是,并非所有目标都支持所有类型。
declare float @llvm.tan.f32(float %Val)
declare double @llvm.tan.f64(double %Val)
declare x86_fp80 @llvm.tan.f80(x86_fp80 %Val)
declare fp128 @llvm.tan.f128(fp128 %Val)
declare ppc_fp128 @llvm.tan.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.tan.*
’ intrinsic 返回操作数的正切值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘tan
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.asin.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.asin
。 但是,并非所有目标都支持所有类型。
declare float @llvm.asin.f32(float %Val)
declare double @llvm.asin.f64(double %Val)
declare x86_fp80 @llvm.asin.f80(x86_fp80 %Val)
declare fp128 @llvm.asin.f128(fp128 %Val)
declare ppc_fp128 @llvm.asin.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.asin.*
’ intrinsic 返回操作数的反正弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘asin
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.acos.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.acos
。 但是,并非所有目标都支持所有类型。
declare float @llvm.acos.f32(float %Val)
declare double @llvm.acos.f64(double %Val)
declare x86_fp80 @llvm.acos.f80(x86_fp80 %Val)
declare fp128 @llvm.acos.f128(fp128 %Val)
declare ppc_fp128 @llvm.acos.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.acos.*
’ intrinsic 返回操作数的反余弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘acos
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.atan.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.atan
。 但是,并非所有目标都支持所有类型。
declare float @llvm.atan.f32(float %Val)
declare double @llvm.atan.f64(double %Val)
declare x86_fp80 @llvm.atan.f80(x86_fp80 %Val)
declare fp128 @llvm.atan.f128(fp128 %Val)
declare ppc_fp128 @llvm.atan.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.atan.*
’ intrinsic 返回操作数的反正切值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘atan
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.atan2.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.atan2
。 但是,并非所有目标都支持所有类型。
declare float @llvm.atan2.f32(float %Y, float %X)
declare double @llvm.atan2.f64(double %Y, double %X)
declare x86_fp80 @llvm.atan2.f80(x86_fp80 %Y, x86_fp80 %X)
declare fp128 @llvm.atan2.f128(fp128 %Y, fp128 %X)
declare ppc_fp128 @llvm.atan2.ppcf128(ppc_fp128 %Y, ppc_fp128 %X)
概述:¶
‘llvm.atan2.*
’ intrinsic 返回 Y/X
的反正切值,并考虑象限。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘atan2
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.sinh.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.sinh
。 但是,并非所有目标都支持所有类型。
declare float @llvm.sinh.f32(float %Val)
declare double @llvm.sinh.f64(double %Val)
declare x86_fp80 @llvm.sinh.f80(x86_fp80 %Val)
declare fp128 @llvm.sinh.f128(fp128 %Val)
declare ppc_fp128 @llvm.sinh.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.sinh.*
’ intrinsic 返回操作数的双曲正弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘sinh
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.cosh.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.cosh
。 但是,并非所有目标都支持所有类型。
declare float @llvm.cosh.f32(float %Val)
declare double @llvm.cosh.f64(double %Val)
declare x86_fp80 @llvm.cosh.f80(x86_fp80 %Val)
declare fp128 @llvm.cosh.f128(fp128 %Val)
declare ppc_fp128 @llvm.cosh.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.cosh.*
’ intrinsic 返回操作数的双曲余弦值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘cosh
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.tanh.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.tanh
。 但是,并非所有目标都支持所有类型。
declare float @llvm.tanh.f32(float %Val)
declare double @llvm.tanh.f64(double %Val)
declare x86_fp80 @llvm.tanh.f80(x86_fp80 %Val)
declare fp128 @llvm.tanh.f128(fp128 %Val)
declare ppc_fp128 @llvm.tanh.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.tanh.*
’ intrinsic 返回操作数的双曲正切值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘tanh
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.sincos.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.sincos
。 但是,并非所有目标都支持所有类型。
declare { float, float } @llvm.sincos.f32(float %Val)
declare { double, double } @llvm.sincos.f64(double %Val)
declare { x86_fp80, x86_fp80 } @llvm.sincos.f80(x86_fp80 %Val)
declare { fp128, fp128 } @llvm.sincos.f128(fp128 %Val)
declare { ppc_fp128, ppc_fp128 } @llvm.sincos.ppcf128(ppc_fp128 %Val)
declare { <4 x float>, <4 x float> } @llvm.sincos.v4f32(<4 x float> %Val)
概述:¶
‘llvm.sincos.*
’ intrinsic 返回操作数的正弦值和余弦值。
参数:¶
语义:¶
此 intrinsic 等效于对参数同时调用 llvm.sin 和 llvm.cos。
第一个结果是参数的正弦值,第二个结果是参数的余弦值。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.sincospi.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.sincospi
。 但是,并非所有目标都支持所有类型。
declare { float, float } @llvm.sincospi.f32(float %Val)
declare { double, double } @llvm.sincospi.f64(double %Val)
declare { x86_fp80, x86_fp80 } @llvm.sincospi.f80(x86_fp80 %Val)
declare { fp128, fp128 } @llvm.sincospi.f128(fp128 %Val)
declare { ppc_fp128, ppc_fp128 } @llvm.sincospi.ppcf128(ppc_fp128 %Val)
declare { <4 x float>, <4 x float> } @llvm.sincospi.v4f32(<4 x float> %Val)
概述:¶
‘llvm.sincospi.*
’ intrinsic 返回 pi*操作数的正弦值和余弦值。
参数:¶
语义:¶
这等效于 llvm.sincos.*
intrinsic,其中参数已乘以 pi,但是,它计算结果更准确,尤其对于较大的输入值。
注意
目前,此 intrinsic 的默认降级依赖于目标运行库(例如 libc)中提供的 sincospi[f|l]
函数。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.modf.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.modf
。 但是,并非所有目标都支持所有类型。
declare { float, float } @llvm.modf.f32(float %Val)
declare { double, double } @llvm.modf.f64(double %Val)
declare { x86_fp80, x86_fp80 } @llvm.modf.f80(x86_fp80 %Val)
declare { fp128, fp128 } @llvm.modf.f128(fp128 %Val)
declare { ppc_fp128, ppc_fp128 } @llvm.modf.ppcf128(ppc_fp128 %Val)
declare { <4 x float>, <4 x float> } @llvm.modf.v4f32(<4 x float> %Val)
概述:¶
‘llvm.modf.*
’ intrinsic 返回操作数的整数部分和小数部分。
参数:¶
语义:¶
返回值与相应的 libm ‘modf
’ 函数相同,但不发生陷阱或设置 errno
。
第一个结果是操作数的小数部分,第二个结果是操作数的整数部分。 两个结果都与操作数具有相同的符号。
不包括异常输入(如下所列),llvm.modf.*
在语义上等同于
%fp = frem <fptype> %x, 1.0 ; Fractional part
%ip = fsub <fptype> %x, %fp ; Integral part
(假设没有浮点精度误差)
如果参数为零,则为小数部分和整数部分返回一个具有相同符号的零。
如果参数为无穷大,则返回小数部分为零(具有相同符号),整数部分为无穷大(具有相同符号)。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.pow.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.pow
。 但是,并非所有目标都支持所有类型。
declare float @llvm.pow.f32(float %Val, float %Power)
declare double @llvm.pow.f64(double %Val, double %Power)
declare x86_fp80 @llvm.pow.f80(x86_fp80 %Val, x86_fp80 %Power)
declare fp128 @llvm.pow.f128(fp128 %Val, fp128 %Power)
declare ppc_fp128 @llvm.pow.ppcf128(ppc_fp128 %Val, ppc_fp128 Power)
概述:¶
‘llvm.pow.*
’ intrinsic 返回第一个操作数提升到指定(正或负)幂的结果。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘pow
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.exp.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.exp
。 但是,并非所有目标都支持所有类型。
declare float @llvm.exp.f32(float %Val)
declare double @llvm.exp.f64(double %Val)
declare x86_fp80 @llvm.exp.f80(x86_fp80 %Val)
declare fp128 @llvm.exp.f128(fp128 %Val)
declare ppc_fp128 @llvm.exp.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.exp.*
’ intrinsic 计算指定值的自然指数(以 e 为底的指数)。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘exp
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.exp2.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.exp2
。 但是,并非所有目标都支持所有类型。
declare float @llvm.exp2.f32(float %Val)
declare double @llvm.exp2.f64(double %Val)
declare x86_fp80 @llvm.exp2.f80(x86_fp80 %Val)
declare fp128 @llvm.exp2.f128(fp128 %Val)
declare ppc_fp128 @llvm.exp2.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.exp2.*
’ intrinsic 计算指定值的以 2 为底的指数。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘exp2
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.exp10.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.exp10
。 但是,并非所有目标都支持所有类型。
declare float @llvm.exp10.f32(float %Val)
declare double @llvm.exp10.f64(double %Val)
declare x86_fp80 @llvm.exp10.f80(x86_fp80 %Val)
declare fp128 @llvm.exp10.f128(fp128 %Val)
declare ppc_fp128 @llvm.exp10.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.exp10.*
’ intrinsic 计算指定值的以 10 为底的指数。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与相应的 libm ‘exp10
’ 函数相同,但不发生陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.ldexp.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.ldexp
。 但是,并非所有目标都支持所有类型。
declare float @llvm.ldexp.f32.i32(float %Val, i32 %Exp)
declare double @llvm.ldexp.f64.i32(double %Val, i32 %Exp)
declare x86_fp80 @llvm.ldexp.f80.i32(x86_fp80 %Val, i32 %Exp)
declare fp128 @llvm.ldexp.f128.i32(fp128 %Val, i32 %Exp)
declare ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 %Val, i32 %Exp)
declare <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> %Val, <2 x i32> %Exp)
概述:¶
‘llvm.ldexp.*
’ intrinsic 执行 ldexp 函数。
参数:¶
语义:¶
此函数将第一个参数乘以 2 的第二个参数次方。 如果第一个参数是 NaN 或无穷大,则返回相同的值。 如果结果下溢,则返回具有相同符号的零。 如果结果溢出,则结果是具有相同符号的无穷大。
‘llvm.frexp.*
’ Intrinsic¶
语法:¶
这是一个重载的 intrinsic。 您可以在任何浮点或浮点向量类型上使用 llvm.frexp
。 但是,并非所有目标都支持所有类型。
declare { float, i32 } @llvm.frexp.f32.i32(float %Val)
declare { double, i32 } @llvm.frexp.f64.i32(double %Val)
declare { x86_fp80, i32 } @llvm.frexp.f80.i32(x86_fp80 %Val)
declare { fp128, i32 } @llvm.frexp.f128.i32(fp128 %Val)
declare { ppc_fp128, i32 } @llvm.frexp.ppcf128.i32(ppc_fp128 %Val)
declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> %Val)
概述:¶
‘llvm.frexp.*
’ intrinsic 执行 frexp 函数。
参数:¶
参数是 浮点 或 向量 浮点值。 返回结构体中的两个值。 第一个结构体字段与参数类型匹配,第二个字段是整数或与参数具有相同元素数量的整数向量。
语义:¶
此 intrinsic 将浮点值拆分为归一化的分数部分和整数指数部分。
对于非零参数,返回参数乘以 2 的某个幂,使得返回值的绝对值在 [0.5, 1.0) 范围内,并且与参数具有相同的符号。 第二个结果是一个整数,使得第一个结果的第二个结果次方是输入参数。
如果参数为零,则返回具有相同符号的零和 0 指数。
如果参数是 NaN,则返回 NaN,并且返回的指数未指定。
如果参数是无穷大,则返回一个具有相同符号和未指定指数的无穷大。
‘llvm.log.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.log
。但并非所有目标都支持所有类型。
declare float @llvm.log.f32(float %Val)
declare double @llvm.log.f64(double %Val)
declare x86_fp80 @llvm.log.f80(x86_fp80 %Val)
declare fp128 @llvm.log.f128(fp128 %Val)
declare ppc_fp128 @llvm.log.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.log.*
’ 内建函数计算指定值的自然对数(底为 e 的对数)。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回与对应的 libm ‘log
’ 函数相同的值,但不陷入陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.log10.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.log10
。但并非所有目标都支持所有类型。
declare float @llvm.log10.f32(float %Val)
declare double @llvm.log10.f64(double %Val)
declare x86_fp80 @llvm.log10.f80(x86_fp80 %Val)
declare fp128 @llvm.log10.f128(fp128 %Val)
declare ppc_fp128 @llvm.log10.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.log10.*
’ 内建函数计算指定值的常用对数(底为 10 的对数)。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回与对应的 libm ‘log10
’ 函数相同的值,但不陷入陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.log2.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.log2
。但并非所有目标都支持所有类型。
declare float @llvm.log2.f32(float %Val)
declare double @llvm.log2.f64(double %Val)
declare x86_fp80 @llvm.log2.f80(x86_fp80 %Val)
declare fp128 @llvm.log2.f128(fp128 %Val)
declare ppc_fp128 @llvm.log2.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.log2.*
’ 内建函数计算指定值的二进制对数(底为 2 的对数)。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回与对应的 libm ‘log2
’ 函数相同的值,但不陷入陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.fma.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.fma
。但并非所有目标都支持所有类型。
declare float @llvm.fma.f32(float %a, float %b, float %c)
declare double @llvm.fma.f64(double %a, double %b, double %c)
declare x86_fp80 @llvm.fma.f80(x86_fp80 %a, x86_fp80 %b, x86_fp80 %c)
declare fp128 @llvm.fma.f128(fp128 %a, fp128 %b, fp128 %c)
declare ppc_fp128 @llvm.fma.ppcf128(ppc_fp128 %a, ppc_fp128 %b, ppc_fp128 %c)
概述:¶
‘llvm.fma.*
’ 内建函数执行融合乘加运算。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
返回值与 IEEE-754 融合乘加运算相同。假定此操作不会陷入陷阱或设置 errno
。
当使用快速数学标志 ‘afn’ 指定时,结果可以使用不太精确的计算来近似。
‘llvm.fabs.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.fabs
。但并非所有目标都支持所有类型。
declare float @llvm.fabs.f32(float %Val)
declare double @llvm.fabs.f64(double %Val)
declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val)
declare fp128 @llvm.fabs.f128(fp128 %Val)
declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.fabs.*
’ 内建函数返回操作数的绝对值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm fabs
函数相同的值,并以相同方式处理错误情况。除了符号位之外,返回值与输入完全相同;特别是,如果输入是 NaN,则静默/信号位和有效载荷将完美保留。
‘llvm.min.*
’ 内建函数比较¶
标准:¶
IEEE754 和 ISO C 定义了一些 min/max 操作,并且它们在处理 qNaN/sNaN 和 +0.0/-0.0 时存在一些差异。以下是列表
|
fmin/fmax |
fminimum/fmaximum |
fminimum_num/fmaximum_num |
---|---|---|---|
|
minNum/maxNum (2008) |
minimum/maximum (2019) |
minimumNumber/maximumNumber (2019) |
|
任意一个 |
+0.0 > -0.0 |
+0.0 > -0.0 |
|
qNaN,无效异常 |
qNaN,无效异常 |
NUM,无效异常 |
|
qNaN,无效异常 |
qNaN,无效异常 |
qNaN,无效异常 |
|
NUM,无异常 |
qNaN,无异常 |
NUM,无异常 |
LLVM 实现:¶
LLVM 实现了此表中列出的所有 ISO C 类型,但默认情况下浮点环境异常被忽略。内建函数的约束版本遵循异常行为。
操作 |
minnum/maxnum |
minimum/maximum |
minimumnum/maximumnum |
---|---|---|---|
|
NUM,无异常 |
qNaN,无异常 |
NUM,无异常 |
|
qNaN,无效异常 |
qNaN,无效异常 |
NUM,无效异常 |
|
qNaN,无效异常 |
qNaN,无效异常 |
qNaN,无效异常 |
|
qNaN,无效异常 |
qNaN,无效异常 |
qNaN,无效异常 |
|
+0.0(max)/-0.0(min) |
+0.0(max)/-0.0(min) |
+0.0(max)/-0.0(min) |
|
较大值(max)/较小值(min) |
较大值(max)/较小值(min) |
较大值(max)/较小值(min) |
‘llvm.minnum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.minnum
。但并非所有目标都支持所有类型。
declare float @llvm.minnum.f32(float %Val0, float %Val1)
declare double @llvm.minnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.minnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.minnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.minnum.*
’ 内建函数返回两个参数的最小值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
遵循 IEEE-754-2008 中 minNum 的语义,但在此内建函数中,-0.0 < +0.0。 对于信号 NaN,根据 minNum 语义,如果任一操作数是 sNaN,则结果为 qNaN。 这与 libm 函数 fmin
的推荐行为相匹配,尽管并非所有实现都实现了这些推荐行为。
如果任一操作数是 qNaN,则返回另一个非 NaN 操作数。 仅当两个操作数都是 NaN 或任一操作数是 sNaN 时才返回 NaN。 请注意,对 sNaN 的算术运算不会始终产生 qNaN,因此输入到 minnum 的算术运算可能会产生不一致的结果。 例如,minnum(fadd(sNaN, -0.0), 1.0)
可能会产生 qNaN 或 1.0,具体取决于 fadd
是否被折叠。
IEEE-754-2008 定义了 minNum,但在 IEEE-754-2019 中已删除。 作为替代,IEEE-754-2019 定义了 minimumNumber。
如果内建函数标记有 nsz 属性,则效果与 C 和 IEEE-754-2008 中的定义相同:minnum(-0.0, +0.0)
的结果可能是 -0.0 或 +0.0。
某些架构,例如 ARMv8 (FMINNM)、LoongArch (fmin)、MIPSr6 (min.fmt)、PowerPC/VSX (xsmindp),具有完全匹配这些语义的指令;因此,对于这些架构来说非常简单。 某些架构具有类似的指令,但它们并非完全等效。 例如,x86 实现了 MINPS
,它实现了 C 代码 a<b?a:b
的语义:NUM vs qNaN 始终返回 qNaN。 如果给定 nsz
和 nnan
,则可以使用 MINPS
。
对于现有的 libc 实现,即使在单个 libm 实现的同一版本中,fmin 的行为在 sNaN 和有符号零行为方面也可能大相径庭。
‘llvm.maxnum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.maxnum
。但并非所有目标都支持所有类型。
declare float @llvm.maxnum.f32(float %Val0, float %Val1)
declare double @llvm.maxnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.maxnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.maxnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.maxnum.*
’ 内建函数返回两个参数的最大值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
遵循 IEEE-754-2008 中 maxNum 的语义,但在此内建函数中,-0.0 < +0.0。 对于信号 NaN,根据 maxNum 语义,如果任一操作数是 sNaN,则结果为 qNaN。 这与 libm 函数 fmax
的推荐行为相匹配,尽管并非所有实现都实现了这些推荐行为。
如果任一操作数是 qNaN,则返回另一个非 NaN 操作数。 仅当两个操作数都是 NaN 或任一操作数是 sNaN 时才返回 NaN。 请注意,对 sNaN 的算术运算不会始终产生 qNaN,因此输入到 maxnum 的算术运算可能会产生不一致的结果。 例如,maxnum(fadd(sNaN, -0.0), 1.0)
可能会产生 qNaN 或 1.0,具体取决于 fadd
是否被折叠。
IEEE-754-2008 定义了 maxNum,但在 IEEE-754-2019 中已删除。 作为替代,IEEE-754-2019 定义了 maximumNumber。
如果内建函数标记有 nsz 属性,则效果与 C 和 IEEE-754-2008 中的定义相同:maxnum(-0.0, +0.0) 的结果可能是 -0.0 或 +0.0。
某些架构,例如 ARMv8 (FMAXNM)、LoongArch (fmax)、MIPSr6 (max.fmt)、PowerPC/VSX (xsmaxdp),具有完全匹配这些语义的指令;因此,对于这些架构来说非常简单。 某些架构具有类似的指令,但它们并非完全等效。 例如,x86 实现了 MAXPS
,它实现了 C 代码 a>b?a:b
的语义:NUM vs qNaN 始终返回 qNaN。 如果给定 nsz
和 nnan
,则可以使用 MAXPS
。
对于现有的 libc 实现,即使在单个 libm 实现的同一版本中,fmin 的行为在 sNaN 和有符号零行为方面也可能大相径庭。
‘llvm.minimum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.minimum
。但并非所有目标都支持所有类型。
declare float @llvm.minimum.f32(float %Val0, float %Val1)
declare double @llvm.minimum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.minimum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.minimum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.minimum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.minimum.*
’ 内建函数返回两个参数的最小值,传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
如果任一操作数是 NaN,则返回 NaN。 否则,返回两个参数中较小的一个。 对于此内建函数,-0.0 被认为小于 +0.0。 请注意,这些是 IEEE 754-2019 草案中指定的语义。
‘llvm.maximum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.maximum
。但并非所有目标都支持所有类型。
declare float @llvm.maximum.f32(float %Val0, float %Val1)
declare double @llvm.maximum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.maximum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.maximum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.maximum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.maximum.*
’ 内建函数返回两个参数的最大值,传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
如果任一操作数是 NaN,则返回 NaN。 否则,返回两个参数中较大的一个。 对于此内建函数,-0.0 被认为小于 +0.0。 请注意,这些是 IEEE 754-2019 草案中指定的语义。
‘llvm.minimumnum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.minimumnum
。但并非所有目标都支持所有类型。
declare float @llvm.minimumnum.f32(float %Val0, float %Val1)
declare double @llvm.minimumnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.minimumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.minimumnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.minimumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.minimumnum.*
’ 内建函数返回两个参数的最小值,不传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
如果两个操作数都是 NaN(包括 sNaN),则返回 qNaN。 如果一个操作数是 NaN(包括 sNaN)而另一个操作数是数字,则返回该数字。 否则,返回两个参数中较小的一个。 对于此内建函数,-0.0 被认为小于 +0.0。
请注意,这些是 IEEE 754-2019 中指定的 minimumNumber 的语义。
它与 ‘llvm.minnum.*
’ 有一些区别:1) 如果任一操作数是 sNaN,‘llvm.minnum.*
’ 将返回 qNaN。 2) 如果我们比较 +0.0 和 -0.0,‘llvm.minnum*
’ 可能会返回任意一个。
‘llvm.maximumnum.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.maximumnum
。但并非所有目标都支持所有类型。
declare float @llvm.maximumnum.f32(float %Val0, float %Val1)
declare double @llvm.maximumnum.f64(double %Val0, double %Val1)
declare x86_fp80 @llvm.maximumnum.f80(x86_fp80 %Val0, x86_fp80 %Val1)
declare fp128 @llvm.maximumnum.f128(fp128 %Val0, fp128 %Val1)
declare ppc_fp128 @llvm.maximumnum.ppcf128(ppc_fp128 %Val0, ppc_fp128 %Val1)
概述:¶
‘llvm.maximumnum.*
’ 内建函数返回两个参数的最大值,不传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
如果两个操作数都是 NaN(包括 sNaN),则返回 qNaN。 如果一个操作数是 NaN(包括 sNaN)而另一个操作数是数字,则返回该数字。 否则,返回两个参数中较大的一个。 对于此内建函数,-0.0 被认为小于 +0.0。
请注意,这些是 IEEE 754-2019 中指定的 maximumNumber 的语义。
它与 ‘llvm.maxnum.*
’ 有一些区别:1) 如果任一操作数是 sNaN,‘llvm.maxnum.*
’ 将返回 qNaN。 2) 如果我们比较 +0.0 和 -0.0,‘llvm.maxnum*
’ 可能会返回任意一个。
‘llvm.copysign.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.copysign
。但并非所有目标都支持所有类型。
declare float @llvm.copysign.f32(float %Mag, float %Sgn)
declare double @llvm.copysign.f64(double %Mag, double %Sgn)
declare x86_fp80 @llvm.copysign.f80(x86_fp80 %Mag, x86_fp80 %Sgn)
declare fp128 @llvm.copysign.f128(fp128 %Mag, fp128 %Sgn)
declare ppc_fp128 @llvm.copysign.ppcf128(ppc_fp128 %Mag, ppc_fp128 %Sgn)
概述:¶
‘llvm.copysign.*
’ 内建函数返回一个值,该值具有第一个操作数的幅度,以及第二个操作数的符号。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm copysign
函数相同的值,并以相同方式处理错误情况。除了符号位之外,返回值与第一个操作数完全相同;特别是,如果输入是 NaN,则静默/信号位和有效载荷将完美保留。
‘llvm.floor.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.floor
。但并非所有目标都支持所有类型。
declare float @llvm.floor.f32(float %Val)
declare double @llvm.floor.f64(double %Val)
declare x86_fp80 @llvm.floor.f80(x86_fp80 %Val)
declare fp128 @llvm.floor.f128(fp128 %Val)
declare ppc_fp128 @llvm.floor.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.floor.*
’ 内建函数返回操作数的向下取整值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm floor
函数相同的值,并以相同方式处理错误情况。
‘llvm.ceil.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.ceil
。但并非所有目标都支持所有类型。
declare float @llvm.ceil.f32(float %Val)
declare double @llvm.ceil.f64(double %Val)
declare x86_fp80 @llvm.ceil.f80(x86_fp80 %Val)
declare fp128 @llvm.ceil.f128(fp128 %Val)
declare ppc_fp128 @llvm.ceil.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.ceil.*
’ 内建函数返回操作数的向上取整值。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm ceil
函数相同的值,并以相同方式处理错误情况。
‘llvm.trunc.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.trunc
。但并非所有目标都支持所有类型。
declare float @llvm.trunc.f32(float %Val)
declare double @llvm.trunc.f64(double %Val)
declare x86_fp80 @llvm.trunc.f80(x86_fp80 %Val)
declare fp128 @llvm.trunc.f128(fp128 %Val)
declare ppc_fp128 @llvm.trunc.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.trunc.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数,其量值不大于操作数。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm trunc
函数相同的值,并以相同方式处理错误情况。
‘llvm.rint.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.rint
。但并非所有目标都支持所有类型。
declare float @llvm.rint.f32(float %Val)
declare double @llvm.rint.f64(double %Val)
declare x86_fp80 @llvm.rint.f80(x86_fp80 %Val)
declare fp128 @llvm.rint.f128(fp128 %Val)
declare ppc_fp128 @llvm.rint.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.rint.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数。如果操作数不是整数,则可能会引发不精确浮点异常。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm rint
函数相同的值,并以相同方式处理错误情况。由于 LLVM 假定默认浮点环境,因此舍入模式假定设置为“最近”,因此中间情况将舍入到偶数整数。 使用 约束浮点内建函数 以避免该假定。
‘llvm.nearbyint.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.nearbyint
。但并非所有目标都支持所有类型。
declare float @llvm.nearbyint.f32(float %Val)
declare double @llvm.nearbyint.f64(double %Val)
declare x86_fp80 @llvm.nearbyint.f80(x86_fp80 %Val)
declare fp128 @llvm.nearbyint.f128(fp128 %Val)
declare ppc_fp128 @llvm.nearbyint.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.nearbyint.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm nearbyint
函数相同的值,并以相同方式处理错误情况。由于 LLVM 假定默认浮点环境,因此舍入模式假定设置为“最近”,因此中间情况将舍入到偶数整数。 使用 约束浮点内建函数 以避免该假定。
‘llvm.round.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.round
。但并非所有目标都支持所有类型。
declare float @llvm.round.f32(float %Val)
declare double @llvm.round.f64(double %Val)
declare x86_fp80 @llvm.round.f80(x86_fp80 %Val)
declare fp128 @llvm.round.f128(fp128 %Val)
declare ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.round.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数返回与 libm round
函数相同的值,并以相同方式处理错误情况。
‘llvm.roundeven.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点或浮点向量类型上使用 llvm.roundeven
。但并非所有目标都支持所有类型。
declare float @llvm.roundeven.f32(float %Val)
declare double @llvm.roundeven.f64(double %Val)
declare x86_fp80 @llvm.roundeven.f80(x86_fp80 %Val)
declare fp128 @llvm.roundeven.f128(fp128 %Val)
declare ppc_fp128 @llvm.roundeven.ppcf128(ppc_fp128 %Val)
概述:¶
‘llvm.roundeven.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数,采用浮点格式,中间情况舍入到偶数(即,舍入到最接近的偶数整数值)。
参数:¶
参数和返回值是相同类型的浮点数。
语义:¶
此函数实现 IEEE-754 操作 roundToIntegralTiesToEven
。 它的行为也与 C 标准函数 roundeven
相同,包括它忽略舍入模式且不引发浮点异常。
‘llvm.lround.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。你可以在任何浮点类型或浮点向量类型上使用 llvm.lround
。但并非所有目标都支持所有类型。
declare i32 @llvm.lround.i32.f32(float %Val)
declare i32 @llvm.lround.i32.f64(double %Val)
declare i32 @llvm.lround.i32.f80(float %Val)
declare i32 @llvm.lround.i32.f128(double %Val)
declare i32 @llvm.lround.i32.ppcf128(double %Val)
declare i64 @llvm.lround.i64.f32(float %Val)
declare i64 @llvm.lround.i64.f64(double %Val)
declare i64 @llvm.lround.i64.f80(float %Val)
declare i64 @llvm.lround.i64.f128(double %Val)
declare i64 @llvm.lround.i64.ppcf128(double %Val)
概述:¶
‘llvm.lround.*
’ 内建函数返回操作数,该操作数被舍入为最接近的整数,平局情况远离零舍入。
参数:¶
参数是浮点数,返回值是整数类型。
语义:¶
此函数返回与 libm lround
函数相同的值,但不设置 errno。如果舍入值太大而无法存储在结果类型中,则返回值是不确定值(等同于 freeze poison)。
‘llvm.llround.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何浮点类型上使用 llvm.llround
。但是,并非所有目标都支持所有类型。
declare i64 @llvm.llround.i64.f32(float %Val)
declare i64 @llvm.llround.i64.f64(double %Val)
declare i64 @llvm.llround.i64.f80(float %Val)
declare i64 @llvm.llround.i64.f128(double %Val)
declare i64 @llvm.llround.i64.ppcf128(double %Val)
概述:¶
‘llvm.llround.*
’ 内建函数返回操作数四舍五入到最接近的整数,与零的距离相等时远离零。
参数:¶
参数是浮点数,返回值是整数类型。
语义:¶
此函数返回与 libm llround
函数相同的值,但不设置 errno。如果舍入值太大而无法存储在结果类型中,则返回值是不确定值(等同于 freeze poison)。
‘llvm.lrint.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何浮点类型或浮点类型向量上使用 llvm.lrint
。但是,并非所有目标都支持所有类型。
declare i32 @llvm.lrint.i32.f32(float %Val)
declare i32 @llvm.lrint.i32.f64(double %Val)
declare i32 @llvm.lrint.i32.f80(float %Val)
declare i32 @llvm.lrint.i32.f128(double %Val)
declare i32 @llvm.lrint.i32.ppcf128(double %Val)
declare i64 @llvm.lrint.i64.f32(float %Val)
declare i64 @llvm.lrint.i64.f64(double %Val)
declare i64 @llvm.lrint.i64.f80(float %Val)
declare i64 @llvm.lrint.i64.f128(double %Val)
declare i64 @llvm.lrint.i64.ppcf128(double %Val)
概述:¶
‘llvm.lrint.*
’ 内建函数返回操作数四舍五入到最接近的整数。
参数:¶
参数是浮点数,返回值是整数类型。
语义:¶
此函数返回与 libm lrint
函数相同的值,但不设置 errno。如果舍入值太大而无法存储在结果类型中,则返回值是不确定值(等同于 freeze poison)。
‘llvm.llrint.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何浮点类型或浮点类型向量上使用 llvm.llrint
。但是,并非所有目标都支持所有类型。
declare i64 @llvm.llrint.i64.f32(float %Val)
declare i64 @llvm.llrint.i64.f64(double %Val)
declare i64 @llvm.llrint.i64.f80(float %Val)
declare i64 @llvm.llrint.i64.f128(double %Val)
declare i64 @llvm.llrint.i64.ppcf128(double %Val)
概述:¶
‘llvm.llrint.*
’ 内建函数返回操作数四舍五入到最接近的整数。
参数:¶
参数是浮点数,返回值是整数类型。
语义:¶
此函数返回与 libm llrint
函数相同的值,但不设置 errno。如果舍入值太大而无法存储在结果类型中,则返回值是不确定值(等同于 freeze poison)。
位操作内建函数¶
LLVM 为一些重要的位操作提供了内建函数。这些内建函数允许为某些算法生成高效的代码。
‘llvm.bitreverse.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数类型上使用 bitreverse。
declare i16 @llvm.bitreverse.i16(i16 <id>)
declare i32 @llvm.bitreverse.i32(i32 <id>)
declare i64 @llvm.bitreverse.i64(i64 <id>)
declare <4 x i32> @llvm.bitreverse.v4i32(<4 x i32> <id>)
概述:¶
‘llvm.bitreverse
’ 系列内建函数用于反转整数值或整数值向量的位模式;例如 0b10110110
变为 0b01101101
。
语义:¶
llvm.bitreverse.iN
内建函数返回一个 iN 值,该值的输入中的位 M
移动到输出中的位 N-M-1
。向量内建函数(例如 llvm.bitreverse.v4i32
)在每个元素的基础上运行,并且元素顺序不受影响。
‘llvm.bswap.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在字节数为偶数的任何整数类型(即 BitWidth % 16 == 0)上使用 bswap。
declare i16 @llvm.bswap.i16(i16 <id>)
declare i32 @llvm.bswap.i32(i32 <id>)
declare i64 @llvm.bswap.i64(i64 <id>)
declare <4 x i32> @llvm.bswap.v4i32(<4 x i32> <id>)
概述:¶
‘llvm.bswap
’ 系列内建函数用于字节交换字节数为偶数(16 位的正倍数)的整数值或整数值向量。
语义:¶
llvm.bswap.i16
内建函数返回一个 i16 值,该值交换了输入 i16 的高字节和低字节。同样,llvm.bswap.i32
内建函数返回一个 i32 值,该值交换了输入 i32 的四个字节,因此如果输入字节编号为 0、1、2、3,则返回的 i32 的字节顺序为 3、2、1、0。llvm.bswap.i48
、llvm.bswap.i64
和其他内建函数将此概念扩展到其他偶数字节长度(分别为 6 字节、8 字节和更多字节)。向量内建函数(例如 llvm.bswap.v4i32
)在每个元素的基础上运行,并且元素顺序不受影响。
‘llvm.ctpop.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或任何具有整数元素的向量上使用 llvm.ctpop。但是,并非所有目标都支持所有位宽或向量类型。
declare i8 @llvm.ctpop.i8(i8 <src>)
declare i16 @llvm.ctpop.i16(i16 <src>)
declare i32 @llvm.ctpop.i32(i32 <src>)
declare i64 @llvm.ctpop.i64(i64 <src>)
declare i256 @llvm.ctpop.i256(i256 <src>)
declare <2 x i32> @llvm.ctpop.v2i32(<2 x i32> <src>)
概述:¶
‘llvm.ctpop
’ 系列内建函数计算值中设置的位数。
参数:¶
唯一的参数是要计数的数值。参数可以是任何整数类型,也可以是具有整数元素的向量。返回类型必须与参数类型匹配。
语义:¶
‘llvm.ctpop
’ 内建函数计算变量中或向量的每个元素中的 1 的数量。
‘llvm.ctlz.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或任何元素为整数的向量上使用 llvm.ctlz
。但是,并非所有目标都支持所有位宽或向量类型。
declare i8 @llvm.ctlz.i8 (i8 <src>, i1 <is_zero_poison>)
declare <2 x i37> @llvm.ctlz.v2i37(<2 x i37> <src>, i1 <is_zero_poison>)
概述:¶
‘llvm.ctlz
’ 系列内建函数计算变量中前导零的数量。
参数:¶
第一个参数是要计数的数值。此参数可以是任何整数类型,也可以是具有整数元素类型的向量。返回类型必须与第一个参数类型匹配。
第二个参数是一个常量标志,指示如果第一个参数为零,内建函数是否返回有效结果。如果第一个参数为零且第二个参数为 true,则结果为 poison。从历史上看,某些架构没有为零值提供定义的有效结果,并且许多算法现在都基于避免零值输入。
语义:¶
‘llvm.ctlz
’ 内建函数计算变量中或向量的每个元素中的前导(最高有效位)零的数量。如果 src == 0
,则如果 is_zero_poison == 0
,则结果是 src
类型的大小(以位为单位),否则为 poison
。例如,llvm.ctlz(i32 2) = 30
。
‘llvm.cttz.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或任何整数元素向量上使用 llvm.cttz
。但是,并非所有目标都支持所有位宽或向量类型。
declare i42 @llvm.cttz.i42 (i42 <src>, i1 <is_zero_poison>)
declare <2 x i32> @llvm.cttz.v2i32(<2 x i32> <src>, i1 <is_zero_poison>)
概述:¶
‘llvm.cttz
’ 系列内建函数计算尾随零的数量。
参数:¶
第一个参数是要计数的数值。此参数可以是任何整数类型,也可以是具有整数元素类型的向量。返回类型必须与第一个参数类型匹配。
第二个参数是一个常量标志,指示如果第一个参数为零,内建函数是否返回有效结果。如果第一个参数为零且第二个参数为 true,则结果为 poison。从历史上看,某些架构没有为零值提供定义的有效结果,并且许多算法现在都基于避免零值输入。
语义:¶
‘llvm.cttz
’ 内建函数计算变量中或向量的每个元素中的尾随(最低有效位)零的数量。如果 src == 0
,则如果 is_zero_poison == 0
,则结果是 src
类型的大小(以位为单位),否则为 poison
。例如,llvm.cttz(2) = 1
。
‘llvm.fshl.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或任何整数元素向量上使用 llvm.fshl
。但是,并非所有目标都支持所有位宽或向量类型。
declare i8 @llvm.fshl.i8 (i8 %a, i8 %b, i8 %c)
declare i64 @llvm.fshl.i64(i64 %a, i64 %b, i64 %c)
declare <2 x i32> @llvm.fshl.v2i32(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c)
概述:¶
‘llvm.fshl
’ 系列内建函数执行漏斗左移:前两个值连接为 { %a : %b }(%a 是宽值的最高有效位),组合值向左移动,并提取最高有效位以生成与原始参数大小相同的结果。如果前 2 个参数相同,则这等效于循环左移操作。对于向量类型,操作发生在向量的每个元素上。移位参数被视为无符号量,模数为参数的元素大小。
参数:¶
前两个参数是要连接的值。第三个参数是移位量。参数可以是任何整数类型或具有整数元素类型的向量。所有参数和返回值必须具有相同的类型。
示例:¶
%r = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 %z) ; %r = i8: msb_extract((concat(x, y) << (z % 8)), 8)
%r = call i8 @llvm.fshl.i8(i8 255, i8 0, i8 15) ; %r = i8: 128 (0b10000000)
%r = call i8 @llvm.fshl.i8(i8 15, i8 15, i8 11) ; %r = i8: 120 (0b01111000)
%r = call i8 @llvm.fshl.i8(i8 0, i8 255, i8 8) ; %r = i8: 0 (0b00000000)
‘llvm.fshr.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或任何整数元素向量上使用 llvm.fshr
。但是,并非所有目标都支持所有位宽或向量类型。
declare i8 @llvm.fshr.i8 (i8 %a, i8 %b, i8 %c)
declare i64 @llvm.fshr.i64(i64 %a, i64 %b, i64 %c)
declare <2 x i32> @llvm.fshr.v2i32(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c)
概述:¶
‘llvm.fshr
’ 系列内建函数执行漏斗右移:前两个值连接为 { %a : %b }(%a 是宽值的最高有效位),组合值向右移动,并提取最低有效位以生成与原始参数大小相同的结果。如果前 2 个参数相同,则这等效于循环右移操作。对于向量类型,操作发生在向量的每个元素上。移位参数被视为无符号量,模数为参数的元素大小。
参数:¶
前两个参数是要连接的值。第三个参数是移位量。参数可以是任何整数类型或具有整数元素类型的向量。所有参数和返回值必须具有相同的类型。
示例:¶
%r = call i8 @llvm.fshr.i8(i8 %x, i8 %y, i8 %z) ; %r = i8: lsb_extract((concat(x, y) >> (z % 8)), 8)
%r = call i8 @llvm.fshr.i8(i8 255, i8 0, i8 15) ; %r = i8: 254 (0b11111110)
%r = call i8 @llvm.fshr.i8(i8 15, i8 15, i8 11) ; %r = i8: 225 (0b11100001)
%r = call i8 @llvm.fshr.i8(i8 0, i8 255, i8 8) ; %r = i8: 255 (0b11111111)
带溢出算术内建函数¶
LLVM 提供了用于快速算术溢出检查的内建函数。
这些内建函数中的每一个都返回一个包含两个元素的结构体。此结构的第一个元素包含相应算术运算模 2n 的结果,其中 n 是结果的位宽。因此,例如,llvm.sadd.with.overflow.i32
返回的结构的第一个元素始终与具有相同操作数的 32 位 add
指令的结果相同,其中 add
*未*被 nsw
或 nuw
标志修改。
结果的第二个元素是一个 i1
,如果算术运算溢出则为 1,否则为 0。如果对于其操作数 A
和 B
的任何值以及对于大于操作数宽度的任何 N
,ext(A op B) to iN
不等于 (ext(A) to iN) op (ext(B) to iN)
,则操作溢出,其中 ext
对于有符号溢出是 sext
,对于无符号溢出是 zext
,并且 op
是底层算术运算。
这些内建函数的行为对于所有参数值都是明确定义的。
‘llvm.sadd.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.sadd.with.overflow
。
declare {i16, i1} @llvm.sadd.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.sadd.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.sadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.sadd.with.overflow
’ 系列内建函数对两个参数执行有符号加法,并指示有符号求和期间是否发生溢出。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行有符号加法的两个值。
语义:¶
‘llvm.sadd.with.overflow
’ 系列内建函数对两个变量执行有符号加法。它们返回一个结构体——第一个元素是有符号求和,第二个元素是一个位,指定有符号求和是否导致溢出。
示例:¶
%res = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %normal
‘llvm.uadd.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.uadd.with.overflow
。
declare {i16, i1} @llvm.uadd.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.uadd.with.overflow
’ 系列内建函数对两个参数执行无符号加法,并指示无符号求和期间是否发生进位。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行无符号加法的两个值。
语义:¶
‘llvm.uadd.with.overflow
’ 系列内建函数对两个参数执行无符号加法。它们返回一个结构体——第一个元素是和,第二个元素是一个位,指定无符号求和是否导致进位。
示例:¶
%res = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %carry, label %normal
‘llvm.ssub.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.ssub.with.overflow
。
declare {i16, i1} @llvm.ssub.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.ssub.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.ssub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.ssub.with.overflow
’ 系列内建函数对两个参数执行有符号减法,并指示有符号减法期间是否发生溢出。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行有符号减法的两个值。
语义:¶
‘llvm.ssub.with.overflow
’ 系列内建函数对两个参数执行有符号减法。它们返回一个结构体——第一个元素是减法,第二个元素是一个位,指定有符号减法是否导致溢出。
示例:¶
%res = call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %normal
‘llvm.usub.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.usub.with.overflow
。
declare {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.usub.with.overflow
’ 系列内建函数对两个参数执行无符号减法,并指示无符号减法期间是否发生溢出。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行无符号减法的两个值。
语义:¶
‘llvm.usub.with.overflow
’ 系列内建函数对两个参数执行无符号减法。它们返回一个结构体——第一个元素是减法,第二个元素是一个位,指定无符号减法是否导致溢出。
示例:¶
%res = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %normal
‘llvm.smul.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.smul.with.overflow
。
declare {i16, i1} @llvm.smul.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.smul.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.smul.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.smul.with.overflow
’ 系列内建函数对两个参数执行有符号乘法,并指示有符号乘法期间是否发生溢出。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行有符号乘法的两个值。
语义:¶
‘llvm.smul.with.overflow
’ 系列内建函数对两个参数执行有符号乘法。它们返回一个结构体——第一个元素是乘法,第二个元素是一个位,指定有符号乘法是否导致溢出。
示例:¶
%res = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %normal
‘llvm.umul.with.overflow.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.umul.with.overflow
。
declare {i16, i1} @llvm.umul.with.overflow.i16(i16 %a, i16 %b)
declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)
declare {i64, i1} @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
declare {<4 x i32>, <4 x i1>} @llvm.umul.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
概述:¶
‘llvm.umul.with.overflow
’ 系列内建函数对两个参数执行无符号乘法,并指示无符号乘法期间是否发生溢出。
参数:¶
参数(%a 和 %b)和结果结构的第一个元素可以是任何位宽的整数类型,但它们必须具有相同的位宽。结果结构的第二个元素必须是 i1
类型。%a
和 %b
是将进行无符号乘法的两个值。
语义:¶
‘llvm.umul.with.overflow
’ 系列内建函数对两个参数执行无符号乘法。它们返回一个结构体——第一个元素是乘法,第二个元素是一个位,指定无符号乘法是否导致溢出。
示例:¶
%res = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b)
%sum = extractvalue {i32, i1} %res, 0
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %normal
饱和算术内建函数¶
饱和算术是一种算术版本,其中运算被限制在最小值和最大值之间的固定范围内。如果运算结果大于最大值,则结果设置为(或“钳制”为)此最大值。如果低于最小值,则将其钳制为此最小值。
‘llvm.sadd.sat.*
’ 内建函数¶
语法¶
这是一个重载的内建函数。您可以在任何整数位宽或整数向量上使用 llvm.sadd.sat
。
declare i16 @llvm.sadd.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.sadd.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.sadd.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.sadd.sat
’ 系列内建函数对 2 个参数执行有符号饱和加法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行有符号加法的两个值。
语义:¶
此操作可以钳制到的最大值是参数位宽可表示的最大有符号值。最小值是此位宽可表示的最小有符号值。
示例¶
%res = call i4 @llvm.sadd.sat.i4(i4 1, i4 2) ; %res = 3
%res = call i4 @llvm.sadd.sat.i4(i4 5, i4 6) ; %res = 7
%res = call i4 @llvm.sadd.sat.i4(i4 -4, i4 2) ; %res = -2
%res = call i4 @llvm.sadd.sat.i4(i4 -4, i4 -5) ; %res = -8
‘llvm.uadd.sat.*
’ 内建函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.uadd.sat
。
declare i16 @llvm.uadd.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.uadd.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.uadd.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.uadd.sat
’ 系列内联函数对 2 个参数执行无符号饱和加法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行无符号加法的两个值。
语义:¶
此操作可以钳位的最大值是参数位宽可以表示的最大无符号值。由于这是一个无符号操作,因此结果永远不会向零饱和。
示例¶
%res = call i4 @llvm.uadd.sat.i4(i4 1, i4 2) ; %res = 3
%res = call i4 @llvm.uadd.sat.i4(i4 5, i4 6) ; %res = 11
%res = call i4 @llvm.uadd.sat.i4(i4 8, i4 8) ; %res = 15
‘llvm.ssub.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.ssub.sat
。
declare i16 @llvm.ssub.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.ssub.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.ssub.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.ssub.sat
’ 系列内联函数对 2 个参数执行有符号饱和减法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行有符号减法的两个值。
语义:¶
此操作可以钳制到的最大值是参数位宽可表示的最大有符号值。最小值是此位宽可表示的最小有符号值。
示例¶
%res = call i4 @llvm.ssub.sat.i4(i4 2, i4 1) ; %res = 1
%res = call i4 @llvm.ssub.sat.i4(i4 2, i4 6) ; %res = -4
%res = call i4 @llvm.ssub.sat.i4(i4 -4, i4 5) ; %res = -8
%res = call i4 @llvm.ssub.sat.i4(i4 4, i4 -5) ; %res = 7
‘llvm.usub.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.usub.sat
。
declare i16 @llvm.usub.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.usub.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.usub.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.usub.sat
’ 系列内联函数对 2 个参数执行无符号饱和减法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行无符号减法的两个值。
语义:¶
此操作可以钳位的最小值是 0,这是无符号参数的位宽可以表示的最小无符号值。由于这是一个无符号操作,因此结果永远不会向此位宽可以表示的最大可能值饱和。
示例¶
%res = call i4 @llvm.usub.sat.i4(i4 2, i4 1) ; %res = 1
%res = call i4 @llvm.usub.sat.i4(i4 2, i4 6) ; %res = 0
‘llvm.sshl.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何位宽的整数或整数向量上使用 llvm.sshl.sat
。
declare i16 @llvm.sshl.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.sshl.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.sshl.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.sshl.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.sshl.sat
’ 系列内联函数对第一个参数执行有符号饱和左移。
参数¶
参数(%a
和 %b
)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
是要移位的值,%b
是移位量。如果 b
(静态或动态地)等于或大于参数的整数位宽,则结果是 poison value。如果参数是向量,则 a
的每个向量元素都按 b
中相应的移位量进行移位。
语义:¶
此操作可以钳制到的最大值是参数位宽可表示的最大有符号值。最小值是此位宽可表示的最小有符号值。
示例¶
%res = call i4 @llvm.sshl.sat.i4(i4 2, i4 1) ; %res = 4
%res = call i4 @llvm.sshl.sat.i4(i4 2, i4 2) ; %res = 7
%res = call i4 @llvm.sshl.sat.i4(i4 -5, i4 1) ; %res = -8
%res = call i4 @llvm.sshl.sat.i4(i4 -1, i4 1) ; %res = -2
‘llvm.ushl.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何位宽的整数或整数向量上使用 llvm.ushl.sat
。
declare i16 @llvm.ushl.sat.i16(i16 %a, i16 %b)
declare i32 @llvm.ushl.sat.i32(i32 %a, i32 %b)
declare i64 @llvm.ushl.sat.i64(i64 %a, i64 %b)
declare <4 x i32> @llvm.ushl.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
概述¶
‘llvm.ushl.sat
’ 系列内联函数对第一个参数执行无符号饱和左移。
参数¶
参数(%a
和 %b
)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
是要移位的值,%b
是移位量。如果 b
(静态或动态地)等于或大于参数的整数位宽,则结果是 poison value。如果参数是向量,则 a
的每个向量元素都按 b
中相应的移位量进行移位。
语义:¶
此操作可以钳位的最大值是参数位宽可以表示的最大无符号值。
示例¶
%res = call i4 @llvm.ushl.sat.i4(i4 2, i4 1) ; %res = 4
%res = call i4 @llvm.ushl.sat.i4(i4 3, i4 3) ; %res = 15
定点算术内联函数¶
定点数表示一种实数数据类型,用于小数点(相当于小数点“.”)后具有固定位数的数字。小数点后的位数称为比例。这些对于以特定精度表示小数值非常有用。以下内联函数对具有相同比例的 2 个操作数执行定点算术运算,比例在第三个参数中指定。
llvm.*mul.fix
系列内联函数表示通过缩放整数的定点数乘法。因此,定点乘法可以表示为
%result = call i4 @llvm.smul.fix.i4(i4 %a, i4 %b, i32 %scale)
; Expands to
%a2 = sext i4 %a to i8
%b2 = sext i4 %b to i8
%mul = mul nsw nuw i8 %a2, %b2
%scale2 = trunc i32 %scale to i8
%r = ashr i8 %mul, i8 %scale2 ; this is for a target rounding down towards negative infinity
%result = trunc i8 %r to i4
llvm.*div.fix
系列内联函数表示通过缩放整数的定点数除法。定点除法可以表示为
%result call i4 @llvm.sdiv.fix.i4(i4 %a, i4 %b, i32 %scale)
; Expands to
%a2 = sext i4 %a to i8
%b2 = sext i4 %b to i8
%scale2 = trunc i32 %scale to i8
%a3 = shl i8 %a2, %scale2
%r = sdiv i8 %a3, %b2 ; this is for a target rounding towards zero
%result = trunc i8 %r to i4
对于这些函数中的每一个,如果结果无法以提供的比例精确表示,则结果将被舍入。舍入方式未指定,因为不同目标的首选舍入方式可能不同。舍入通过目标钩子指定。如果提供了此钩子,则不同的流水线应使用此钩子指定的舍入方式来合法化或优化此操作。常量折叠、指令组合、KnownBits 和 ValueTracking 等操作也应使用此钩子(如果提供),而不要假定舍入方向。舍入后的结果必须始终在真实结果的一个精度单位之内。也就是说,返回结果与真实结果之间的误差必须小于 1/2^(比例)。
‘llvm.smul.fix.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.smul.fix
。
declare i16 @llvm.smul.fix.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.smul.fix.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.smul.fix.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.smul.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.smul.fix
’ 系列内联函数对具有相同比例的 2 个参数执行有符号定点乘法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。参数也可以使用相同长度和整数大小的整数向量。%a
和 %b
是将进行有符号定点乘法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点乘法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
如果结果值不适合定点类型的范围,则为未定义行为。
示例¶
%res = call i4 @llvm.smul.fix.i4(i4 3, i4 2, i32 0) ; %res = 6 (2 x 3 = 6)
%res = call i4 @llvm.smul.fix.i4(i4 3, i4 2, i32 1) ; %res = 3 (1.5 x 1 = 1.5)
%res = call i4 @llvm.smul.fix.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 x -1 = -1.5)
; The result in the following could be rounded up to -2 or down to -2.5
%res = call i4 @llvm.smul.fix.i4(i4 3, i4 -3, i32 1) ; %res = -5 (or -4) (1.5 x -1.5 = -2.25)
‘llvm.umul.fix.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.umul.fix
。
declare i16 @llvm.umul.fix.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.umul.fix.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.umul.fix.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.umul.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.umul.fix
’ 系列内联函数对具有相同比例的 2 个参数执行无符号定点乘法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。参数也可以使用相同长度和整数大小的整数向量。%a
和 %b
是将进行无符号定点乘法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行无符号定点乘法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
如果结果值不适合定点类型的范围,则为未定义行为。
示例¶
%res = call i4 @llvm.umul.fix.i4(i4 3, i4 2, i32 0) ; %res = 6 (2 x 3 = 6)
%res = call i4 @llvm.umul.fix.i4(i4 3, i4 2, i32 1) ; %res = 3 (1.5 x 1 = 1.5)
; The result in the following could be rounded down to 3.5 or up to 4
%res = call i4 @llvm.umul.fix.i4(i4 15, i4 1, i32 1) ; %res = 7 (or 8) (7.5 x 0.5 = 3.75)
‘llvm.smul.fix.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.smul.fix.sat
。
declare i16 @llvm.smul.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.smul.fix.sat.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.smul.fix.sat.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.smul.fix.sat.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.smul.fix.sat
’ 系列内联函数对具有相同比例的 2 个参数执行有符号定点饱和乘法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行有符号定点乘法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点乘法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
此操作可以钳位的最大值是前 2 个参数的位宽可以表示的最大有符号值。最小值是此位宽可以表示的最小有符号值。
示例¶
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 2, i32 0) ; %res = 6 (2 x 3 = 6)
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 2, i32 1) ; %res = 3 (1.5 x 1 = 1.5)
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 x -1 = -1.5)
; The result in the following could be rounded up to -2 or down to -2.5
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 -3, i32 1) ; %res = -5 (or -4) (1.5 x -1.5 = -2.25)
; Saturation
%res = call i4 @llvm.smul.fix.sat.i4(i4 7, i4 2, i32 0) ; %res = 7
%res = call i4 @llvm.smul.fix.sat.i4(i4 7, i4 4, i32 2) ; %res = 7
%res = call i4 @llvm.smul.fix.sat.i4(i4 -8, i4 5, i32 2) ; %res = -8
%res = call i4 @llvm.smul.fix.sat.i4(i4 -8, i4 -2, i32 1) ; %res = 7
; Scale can affect the saturation result
%res = call i4 @llvm.smul.fix.sat.i4(i4 2, i4 4, i32 0) ; %res = 7 (2 x 4 -> clamped to 7)
%res = call i4 @llvm.smul.fix.sat.i4(i4 2, i4 4, i32 1) ; %res = 4 (1 x 2 = 2)
‘llvm.umul.fix.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.umul.fix.sat
。
declare i16 @llvm.umul.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.umul.fix.sat.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.umul.fix.sat.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.umul.fix.sat.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.umul.fix.sat
’ 系列内联函数对具有相同比例的 2 个参数执行无符号定点饱和乘法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行无符号定点乘法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点乘法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
此操作可以钳位的最大值是前 2 个参数的位宽可以表示的最大无符号值。最小值是此位宽可以表示的最小无符号值(零)。
示例¶
%res = call i4 @llvm.umul.fix.sat.i4(i4 3, i4 2, i32 0) ; %res = 6 (2 x 3 = 6)
%res = call i4 @llvm.umul.fix.sat.i4(i4 3, i4 2, i32 1) ; %res = 3 (1.5 x 1 = 1.5)
; The result in the following could be rounded down to 2 or up to 2.5
%res = call i4 @llvm.umul.fix.sat.i4(i4 3, i4 3, i32 1) ; %res = 4 (or 5) (1.5 x 1.5 = 2.25)
; Saturation
%res = call i4 @llvm.umul.fix.sat.i4(i4 8, i4 2, i32 0) ; %res = 15 (8 x 2 -> clamped to 15)
%res = call i4 @llvm.umul.fix.sat.i4(i4 8, i4 8, i32 2) ; %res = 15 (2 x 2 -> clamped to 3.75)
; Scale can affect the saturation result
%res = call i4 @llvm.umul.fix.sat.i4(i4 2, i4 4, i32 0) ; %res = 7 (2 x 4 -> clamped to 7)
%res = call i4 @llvm.umul.fix.sat.i4(i4 2, i4 4, i32 1) ; %res = 4 (1 x 2 = 2)
‘llvm.sdiv.fix.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.sdiv.fix
。
declare i16 @llvm.sdiv.fix.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.sdiv.fix.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.sdiv.fix.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.sdiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.sdiv.fix
’ 系列内联函数对具有相同比例的 2 个参数执行有符号定点除法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。参数也可以使用相同长度和整数大小的整数向量。%a
和 %b
是将进行有符号定点除法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点除法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
如果结果值不适合定点类型的范围,或者如果第二个参数为零,则为未定义行为。
示例¶
%res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3)
%res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5)
%res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 / -1 = -1.5)
; The result in the following could be rounded up to 1 or down to 0.5
%res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 4, i32 1) ; %res = 2 (or 1) (1.5 / 2 = 0.75)
‘llvm.udiv.fix.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.udiv.fix
。
declare i16 @llvm.udiv.fix.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.udiv.fix.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.udiv.fix.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.udiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.udiv.fix
’ 系列内联函数对具有相同比例的 2 个参数执行无符号定点除法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。参数也可以使用相同长度和整数大小的整数向量。%a
和 %b
是将进行无符号定点除法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点除法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
如果结果值不适合定点类型的范围,或者如果第二个参数为零,则为未定义行为。
示例¶
%res = call i4 @llvm.udiv.fix.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3)
%res = call i4 @llvm.udiv.fix.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5)
%res = call i4 @llvm.udiv.fix.i4(i4 1, i4 -8, i32 4) ; %res = 2 (0.0625 / 0.5 = 0.125)
; The result in the following could be rounded up to 1 or down to 0.5
%res = call i4 @llvm.udiv.fix.i4(i4 3, i4 4, i32 1) ; %res = 2 (or 1) (1.5 / 2 = 0.75)
‘llvm.sdiv.fix.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.sdiv.fix.sat
。
declare i16 @llvm.sdiv.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.sdiv.fix.sat.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.sdiv.fix.sat.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.sdiv.fix.sat.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.sdiv.fix.sat
’ 系列内联函数对具有相同比例的 2 个参数执行有符号定点饱和除法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行有符号定点除法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点除法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
此操作可以钳位的最大值是前 2 个参数的位宽可以表示的最大有符号值。最小值是此位宽可以表示的最小有符号值。
如果第二个参数为零,则为未定义行为。
示例¶
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3)
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5)
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 / -1 = -1.5)
; The result in the following could be rounded up to 1 or down to 0.5
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 3, i4 4, i32 1) ; %res = 2 (or 1) (1.5 / 2 = 0.75)
; Saturation
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 -8, i4 -1, i32 0) ; %res = 7 (-8 / -1 = 8 => 7)
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 4, i4 2, i32 2) ; %res = 7 (1 / 0.5 = 2 => 1.75)
%res = call i4 @llvm.sdiv.fix.sat.i4(i4 -4, i4 1, i32 2) ; %res = -8 (-1 / 0.25 = -4 => -2)
‘llvm.udiv.fix.sat.*
’ 内联函数¶
语法¶
这是一个重载的内联函数。你可以在任何整数位宽或整数向量上使用 llvm.udiv.fix.sat
。
declare i16 @llvm.udiv.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.udiv.fix.sat.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.udiv.fix.sat.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.udiv.fix.sat.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
概述¶
‘llvm.udiv.fix.sat
’ 系列内联函数对具有相同比例的 2 个参数执行无符号定点饱和除法。
参数¶
参数(%a 和 %b)和结果可以是任何位宽的整数类型,但它们必须具有相同的位宽。%a
和 %b
是将进行无符号定点除法的两个值。参数 %scale
表示两个操作数的比例,并且必须是常量整数。
语义:¶
此操作对具有指定比例的 2 个参数执行定点除法。结果也将以第三个参数中指定的相同比例返回。
如果结果值无法以给定的比例精确表示,则该值将向上或向下舍入到最接近的可表示值。舍入方向未指定。
此操作可以钳位的最大值是前 2 个参数的位宽可以表示的最大无符号值。最小值是此位宽可以表示的最小无符号值(零)。
如果第二个参数为零,则为未定义行为。
示例¶
%res = call i4 @llvm.udiv.fix.sat.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3)
%res = call i4 @llvm.udiv.fix.sat.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5)
; The result in the following could be rounded down to 0.5 or up to 1
%res = call i4 @llvm.udiv.fix.sat.i4(i4 3, i4 4, i32 1) ; %res = 1 (or 2) (1.5 / 2 = 0.75)
; Saturation
%res = call i4 @llvm.udiv.fix.sat.i4(i4 8, i4 2, i32 2) ; %res = 15 (2 / 0.5 = 4 => 3.75)
专用算术内联函数¶
‘llvm.canonicalize.*
’ 内联函数¶
语法:¶
declare float @llvm.canonicalize.f32(float %a)
declare double @llvm.canonicalize.f64(double %b)
概述:¶
‘llvm.canonicalize.*
’ 内联函数返回浮点数的平台特定规范编码。这种规范化对于实现某些数值原语(如 frexp)非常有用。规范编码由 IEEE-754-2008 定义为
2.1.8 canonical encoding: The preferred encoding of a floating-point
representation in a format. Applied to declets, significands of finite
numbers, infinities, and NaNs, especially in decimal formats.
此操作也可以被视为等效于 IEEE-754-2008 将浮点值转换为相同格式。NaN 根据第 6.2 节处理。
非规范编码的示例
x87 伪次正规数、伪 NaN、伪无穷大、非正规数。这些根据硬件特定的协议转换为规范表示。
许多正规十进制浮点数具有非规范的替代编码。
某些机器(如 GPU 或 ARMv7 NEON)不支持次正规值。这些被视为零的非规范编码,并将通过此操作刷新为具有相同符号的零。
请注意,根据 IEEE-754-2008 6.2,支持带默认异常处理的 signaling NaN 的系统必须发出无效异常信号,并生成 quiet NaN 结果。
此函数应始终可以实现为乘以 1.0,前提是编译器不常量折叠该操作。同样,除以 1.0 和 llvm.minnum(x, x)
也是可能的实现。与 -0.0 相加也足够,前提是舍入模式不是 -Infinity。
@llvm.canonicalize
必须保留相等关系。也就是说
(@llvm.canonicalize(x) == x)
等效于(x == x)
(@llvm.canonicalize(x) == @llvm.canonicalize(y))
等效于(x == y)
此外,必须保留零的符号:@llvm.canonicalize(-0.0) = -0.0
和 @llvm.canonicalize(+0.0) = +0.0
NaN 的有效负载位必须保留,但有两个例外。首先,仅使用 NaN 的单个规范表示的环境必须执行所述规范化。其次,SNaN 必须按照通常的方法进行静默处理。
如果满足以下条件,则可以优化掉规范化操作
输入已知是规范的。例如,它是由标准要求为规范的浮点运算生成的。
结果仅由其他浮点运算使用(或与之融合)。也就是说,不检查浮点值的位。
‘llvm.fmuladd.*
’ 内联函数¶
语法:¶
declare float @llvm.fmuladd.f32(float %a, float %b, float %c)
declare double @llvm.fmuladd.f64(double %a, double %b, double %c)
概述:¶
‘llvm.fmuladd.*
’ 内联函数表示乘加表达式,如果代码生成器确定 (a) 目标指令集支持融合操作,并且 (b) 融合操作比等效的、单独的 mul 和 add 指令对更有效,则可以融合这些表达式。
参数:¶
‘llvm.fmuladd.*
’ 内联函数各自接受三个参数:两个被乘数 a 和 b,以及一个加数 c。
语义:¶
表达式
%0 = call float @llvm.fmuladd.f32(%a, %b, %c)
等效于表达式 a * b + c,不同之处在于,乘法和加法步骤之间是否执行舍入是未指定的。即使目标平台支持融合乘加,也不能保证一定会进行融合。如果需要融合乘加,则应改用相应的 llvm.fma 内联函数。这永远不会设置 errno,就像 ‘llvm.fma.*
’ 一样。
示例:¶
%r2 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) ; yields float:r2 = (a * b) + c
硬件循环内联函数¶
LLVM 支持多个内联函数来将循环标记为硬件循环。这些是对后端的提示,后端需要进一步降低这些内联函数以针对特定指令,或者在不满足目标特定限制且无法生成硬件循环时将硬件循环恢复为普通循环。
这些内联函数将来可能会被修改,并且不打算在后端之外使用。因此,前端和中级优化不应生成这些内联函数。
‘llvm.set.loop.iterations.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare void @llvm.set.loop.iterations.i32(i32)
declare void @llvm.set.loop.iterations.i64(i64)
概述:¶
‘llvm.set.loop.iterations.*
’ 内联函数用于指定硬件循环的循环计数。它们放置在循环前置头基本块中,并标记为 IntrNoDuplicate
以避免优化器复制这些指令。
参数:¶
整数操作数是硬件循环的循环计数,因此不是例如循环后沿采取计数。
语义:¶
‘llvm.set.loop.iterations.*
’ 内联函数不对其操作数执行任何算术运算。这是对后端的提示,后端可以使用它通过目标特定指令来设置硬件循环计数,通常是将此值移动到特殊寄存器或硬件循环指令。
‘llvm.start.loop.iterations.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.start.loop.iterations.i32(i32)
declare i64 @llvm.start.loop.iterations.i64(i64)
概述:¶
‘llvm.start.loop.iterations.*
’ 内联函数类似于 ‘llvm.set.loop.iterations.*
’ 内联函数,用于指定硬件循环的循环计数,但也生成与输入相同的值,该值可以用作循环的输入。它们放置在循环前置头基本块中,并且输出预计是循环归纳变量的 phi 的输入,由 ‘llvm.loop.decrement.reg.*
’ 递减。
参数:¶
整数操作数是硬件循环的循环计数,因此不是例如循环后沿采取计数。
语义:¶
‘llvm.start.loop.iterations.*
’ 内联函数不对其操作数执行任何算术运算。这是对后端的提示,后端可以使用它通过目标特定指令来设置硬件循环计数,通常是将此值移动到特殊寄存器或硬件循环指令。
‘llvm.test.set.loop.iterations.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare i1 @llvm.test.set.loop.iterations.i32(i32)
declare i1 @llvm.test.set.loop.iterations.i64(i64)
概述:¶
‘llvm.test.set.loop.iterations.*
’ 内部函数用于指定循环的迭代计数,并测试给定的计数是否非零,从而控制进入 while 循环。它们被放置在循环前置头的前驱基本块中,并标记为 IntrNoDuplicate
以避免优化器复制这些指令。
参数:¶
整数操作数是硬件循环的循环计数,因此不是例如循环后沿采取计数。
语义:¶
‘llvm.test.set.loop.iterations.*
’ 内部函数不对其操作数执行任何算术运算。它向后端发出提示,后端可以使用它来设置硬件循环计数,使用目标特定的指令,通常是将此值移动到特殊寄存器或硬件循环指令。结果是给定计数是否非零的条件值。
‘llvm.test.start.loop.iterations.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare {i32, i1} @llvm.test.start.loop.iterations.i32(i32)
declare {i64, i1} @llvm.test.start.loop.iterations.i64(i64)
概述:¶
‘llvm.test.start.loop.iterations.*
’ 内部函数类似于 ‘llvm.test.set.loop.iterations.*
’ 和 ‘llvm.start.loop.iterations.*
’ 内部函数,用于指定硬件循环的迭代计数,但也会生成一个与输入相同的值,可以用作循环的输入。第二个 i1 输出控制进入 while 循环。
参数:¶
整数操作数是硬件循环的循环计数,因此不是例如循环后沿采取计数。
语义:¶
‘llvm.test.start.loop.iterations.*
’ 内部函数不对其操作数执行任何算术运算。它向后端发出提示,后端可以使用它来设置硬件循环计数,使用目标特定的指令,通常是将此值移动到特殊寄存器或硬件循环指令。结果是一对值,包括输入值和给定计数是否非零的条件值。
‘llvm.loop.decrement.reg.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.loop.decrement.reg.i32(i32, i32)
declare i64 @llvm.loop.decrement.reg.i64(i64, i64)
概述:¶
‘llvm.loop.decrement.reg.*
’ 内部函数用于减少循环迭代计数器,并返回将在下一次循环测试检查中使用的更新值。
参数:¶
两个参数必须具有相同的整数类型。第一个操作数是循环迭代计数器。第二个操作数是每次迭代中处理的最大元素数。
语义:¶
‘llvm.loop.decrement.reg.*
’ 内部函数对其两个操作数执行整数 SUB
运算,该运算不允许环绕。它们返回仍要执行的剩余迭代次数,并且可以与 PHI
、ICMP
和 BR
一起使用来控制执行的循环迭代次数。任何优化都允许将其视为 SUB
,并且 SCEV 支持它,因此后端有责任处理可能被优化的情况。这些内部函数被标记为 IntrNoDuplicate
以避免优化器复制这些指令。
‘llvm.loop.decrement.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare i1 @llvm.loop.decrement.i32(i32)
declare i1 @llvm.loop.decrement.i64(i64)
概述:¶
HardwareLoops pass 允许使用选项指定循环递减值。它默认为循环递减值 1,但它可以是由此选项提供的无符号整数值。‘llvm.loop.decrement.*
’ 内部函数使用此值递减循环迭代计数器,如果循环应退出,则返回 false 谓词,否则返回 true。如果循环计数器未通过 PHI
节点更新,则会发出此内部函数,这也可以通过选项控制。
参数:¶
整数参数是用于递减循环迭代计数器的循环递减值。
语义:¶
‘llvm.loop.decrement.*
’ 内部函数对循环迭代计数器执行 SUB
运算,使用给定的循环递减值,如果循环应退出则返回 false,此 SUB
运算不允许环绕。结果是由控制循环的条件分支使用的条件。
向量归约内部函数¶
向量的水平归约可以使用以下内部函数表示。每个内部函数都将向量操作数作为输入,并对其向量的所有元素应用各自的操作,返回与元素类型相同的单个标量结果。
‘llvm.vector.reduce.add.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %a)
declare i64 @llvm.vector.reduce.add.v2i64(<2 x i64> %a)
概述:¶
‘llvm.vector.reduce.add.*
’ 内部函数对向量执行整数 ADD
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.fadd.*
’ 内部函数¶
语法:¶
declare float @llvm.vector.reduce.fadd.v4f32(float %start_value, <4 x float> %a)
declare double @llvm.vector.reduce.fadd.v2f64(double %start_value, <2 x double> %a)
概述:¶
‘llvm.vector.reduce.fadd.*
’ 内部函数对向量执行浮点 ADD
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
如果内部函数调用设置了 ‘reassoc’ 标志,则归约将不会保留等效标量化对应项的结合性。否则,归约将是顺序的,因此意味着该操作尊重标量化归约的结合性。也就是说,归约从起始值开始,并对连续递增的向量元素索引执行 fadd 操作。请参阅以下伪代码
float sequential_fadd(start_value, input_vector)
result = start_value
for i = 0 to length(input_vector)
result = result + input_vector[i]
return result
参数:¶
此内部函数的第一个参数是归约的标量起始值。起始值的类型与向量输入的元素类型匹配。第二个参数必须是浮点值向量。
要忽略起始值,可以使用负零 (-0.0
),因为它是浮点加法的单位元。
示例:¶
%unord = call reassoc float @llvm.vector.reduce.fadd.v4f32(float -0.0, <4 x float> %input) ; relaxed reduction
%ord = call float @llvm.vector.reduce.fadd.v4f32(float %start_value, <4 x float> %input) ; sequential reduction
‘llvm.vector.reduce.mul.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> %a)
declare i64 @llvm.vector.reduce.mul.v2i64(<2 x i64> %a)
概述:¶
‘llvm.vector.reduce.mul.*
’ 内部函数对向量执行整数 MUL
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.fmul.*
’ 内部函数¶
语法:¶
declare float @llvm.vector.reduce.fmul.v4f32(float %start_value, <4 x float> %a)
declare double @llvm.vector.reduce.fmul.v2f64(double %start_value, <2 x double> %a)
概述:¶
‘llvm.vector.reduce.fmul.*
’ 内部函数对向量执行浮点 MUL
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
如果内部函数调用设置了 ‘reassoc’ 标志,则归约将不会保留等效标量化对应项的结合性。否则,归约将是顺序的,因此意味着该操作尊重标量化归约的结合性。也就是说,归约从起始值开始,并对连续递增的向量元素索引执行 fmul 操作。请参阅以下伪代码
float sequential_fmul(start_value, input_vector)
result = start_value
for i = 0 to length(input_vector)
result = result * input_vector[i]
return result
参数:¶
此内部函数的第一个参数是归约的标量起始值。起始值的类型与向量输入的元素类型匹配。第二个参数必须是浮点值向量。
要忽略起始值,可以使用一 (1.0
),因为它是浮点乘法的单位元。
示例:¶
%unord = call reassoc float @llvm.vector.reduce.fmul.v4f32(float 1.0, <4 x float> %input) ; relaxed reduction
%ord = call float @llvm.vector.reduce.fmul.v4f32(float %start_value, <4 x float> %input) ; sequential reduction
‘llvm.vector.reduce.and.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.and.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.and.*
’ 内部函数对向量执行按位 AND
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.or.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.or.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.or.*
’ 内部函数对向量执行按位 OR
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.xor.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.xor.*
’ 内部函数对向量执行按位 XOR
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.smax.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.smax.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.smax.*
’ 内部函数对向量执行有符号整数 MAX
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.smin.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.smin.*
’ 内部函数对向量执行有符号整数 MIN
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.umax.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.umax.*
’ 内部函数对向量执行无符号整数 MAX
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.umin.*
’ 内部函数¶
语法:¶
declare i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> %a)
概述:¶
‘llvm.vector.reduce.umin.*
’ 内部函数对向量执行无符号整数 MIN
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
参数:¶
此内部函数的参数必须是整数值向量。
‘llvm.vector.reduce.fmax.*
’ 内部函数¶
语法:¶
declare float @llvm.vector.reduce.fmax.v4f32(<4 x float> %a)
declare double @llvm.vector.reduce.fmax.v2f64(<2 x double> %a)
概述:¶
‘llvm.vector.reduce.fmax.*
’ 内部函数对向量执行浮点 MAX
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
此指令具有与 ‘llvm.maxnum.*
’ 内部函数相同的比较语义。如果内部函数调用具有 nnan
快速数学标志,则操作可以假定输入向量中不存在 NaN。
参数:¶
此内部函数的参数必须是浮点值向量。
‘llvm.vector.reduce.fmin.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vector.reduce.fmin.v4f32(<4 x float> %a)
declare double @llvm.vector.reduce.fmin.v2f64(<2 x double> %a)
概述:¶
‘llvm.vector.reduce.fmin.*
’ 内部函数对向量执行浮点 MIN
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
此指令具有与 ‘llvm.minnum.*
’ 内部函数相同的比较语义。如果内部函数调用具有 nnan
快速数学标志,则操作可以假定输入向量中不存在 NaN。
参数:¶
此内部函数的参数必须是浮点值向量。
‘llvm.vector.reduce.fmaximum.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> %a)
declare double @llvm.vector.reduce.fmaximum.v2f64(<2 x double> %a)
概述:¶
‘llvm.vector.reduce.fmaximum.*
’ 内部函数对向量执行浮点 MAX
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
此指令具有与 ‘llvm.maximum.*
’ 内部函数相同的比较语义。也就是说,此内部函数传播 NaN,并且 +0.0 被认为大于 -0.0。如果向量的任何元素是 NaN,则结果为 NaN。
参数:¶
此内部函数的参数必须是浮点值向量。
‘llvm.vector.reduce.fminimum.*
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vector.reduce.fminimum.v4f32(<4 x float> %a)
declare double @llvm.vector.reduce.fminimum.v2f64(<2 x double> %a)
概述:¶
‘llvm.vector.reduce.fminimum.*
’ 内部函数对向量执行浮点 MIN
归约,将结果作为标量返回。返回类型与向量输入的元素类型匹配。
此指令具有与 ‘llvm.minimum.*
’ 内部函数相同的比较语义。也就是说,此内部函数传播 NaN,并且 -0.0 被认为小于 +0.0。如果向量的任何元素是 NaN,则结果为 NaN。
参数:¶
此内部函数的参数必须是浮点值向量。
‘llvm.vector.insert
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
; Insert fixed type into scalable type
declare <vscale x 4 x float> @llvm.vector.insert.nxv4f32.v4f32(<vscale x 4 x float> %vec, <4 x float> %subvec, i64 <idx>)
declare <vscale x 2 x double> @llvm.vector.insert.nxv2f64.v2f64(<vscale x 2 x double> %vec, <2 x double> %subvec, i64 <idx>)
; Insert scalable type into scalable type
declare <vscale x 4 x float> @llvm.vector.insert.nxv4f64.nxv2f64(<vscale x 4 x float> %vec, <vscale x 2 x float> %subvec, i64 <idx>)
; Insert fixed type into fixed type
declare <4 x double> @llvm.vector.insert.v4f64.v2f64(<4 x double> %vec, <2 x double> %subvec, i64 <idx>)
概述:¶
‘llvm.vector.insert.*
’ 内部函数将一个向量插入到另一个向量中,从给定的索引开始。返回类型与我们插入到的向量的类型匹配。从概念上讲,这可以用于从非可伸缩向量构建可伸缩向量,但是此内部函数也可以用于纯粹的固定类型。
可伸缩向量只能插入到其他可伸缩向量中。
参数:¶
vec
是 subvec
将被插入到的向量。subvec
是将被插入的向量。
idx
表示 subvec
将被插入的起始元素编号。idx
必须是 subvec
已知的最小向量长度的常数倍数。如果 subvec
是可伸缩向量,则 idx
首先按 subvec
的运行时缩放因子缩放。vec
中从 idx
开始的元素将被 subvec
覆盖。从 idx
到 (idx
+ num_elements(subvec
) - 1) 的元素必须是有效的 vec
索引。如果无法静态确定此条件,但在运行时为 false,则结果向量是 poison 值。
‘llvm.vector.extract
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
; Extract fixed type from scalable type
declare <4 x float> @llvm.vector.extract.v4f32.nxv4f32(<vscale x 4 x float> %vec, i64 <idx>)
declare <2 x double> @llvm.vector.extract.v2f64.nxv2f64(<vscale x 2 x double> %vec, i64 <idx>)
; Extract scalable type from scalable type
declare <vscale x 2 x float> @llvm.vector.extract.nxv2f32.nxv4f32(<vscale x 4 x float> %vec, i64 <idx>)
; Extract fixed type from fixed type
declare <2 x double> @llvm.vector.extract.v2f64.v4f64(<4 x double> %vec, i64 <idx>)
概述:¶
‘llvm.vector.extract.*
’ 内部函数从另一个向量中提取一个向量,从给定的索引开始。返回类型必须显式指定。从概念上讲,这可以用于将可伸缩向量分解为非可伸缩部分,但是此内部函数也可以用于纯粹的固定类型。
可伸缩向量只能从其他可伸缩向量中提取。
参数:¶
vec
是我们将从中提取子向量的向量。
idx
指定从 vec
中提取子向量的起始元素编号。idx
必须是结果类型的已知最小向量长度的常数倍数。如果结果类型是可伸缩向量,则 idx
首先按结果类型的运行时缩放因子缩放。从 idx
到 (idx
+ num_elements(result_type) - 1) 的元素必须是有效的向量索引。如果无法静态确定此条件,但在运行时为 false,则结果向量是 poison 值。idx
参数必须是向量索引常量类型(对于大多数目标,这将是整数指针类型)。
‘llvm.vector.reverse
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare <2 x i8> @llvm.vector.reverse.v2i8(<2 x i8> %a)
declare <vscale x 4 x i32> @llvm.vector.reverse.nxv4i32(<vscale x 4 x i32> %a)
概述:¶
‘llvm.vector.reverse.*
’ 内部函数反转向量。该内部函数接受单个向量,并返回类型匹配但通道顺序反转的向量。这些内部函数适用于固定向量和可伸缩向量。虽然此内部函数支持所有向量类型,但对于固定宽度向量,表达此操作的推荐方法仍然是使用 shufflevector,因为这可能会带来更多优化机会。
参数:¶
此内部函数的参数必须是向量。
‘llvm.vector.deinterleave2
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare {<2 x double>, <2 x double>} @llvm.vector.deinterleave2.v4f64(<4 x double> %vec1)
declare {<vscale x 4 x i32>, <vscale x 4 x i32>} @llvm.vector.deinterleave2.nxv8i32(<vscale x 8 x i32> %vec1)
概述:¶
‘llvm.vector.deinterleave2
’ 内部函数通过解交错输入向量的偶数和奇数通道来构造两个向量。
此内部函数适用于固定向量和可伸缩向量。虽然此内部函数支持所有向量类型,但对于固定宽度向量,表达此操作的推荐方法仍然是使用 shufflevector,因为这可能会带来更多优化机会。
例如
{<2 x i64>, <2 x i64>} llvm.vector.deinterleave2.v4i64(<4 x i64> <i64 0, i64 1, i64 2, i64 3>); ==> {<2 x i64> <i64 0, i64 2>, <2 x i64> <i64 1, i64 3>}
参数:¶
该参数是一个向量,其类型对应于两个结果类型的逻辑串联。
‘llvm.vector.interleave2
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x double> @llvm.vector.interleave2.v4f64(<2 x double> %vec1, <2 x double> %vec2)
declare <vscale x 8 x i32> @llvm.vector.interleave2.nxv8i32(<vscale x 4 x i32> %vec1, <vscale x 4 x i32> %vec2)
概述:¶
‘llvm.vector.interleave2
’ 内部函数通过交错两个输入向量来构造一个向量。
此内部函数适用于固定向量和可伸缩向量。虽然此内部函数支持所有向量类型,但对于固定宽度向量,表达此操作的推荐方法仍然是使用 shufflevector,因为这可能会带来更多优化机会。
例如
<4 x i64> llvm.vector.interleave2.v4i64(<2 x i64> <i64 0, i64 2>, <2 x i64> <i64 1, i64 3>); ==> <4 x i64> <i64 0, i64 1, i64 2, i64 3>
参数:¶
两个参数都必须是相同类型的向量,其中它们的逻辑串联与结果类型匹配。
‘llvm.experimental.cttz.elts
’ 内部函数¶
语法:¶
这是一个重载的内部函数。您可以在任何整数元素向量(包括固定宽度和可伸缩向量)上使用 `llvm.experimental.cttz.elts`
。
declare i8 @llvm.experimental.cttz.elts.i8.v8i1(<8 x i1> <src>, i1 <is_zero_poison>)
概述:¶
‘llvm.experimental.cttz.elts
’ 内部函数计算向量的尾随零元素的数量。
参数:¶
第一个参数是要计数的向量。此参数必须是具有整数元素类型的向量。返回类型也必须是足够宽的整数类型,以容纳源向量的最大元素数。如果返回类型对于输入向量中的元素数不够宽,则此内部函数的行为未定义。
第二个参数是一个常量标志,指示如果第一个参数全为零,则内部函数是否返回有效结果。如果第一个参数全为零且第二个参数为 true,则结果为 poison。
语义:¶
‘llvm.experimental.cttz.elts
’ 内部函数计算向量中尾随(最低有效位)零元素的数量。如果 src == 0
,则结果是输入向量中的元素数。
‘llvm.vector.splice
’ 内部函数¶
语法:¶
这是一个重载的内联函数。
declare <2 x double> @llvm.vector.splice.v2f64(<2 x double> %vec1, <2 x double> %vec2, i32 %imm)
declare <vscale x 4 x i32> @llvm.vector.splice.nxv4i32(<vscale x 4 x i32> %vec1, <vscale x 4 x i32> %vec2, i32 %imm)
概述:¶
‘llvm.vector.splice.*
’ 内部函数通过将第一个输入向量的元素与第二个输入向量的元素连接起来来构造一个向量,返回与输入向量类型相同的向量。带符号立即数,模向量中的元素数,是到第一个向量的索引,从中提取结果值。这意味着从概念上讲,对于正立即数,从索引 imm
开始从 concat(%vec1, %vec2)
中提取向量,而对于负立即数,它从第一个向量中提取 -imm
个尾随元素,并从 %vec2
中提取剩余元素。
这些内部函数适用于固定向量和可伸缩向量。虽然此内部函数支持所有向量类型,但对于固定宽度向量,表达此操作的推荐方法仍然是使用 shufflevector,因为这可能会带来更多优化机会。
例如
llvm.vector.splice(<A,B,C,D>, <E,F,G,H>, 1); ==> <B, C, D, E> index
llvm.vector.splice(<A,B,C,D>, <E,F,G,H>, -3); ==> <B, C, D, E> trailing elements
参数:¶
前两个操作数是类型相同的向量。起始索引是 imm 模源向量中运行时元素数。对于固定宽度向量 <N x eltty>,imm 是范围 -N <= imm < N 中的带符号整数常量。对于可伸缩向量 <vscale x N x eltty>,imm 是范围 -X <= imm < X 中的带符号整数常量,其中 X=vscale_range_min * N。
‘llvm.stepvector
’ 内部函数¶
这是一个重载的内部函数。您可以使用 llvm.stepvector
生成一个向量,其通道值包含线性序列 <0, 1, 2, …>。它主要用于可伸缩向量。
declare <vscale x 4 x i32> @llvm.stepvector.nxv4i32()
declare <vscale x 8 x i16> @llvm.stepvector.nxv8i16()
‘llvm.stepvector
’ 内部函数用于创建整数向量,其元素包含从 0 开始步长为 1 的线性序列。此内部函数只能用于元素大小至少为 8 位的整数向量。如果序列值超过元素类型允许的限制,则该通道的结果是 poison 值。
这些内在函数适用于固定宽度向量和可伸缩向量。虽然此内在函数支持所有向量类型,但对于固定宽度向量,建议的表达此操作的方式仍然是生成一个常量向量。
参数:¶
无。
‘llvm.experimental.get.vector.length
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.experimental.get.vector.length.i32(i32 %cnt, i32 immarg %vf, i1 immarg %scalable)
declare i32 @llvm.experimental.get.vector.length.i64(i64 %cnt, i32 immarg %vf, i1 immarg %scalable)
概述:¶
‘llvm.experimental.get.vector.length.*
’ 内在函数接受要处理的元素数量,并返回可以使用请求的向量化因子处理的元素数量。
参数:¶
第一个参数是任何标量整数类型的无符号值,指定要处理的元素总数。第二个参数是向量化因子的 i32 立即数。第三个参数指示向量化因子是否应乘以 vscale。
语义:¶
返回一个非负 i32 值(显式向量长度),该值在编译时未知,并且取决于硬件规格。如果结果值不适合结果类型,则结果是poison value(毒值)。
此内在函数旨在与 VP 内在函数一起用于循环向量化,以便获取每次循环迭代要处理的元素数量。结果应用于减少下一次迭代的计数,直到计数达到零。
设 %max_lanes
为由 %vf
和 %scalable
描述的类型中的通道数,以下是返回值的约束条件
如果
%cnt
等于 0,则返回 0。返回值始终小于或等于
%max_lanes
。如果
%cnt
为非零值,则返回值始终大于或等于ceil(%cnt / ceil(%cnt / %max_lanes))
。返回值在每次循环迭代中单调不增。也就是说,一次迭代的返回值至少与任何后续迭代的返回值一样大。
请注意,它具有以下含义
对于使用此内在函数的循环,迭代次数等于
ceil(%C / %max_lanes)
,其中%C
是初始%cnt
值。如果
%cnt
为非零值,则返回值也为非零值。如果
%cnt
小于或等于%max_lanes
,则返回值等于%cnt
。
‘llvm.experimental.vector.partial.reduce.add.*
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x i32> @llvm.experimental.vector.partial.reduce.add.v4i32.v4i32.v8i32(<4 x i32> %a, <8 x i32> %b)
declare <4 x i32> @llvm.experimental.vector.partial.reduce.add.v4i32.v4i32.v16i32(<4 x i32> %a, <16 x i32> %b)
declare <vscale x 4 x i32> @llvm.experimental.vector.partial.reduce.add.nxv4i32.nxv4i32.nxv8i32(<vscale x 4 x i32> %a, <vscale x 8 x i32> %b)
declare <vscale x 4 x i32> @llvm.experimental.vector.partial.reduce.add.nxv4i32.nxv4i32.nxv16i32(<vscale x 4 x i32> %a, <vscale x 16 x i32> %b)
概述:¶
‘llvm.vector.experimental.partial.reduce.add.*
’ 内在函数将两个向量参数的连接缩减为结果向量类型的元素数量。
参数:¶
第一个参数是与结果类型相同的整数向量。
第二个参数是一个向量,其长度是结果类型的已知整数倍,同时保持相同的元素类型。
语义:¶
除了归约运算符(例如,add)之外,连接的参数被归约的方式完全未指定。从本质上讲,这些内在函数预计不会单独使用,而是实现整体归约操作的第一阶段。
典型的用例是循环向量化,其中归约分为循环内阶段(其中保持无序向量结果对于性能很重要)和循环外阶段(用于计算最终标量结果)。
通过避免引入新的排序约束,这些内在函数增强了利用目标架构的累加指令的能力。
‘llvm.experimental.vector.histogram.*
’ 内在函数¶
这些内在函数是重载的。
这些内在函数表示类似直方图的操作;也就是说,更新内存中可能不连续的值,并且单个向量中的多个元素可能正在更新内存中的相同值。
更新操作必须在内在函数名称中指定。对于如下所示的简单直方图,将使用 add
操作。
void simple_histogram(int *restrict buckets, unsigned *indices, int N, int inc) {
for (int i = 0; i < N; ++i)
buckets[indices[i]] += inc;
}
未来可能会添加更多更新操作类型。
declare void @llvm.experimental.vector.histogram.add.v8p0.i32(<8 x ptr> %ptrs, i32 %inc, <8 x i1> %mask)
declare void @llvm.experimental.vector.histogram.add.nxv2p0.i64(<vscale x 2 x ptr> %ptrs, i64 %inc, <vscale x 2 x i1> %mask)
参数:¶
第一个参数是指向要更新的内存位置的指针向量。第二个参数是用于更新内存中的值的标量;它必须与要更新的值的类型匹配。最后一个参数是用于排除要修改的位置的掩码值。
语义:¶
‘llvm.experimental.vector.histogram.*
’ 内在函数用于对内存中可能重叠的值执行更新。这些内在函数表示以下操作序列
从
ptrs
操作数执行 Gather load(收集加载),元素类型与inc
操作数的元素类型匹配。更新从内存加载的值。对于
add
更新操作,这意味着对
ptrs
操作数执行跨向量直方图操作。将结果乘以
inc
操作数。将结果添加到从内存加载的值
将更新操作的结果 Scatter(分散)到来自
ptrs
操作数的内存位置。
mask
操作数将至少应用于 gather 和 scatter 操作。
‘llvm.experimental.vector.extract.last.active
’ 内在函数¶
这是一个重载的内联函数。
declare i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> %data, <4 x i1> %mask, i32 %passthru)
declare i16 @llvm.experimental.vector.extract.last.active.nxv8i16(<vscale x 8 x i16> %data, <vscale x 8 x i1> %mask, i16 %passthru)
参数:¶
第一个参数是要从中提取通道的数据向量。第二个参数是控制提取的掩码向量。第三个参数是 passthru 值(直通值)。
两个输入向量必须具有相同数量的元素,并且 passthru 值的类型必须与数据向量的元素类型匹配。
语义:¶
‘llvm.experimental.vector.extract.last.active
’ 内在函数将从数据向量中提取一个元素,该元素的索引与掩码向量的最高活动通道匹配。如果没有任何掩码通道处于活动状态,则返回 passthru 值。
‘llvm.experimental.vector.compress.*
’ 内在函数¶
LLVM 提供了一个内在函数,用于根据选择掩码压缩向量内的数据。在语义上,这类似于 llvm.masked.compressstore,但具有较弱的假设,并且不将结果存储到内存中,即数据保留在向量中。
语法:¶
这是一个重载的内在函数。从输入向量中收集一些整数、浮点或指针数据类型的标量值,并将它们相邻地放置在结果向量中。掩码定义要从向量中收集哪些元素。剩余的通道用来自 passthru
的值填充。
declare <8 x i32> @llvm.experimental.vector.compress.v8i32(<8 x i32> <value>, <8 x i1> <mask>, <8 x i32> <passthru>)
declare <16 x float> @llvm.experimental.vector.compress.v16f32(<16 x float> <value>, <16 x i1> <mask>, <16 x float> undef)
概述:¶
根据 mask
从输入向量 value
中选择元素。所有选定的元素都从低到高写入结果向量中的相邻通道。掩码为每个向量通道保存一个条目,并用于选择要保留的元素。如果给定了 passthru
向量,则所有剩余的通道都用来自 passthru
的相应通道的值填充。与 llvm.masked.compressstore 的主要区别在于,我们不需要为未选定的通道防范内存访问。这允许无分支代码和更好的优化,适用于所有不支持或具有 llvm.masked.compressstore 的显式语义的低效指令,但仍然具有某种形式的压缩操作的目标架构。结果向量可以以类似的效果写入,因为所有选定的值都位于向量的较低位置,但不需要分支来避免在掩码为 false
时进行写入。
参数:¶
第一个操作数是输入向量,从中选择元素。第二个操作数是掩码,一个布尔值向量。第三个操作数是 passthru 向量,从中将元素填充到剩余的通道中。掩码和输入向量必须具有相同数量的向量元素。输入向量和 passthru 向量必须具有相同的类型。
语义:¶
llvm.experimental.vector.compress
内在函数压缩向量内的数据。它从向量的可能非相邻通道中收集元素,并根据选择掩码将它们连续地放置在结果向量中,并用来自 passthru
的值填充剩余的通道。此内在函数执行以下 C++ 示例的逻辑。如果 passthru
未定义,则 out
中最后一个选定值之后的所有值都是未定义的。如果 mask
中的所有条目都为 0,则 out
向量为 passthru
。如果掩码的任何元素是 poison(毒值),则结果的所有元素都是 poison。否则,如果掩码的任何元素是 undef(未定义值),则结果的所有元素都是 undef。如果 passthru
是未定义的,则有效通道的数量等于掩码中 true
条目的数量,即所有通道 >= number-of-selected-values 都是未定义的。
// Consecutively place selected values in a vector.
using VecT __attribute__((vector_size(N))) = int;
VecT compress(VecT vec, VecT mask, VecT passthru) {
VecT out;
int idx = 0;
for (int i = 0; i < N / sizeof(int); ++i) {
out[idx] = vec[i];
idx += static_cast<bool>(mask[i]);
}
for (; idx < N / sizeof(int); ++idx) {
out[idx] = passthru[idx];
}
return out;
}
‘llvm.experimental.vector.match.*
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare <<n> x i1> @llvm.experimental.vector.match(<<n> x <ty>> %op1, <<m> x <ty>> %op2, <<n> x i1> %mask)
declare <vscale x <n> x i1> @llvm.experimental.vector.match(<vscale x <n> x <ty>> %op1, <<m> x <ty>> %op2, <vscale x <n> x i1> %mask)
概述:¶
查找第一个参数的活动元素,这些元素与第二个参数的任何元素匹配。
参数:¶
第一个参数是搜索向量,第二个参数是我们正在搜索的元素向量(即,我们认为匹配成功的元素),第三个参数是控制第一个参数的哪些元素处于活动状态的掩码。前两个参数必须是匹配的整数元素类型的向量。第一个和第三个参数以及结果类型必须具有匹配的元素计数(固定或可伸缩)。第二个参数必须是固定向量,但其长度可能与其余参数不同。
语义:¶
‘llvm.experimental.vector.match
’ 内在函数将第一个参数中的每个活动元素与第二个参数的元素进行比较,如果任何相等性比较成功,则在输出向量的相应元素中放置 1
,否则放置 0
。掩码中的非活动元素在输出中设置为 0
。
矩阵内在函数¶
矩阵操作,需要形状信息(如行数/列数或内存布局)可以使用矩阵内在函数来表达。这些内在函数需要将矩阵维度作为立即数参数传递,并且矩阵作为向量传递和返回。这意味着对于 R
x C
矩阵,列 j
的元素 i
位于相应向量中的索引 j * R + i
处,索引从 0 开始。目前假定为列优先布局。这些内在函数支持整数和浮点矩阵。
‘llvm.matrix.transpose.*
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare vectorty @llvm.matrix.transpose.*(vectorty %In, i32 <Rows>, i32 <Cols>)
概述:¶
‘llvm.matrix.transpose.*
’ 内在函数将 %In
视为 <Rows> x <Cols>
矩阵,并在结果向量中返回转置矩阵。
参数:¶
第一个参数 %In
是对应于 <Rows> x <Cols>
矩阵的向量。因此,参数 <Rows>
和 <Cols>
分别对应于行数和列数,并且必须是正的常数整数。返回的向量必须具有 <Rows> * <Cols>
个元素,并且具有与 %In
相同的浮点或整数元素类型。
‘llvm.matrix.multiply.*
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare vectorty @llvm.matrix.multiply.*(vectorty %A, vectorty %B, i32 <OuterRows>, i32 <Inner>, i32 <OuterColumns>)
概述:¶
‘llvm.matrix.multiply.*
’ 内在函数将 %A
视为 <OuterRows> x <Inner>
矩阵,%B
视为 <Inner> x <OuterColumns>
矩阵,并将它们相乘。结果矩阵在结果向量中返回。
参数:¶
第一个向量参数 %A
对应于具有 <OuterRows> * <Inner>
个元素的矩阵,第二个参数 %B
对应于具有 <Inner> * <OuterColumns>
个元素的矩阵。参数 <OuterRows>
、<Inner>
和 <OuterColumns>
必须是正的常数整数。返回的向量必须具有 <OuterRows> * <OuterColumns>
个元素。向量 %A
、%B
和返回的向量都具有相同的浮点或整数元素类型。
‘llvm.matrix.column.major.load.*
’ 内在函数¶
语法:¶
这是一个重载的内联函数。
declare vectorty @llvm.matrix.column.major.load.*(
ptrty %Ptr, i64 %Stride, i1 <IsVolatile>, i32 <Rows>, i32 <Cols>)
概述:¶
‘llvm.matrix.column.major.load.*
’ 内在函数使用步幅 %Stride
加载 <Rows> x <Cols>
矩阵,以计算不同列的起始地址。偏移量使用 %Stride
的位宽计算。这允许方便地加载子矩阵。如果 <IsVolatile>
为 true,则内在函数被视为volatile memory access(易失性内存访问)。结果矩阵在结果向量中返回。如果已知 %Ptr
参数与某个边界对齐,则可以在参数上指定此属性。
参数:¶
第一个参数 %Ptr
是指向返回的向量类型的指针类型,并且对应于要加载的起始地址。第二个参数 %Stride
是一个正的常数整数,其中 %Stride >= <Rows>
。%Stride
用于计算列内存地址。即,对于列 C
,其起始内存地址使用 %Ptr + C * %Stride
计算。第三个参数 <IsVolatile>
是一个布尔值。第四个和第五个参数 <Rows>
和 <Cols>
分别对应于行数和列数,并且必须是正的常数整数。返回的向量必须具有 <Rows> * <Cols>
个元素。
可以为 %Ptr
参数提供 align(对齐)参数属性。
‘llvm.matrix.column.major.store.*
’ 内在函数¶
语法:¶
declare void @llvm.matrix.column.major.store.*(
vectorty %In, ptrty %Ptr, i64 %Stride, i1 <IsVolatile>, i32 <Rows>, i32 <Cols>)
概述:¶
‘llvm.matrix.column.major.store.*
’ 内在函数使用列之间的步幅 %Stride
将 %In
中的 <Rows> x <Cols>
矩阵存储到内存中。偏移量使用 %Stride
的位宽计算。如果 <IsVolatile>
为 true,则内在函数被视为volatile memory access(易失性内存访问)。
如果已知 %Ptr
参数与某个边界对齐,则可以在参数上指定此属性。
参数:¶
第一个参数 %In
是一个向量,它对应于要存储到内存的 <Rows> x <Cols>
矩阵。第二个参数 %Ptr
是指向 %In
的向量类型的指针,并且是矩阵在内存中的起始地址。第三个参数 %Stride
是一个正的常数整数,其中 %Stride >= <Rows>
。%Stride
用于计算列内存地址。即,对于列 C
,其起始内存地址使用 %Ptr + C * %Stride
计算。第四个参数 <IsVolatile>
是一个布尔值。参数 <Rows>
和 <Cols>
分别对应于行数和列数,并且必须是正的常数整数。
可以为 %Ptr
参数提供 align(对齐)参数属性。
半精度浮点内在函数¶
对于大多数目标平台,半精度浮点是一种仅用于存储的格式。这意味着它是一种密集编码(在内存中),但不支持以该格式进行计算。
这意味着代码必须首先将半精度浮点值作为 i16 加载,然后使用 llvm.convert.from.fp16 将其转换为 float。然后可以在 float 值上执行计算(包括扩展到 double 等)。要将值存储回内存,首先将其转换为 float(如果需要),然后使用 llvm.convert.to.fp16 转换为 i16,然后作为 i16 值存储。
‘llvm.convert.to.fp16
’ 内在函数¶
语法:¶
declare i16 @llvm.convert.to.fp16.f32(float %a)
declare i16 @llvm.convert.to.fp16.f64(double %a)
概述:¶
‘llvm.convert.to.fp16
’ 内在函数执行从传统浮点类型到半精度浮点格式的转换。
参数:¶
此内在函数包含单个参数 - 要转换的值。
语义:¶
‘llvm.convert.to.fp16
’ 内在函数执行从传统浮点格式到半精度浮点格式的转换。返回值是一个 i16
,其中包含转换后的数字。
示例:¶
%res = call i16 @llvm.convert.to.fp16.f32(float %a)
store i16 %res, i16* @x, align 2
‘llvm.convert.from.fp16
’ 内在函数¶
语法:¶
declare float @llvm.convert.from.fp16.f32(i16 %a)
declare double @llvm.convert.from.fp16.f64(i16 %a)
概述:¶
‘llvm.convert.from.fp16
’ 内在函数执行从半精度浮点格式到单精度浮点格式的转换。
参数:¶
此内在函数包含单个参数 - 要转换的值。
语义:¶
‘llvm.convert.from.fp16
’ 内在函数执行从半单精度浮点格式到单精度浮点格式的转换。输入的半精度浮点值由 i16
值表示。
示例:¶
%a = load i16, ptr @x, align 2
%res = call float @llvm.convert.from.fp16(i16 %a)
饱和浮点到整数转换¶
fptoui
和 fptosi
指令在朝零舍入的值无法用结果类型表示时返回poison value(毒值)。这些内在函数提供了另一种转换方式,它将饱和到最小和最大的可表示整数值。
‘llvm.fptoui.sat.*
’ 内在函数¶
语法:¶
这是一个重载的内在函数。您可以在任何浮点参数类型和任何整数结果类型上使用 llvm.fptoui.sat
,或它们的向量。但是,并非所有目标架构都可能支持所有类型。
declare i32 @llvm.fptoui.sat.i32.f32(float %f)
declare i19 @llvm.fptoui.sat.i19.f64(double %f)
declare <4 x i100> @llvm.fptoui.sat.v4i100.v4f128(<4 x fp128> %f)
概述:¶
此内在函数使用饱和语义将参数转换为无符号整数。
参数:¶
参数可以是任何浮点类型或浮点类型的向量。返回值可以是任何整数类型或整数类型的向量。参数和返回值中的向量元素数量必须相同。
语义:¶
到整数的转换根据以下规则执行
如果参数是任何 NaN,则返回零。
如果参数小于零(包括负无穷大),则返回零。
如果参数大于结果类型的最大可表示无符号整数(包括正无穷大),则返回最大可表示无符号整数。
否则,返回将参数朝零舍入的结果。
示例:¶
%a = call i8 @llvm.fptoui.sat.i8.f32(float 123.875) ; yields i8: 123
%b = call i8 @llvm.fptoui.sat.i8.f32(float -5.75) ; yields i8: 0
%c = call i8 @llvm.fptoui.sat.i8.f32(float 377.0) ; yields i8: 255
%d = call i8 @llvm.fptoui.sat.i8.f32(float 0xFFF8000000000000) ; yields i8: 0
‘llvm.fptosi.sat.*
’ 内在函数¶
语法:¶
这是一个重载的内在函数。您可以在任何浮点参数类型和任何整数结果类型上使用 llvm.fptosi.sat
,或它们的向量。但是,并非所有目标架构都可能支持所有类型。
declare i32 @llvm.fptosi.sat.i32.f32(float %f)
declare i19 @llvm.fptosi.sat.i19.f64(double %f)
declare <4 x i100> @llvm.fptosi.sat.v4i100.v4f128(<4 x fp128> %f)
概述:¶
此内在函数使用饱和语义将参数转换为有符号整数。
参数:¶
参数可以是任何浮点类型或浮点类型的向量。返回值可以是任何整数类型或整数类型的向量。参数和返回值中的向量元素数量必须相同。
语义:¶
到整数的转换根据以下规则执行
如果参数是任何 NaN,则返回零。
如果参数小于结果类型的最小可表示有符号整数(包括负无穷大),则返回最小可表示有符号整数。
如果参数大于结果类型的最大可表示有符号整数(包括正无穷大),则返回最大可表示有符号整数。
否则,返回将参数朝零舍入的结果。
示例:¶
%a = call i8 @llvm.fptosi.sat.i8.f32(float 23.875) ; yields i8: 23
%b = call i8 @llvm.fptosi.sat.i8.f32(float -130.75) ; yields i8: -128
%c = call i8 @llvm.fptosi.sat.i8.f32(float 999.0) ; yields i8: 127
%d = call i8 @llvm.fptosi.sat.i8.f32(float 0xFFF8000000000000) ; yields i8: 0
收敛内在函数¶
用于控制 convergent
操作语义的 LLVM 收敛内在函数(所有这些内在函数都以 llvm.experimental.convergence.
前缀开头)在收敛操作语义文档中描述。
调试器内在函数¶
LLVM 调试器内建函数(均以 llvm.dbg.
前缀开头)在 LLVM 源代码级调试 文档中进行了描述。
异常处理内建函数¶
LLVM 异常处理内建函数(均以 llvm.eh.
前缀开头)在 LLVM 异常处理 文档中进行了描述。
指针认证内建函数¶
LLVM 指针认证内建函数(均以 llvm.ptrauth.
前缀开头)在 指针认证 文档中进行了描述。
蹦床内建函数¶
这些内建函数使得从函数中移除一个用 nest 属性标记的参数成为可能。结果是一个可调用的函数指针,缺少 nest 参数 - 调用者无需为其提供值。相反,要使用的值预先存储在“蹦床”中,这是一块通常在堆栈上分配的内存块,其中还包含将 nest 值拼接回参数列表的代码。这用于实现 GCC 嵌套函数地址扩展。
例如,如果函数是 i32 f(ptr nest %c, i32 %x, i32 %y)
,则生成的函数指针的签名为 i32 (i32, i32)
。它可以按如下方式创建
%tramp = alloca [10 x i8], align 4 ; size and alignment only correct for X86
call ptr @llvm.init.trampoline(ptr %tramp, ptr @f, ptr %nval)
%fp = call ptr @llvm.adjust.trampoline(ptr %tramp)
调用 %val = call i32 %fp(i32 %x, i32 %y)
等同于 %val = call i32 %f(ptr %nval, i32 %x, i32 %y)
。
‘llvm.init.trampoline
’ 内建函数¶
语法:¶
declare void @llvm.init.trampoline(ptr <tramp>, ptr <func>, ptr <nval>)
概述:¶
这会将可执行代码填充到 tramp
指向的内存中,将其转换为蹦床。
参数:¶
llvm.init.trampoline
内建函数接受三个参数,均为指针。tramp
参数必须指向足够大且对齐的内存块;此内存将由内建函数写入。请注意,大小和对齐方式是特定于目标的 - LLVM 目前没有提供可移植的方式来确定它们,因此生成此内建函数的前端需要具备一些特定于目标的知识。func
参数必须持有一个函数。
语义:¶
tramp
指向的内存块被填充了目标相关的代码,将其转换为一个函数。然后需要将 tramp
传递给 llvm.adjust.trampoline 以获得一个指针,该指针可以 位广播(转换为新函数)并调用。新函数的签名与 func
的签名相同,但移除了任何用 nest
属性标记的参数。最多允许一个这样的 nest
参数,并且它必须是指针类型。调用新函数等同于调用 func
并使用相同的参数列表,但将 nval
用于缺少的 nest
参数。如果在调用 llvm.init.trampoline
后,修改了 tramp
指向的内存,则任何后续对返回的函数指针的调用效果都是未定义的。
‘llvm.adjust.trampoline
’ 内建函数¶
语法:¶
declare ptr @llvm.adjust.trampoline(ptr <tramp>)
概述:¶
这对蹦床的地址(作为 tramp
传递)执行任何必需的机器特定调整。
参数:¶
tramp
必须指向一个内存块,该内存块已经通过先前对 llvm.init.trampoline 的调用填充了蹦床代码。
语义:¶
在某些架构上,要执行的代码地址需要与实际存储蹦床的地址不同。此内建函数在执行所需的机器特定调整后,返回与 tramp
对应的可执行地址。然后可以将返回的指针 位广播并执行。
向量谓词化内建函数¶
VP 内建函数旨在用于谓词化 SIMD/向量代码。典型的 VP 操作采用向量掩码和显式向量长度参数,如下所示:
<W x T> llvm.vp.<opcode>.*(<W x T> %x, <W x T> %y, <W x i1> %mask, i32 %evl)
向量掩码参数 (%mask) 始终具有 i1 类型的向量,例如 <32 x i1>。显式向量长度参数始终具有 i32 类型,并且是无符号整数值。显式向量长度参数 (%evl) 的范围是
0 <= %evl <= W, where W is the number of vector elements
请注意,对于 可伸缩向量类型,W
是向量的运行时长度。
如果 %evl > W
,则 VP 内建函数具有未定义的行为。显式向量长度 (%evl) 创建一个掩码 %EVLmask,其中所有元素 0 <= i < %evl
设置为 True,所有其他通道 %evl <= i < W
设置为 False。使用 %mask 和 %EVLmask 的按元素与运算计算新的掩码 %M
M = %mask AND %EVLmask
向量运算 <opcode>
在向量 A
和 B
上计算
A <opcode> B = { A[i] <opcode> B[i] M[i] = True, and
{ undef otherwise
优化提示¶
某些目标(例如 AVX512)在硬件中不支持 %evl 参数。对于这些目标,不鼓励使用有效的 %evl。TargetTransformInfo::hasActiveVectorLength()
函数在目标具有对 %evl 的原生支持时返回 true。
‘llvm.vp.select.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.select.v16i32 (<16 x i1> <condition>, <16 x i32> <on_true>, <16 x i32> <on_false>, i32 <evl>)
declare <vscale x 4 x i64> @llvm.vp.select.nxv4i64 (<vscale x 4 x i1> <condition>, <vscale x 4 x i64> <on_true>, <vscale x 4 x i64> <on_false>, i32 <evl>)
概述:¶
‘llvm.vp.select
’ 内建函数用于根据条件向量选择一个值,而无需 IR 级分支。
参数:¶
第一个参数是 i1
向量,指示条件。第二个参数是在条件向量为真时选择的值。第三个参数是在条件向量为假时选择的值。向量的大小必须相同。第四个参数是显式向量长度。
语义:¶
内建函数根据条件向量从第二个和第三个参数中选择通道。
位置大于或等于 %evl
的所有结果通道都是未定义的。对于所有低于 %evl
的通道,如果条件向量为真,则通道取自第二个参数。否则,通道取自第三个参数。
示例:¶
%r = call <4 x i32> @llvm.vp.select.v4i32(<4 x i1> %cond, <4 x i32> %on_true, <4 x i32> %on_false, i32 %evl)
;;; Expansion.
;; Any result is legal on lanes at and above %evl.
%also.r = select <4 x i1> %cond, <4 x i32> %on_true, <4 x i32> %on_false
‘llvm.vp.merge.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.merge.v16i32 (<16 x i1> <condition>, <16 x i32> <on_true>, <16 x i32> <on_false>, i32 <pivot>)
declare <vscale x 4 x i64> @llvm.vp.merge.nxv4i64 (<vscale x 4 x i1> <condition>, <vscale x 4 x i64> <on_true>, <vscale x 4 x i64> <on_false>, i32 <pivot>)
概述:¶
‘llvm.vp.merge
’ 内建函数用于根据条件向量和索引参数选择一个值,而无需 IR 级分支。
参数:¶
第一个参数是 i1
向量,指示条件。第二个参数是在条件向量为真时合并的值。第三个参数是在条件向量为假或通道位置大于或等于枢轴时选择的值。第四个参数是枢轴。
语义:¶
内建函数根据条件向量和枢轴值从第二个和第三个参数中选择通道。
对于条件向量为真且通道位置小于 %pivot
的所有通道,通道取自第二个参数。否则,通道取自第三个参数。
示例:¶
%r = call <4 x i32> @llvm.vp.merge.v4i32(<4 x i1> %cond, <4 x i32> %on_true, <4 x i32> %on_false, i32 %pivot)
;;; Expansion.
;; Lanes at and above %pivot are taken from %on_false
%atfirst = insertelement <4 x i32> poison, i32 %pivot, i32 0
%splat = shufflevector <4 x i32> %atfirst, <4 x i32> poison, <4 x i32> zeroinitializer
%pivotmask = icmp ult <4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> %splat
%mergemask = and <4 x i1> %cond, <4 x i1> %pivotmask
%also.r = select <4 x i1> %mergemask, <4 x i32> %on_true, <4 x i32> %on_false
‘llvm.vp.add.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.add.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.add.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.add.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数加法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.add
’ 内建函数在每个启用的通道上执行第一个和第二个向量参数的整数加法(add)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.add.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = add <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.sub.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.sub.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.sub.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.sub.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数减法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sub
’ 内建函数在每个启用的通道上执行第一个和第二个向量参数的整数减法(sub)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.sub.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = sub <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.mul.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.mul.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.mul.nxv46i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.mul.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数乘法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.mul
’ 内建函数在每个启用的通道上执行第一个和第二个向量参数的整数乘法(mul)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.mul.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = mul <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.sdiv.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.sdiv.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.sdiv.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.sdiv.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化有符号除法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sdiv
’ 内建函数在每个启用的通道上执行第一个和第二个向量参数的有符号除法(sdiv)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.sdiv.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = sdiv <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.udiv.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.udiv.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.udiv.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.udiv.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化无符号除法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.udiv
’ 内建函数在每个启用的通道上执行第一个和第二个向量参数的无符号除法(udiv)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.udiv.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = udiv <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.srem.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.srem.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.srem.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.srem.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化有符号余数计算。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.srem
’ 内建函数计算每个启用通道上第一个和第二个向量参数的有符号除法(srem)的余数。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.srem.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = srem <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.urem.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.urem.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.urem.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.urem.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化无符号余数计算。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.urem
’ 内建函数计算每个启用通道上第一个和第二个向量参数的无符号除法(urem)的余数。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.urem.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = urem <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.ashr.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.ashr.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.ashr.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.ashr.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化算术右移。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.ashr
’ 内建函数计算每个启用通道上第一个参数按第二个参数进行的算术右移(ashr)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.ashr.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = ashr <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.lshr.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.lshr.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.lshr.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.lshr.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化逻辑右移。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.lshr
’ 内建函数计算每个启用通道上第一个参数按第二个参数进行的逻辑右移(lshr)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.lshr.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = lshr <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.shl.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.shl.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.shl.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.shl.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化左移。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.shl
’ 内建函数计算每个启用通道上第一个参数按第二个参数进行的左移(shl)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.shl.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = shl <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.or.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.or.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.or.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.or.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化或运算。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.or
’ 内建函数在每个启用通道上执行前两个参数的按位或运算(or)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.or.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = or <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.and.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.and.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.and.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.and.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化与运算。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.and
’ 内建函数在每个启用通道上执行前两个参数的按位与运算(and)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.and.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = and <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.xor.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.xor.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.xor.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.xor.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量谓词化按位异或运算。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.xor
’ 内建函数在每个启用通道上执行前两个参数的按位异或运算(xor)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.xor.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = xor <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.abs.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.abs.v16i32 (<16 x i32> <op>, i1 <is_int_min_poison>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.abs.nxv4i32 (<vscale x 4 x i32> <op>, i1 <is_int_min_poison>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.abs.v256i64 (<256 x i64> <op>, i1 <is_int_min_poison>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的谓词化绝对值。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数必须是常量,并且是一个标志,指示如果第一个参数静态或动态地为 INT_MIN
值,则 ‘llvm.vp.abs
’ 内建函数的结果值是否为 poison value。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.abs
’ 内建函数计算每个启用通道上第一个参数的绝对值(abs)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.abs.v4i32(<4 x i32> %a, i1 false, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.abs.v4i32(<4 x i32> %a, i1 false)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.smax.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.smax.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.smax.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.smax.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数有符号最大值。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.smax
’ 内建函数在每个启用通道上执行第一个和第二个向量参数的整数有符号最大值(smax)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.smax.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.smax.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.smin.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.smin.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.smin.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.smin.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数有符号最小值。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.smin
’ 内建函数在每个启用通道上执行第一个和第二个向量参数的整数有符号最小值(smin)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.smin.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.smin.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.umax.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.umax.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.umax.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.umax.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数无符号最大值。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.umax
’ 内建函数在每个启用通道上执行第一个和第二个向量参数的整数无符号最大值(umax)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.umax.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.umax.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.umin.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.umin.v16i32 (<16 x i32> <left_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.umin.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.umin.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的谓词化整数无符号最小值。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.umin
’ 内建函数在每个启用通道上执行第一个和第二个向量参数的整数无符号最小值(umin)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.umin.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.umin.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.copysign.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.copysign.v16f32 (<16 x float> <mag_op>, <16 x float> <sign_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.copysign.nxv4f32 (<vscale x 4 x float> <mag_op>, <vscale x 4 x float> <sign_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.copysign.v256f64 (<256 x double> <mag_op>, <256 x double> <sign_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个浮点值向量的谓词化浮点数 copysign。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.copysign
’ 内建函数在每个启用通道上执行第一个和第二个向量参数的浮点数 copysign(copysign)。禁用通道上的结果是 poison value。该操作在默认浮点环境中执行。
示例:¶
%r = call <4 x float> @llvm.vp.copysign.v4f32(<4 x float> %mag, <4 x float> %sign, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.copysign.v4f32(<4 x float> %mag, <4 x float> %sign)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.minnum.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.minnum.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.minnum.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.minnum.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点 IEEE-754-2008 minNum 运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.minnum
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点最小值 (minnum) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.minnum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.minnum.v4f32(<4 x float> %a, <4 x float> %b)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.maxnum.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.maxnum.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.maxnum.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.maxnum.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点 IEEE-754-2008 maxNum 运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.maxnum
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点最大值 (maxnum) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.maxnum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.maxnum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.minimum.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.minimum.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.minimum.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.minimum.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点最小值运算,作用于两个浮点数值向量,传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.minimum
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点最小值 (minimum) 运算,如果任一参数为 NaN,则结果为 NaN。对于此 intrinsic,-0.0 被认为小于 +0.0。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.minimum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.minimum.v4f32(<4 x float> %a, <4 x float> %b)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.maximum.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.maximum.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.maximum.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.maximum.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点最大值运算,作用于两个浮点数值向量,传播 NaN 并将 -0.0 视为小于 +0.0。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.maximum
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点最大值 (maximum) 运算,如果任一参数为 NaN,则结果为 NaN。对于此 intrinsic,-0.0 被认为小于 +0.0。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.maximum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.maximum.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fadd.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fadd.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fadd.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fadd.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点加法运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fadd
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点加法 (fadd) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fadd.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fadd <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fsub.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fsub.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fsub.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fsub.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点减法运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fsub
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点减法 (fsub) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fsub.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fsub <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fmul.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fmul.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fmul.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fmul.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点乘法运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fmul
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点乘法 (fmul) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fmul.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fmul <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fdiv.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fdiv.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fdiv.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fdiv.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点除法运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fdiv
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点除法 (fdiv) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fdiv.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fdiv <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.frem.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.frem.v16f32 (<16 x float> <left_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.frem.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.frem.v256f64 (<256 x double> <left_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点余数运算,作用于两个浮点数值向量。
参数:¶
前两个参数和结果具有相同的浮点向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.frem
’ intrinsic 对每个启用的通道执行第一个和第二个向量参数的浮点余数 (frem) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.frem.v4f32(<4 x float> %a, <4 x float> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = frem <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fneg.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fneg.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fneg.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fneg.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点取反运算,作用于浮点数值向量。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fneg
’ intrinsic 对每个启用的通道执行第一个向量参数的浮点取反 (fneg) 运算。禁用通道上的结果是poison value。
示例:¶
%r = call <4 x float> @llvm.vp.fneg.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fneg <4 x float> %a
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fabs.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fabs.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fabs.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fabs.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点绝对值运算,作用于浮点数值向量。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fabs
’ intrinsic 对每个启用的通道执行第一个向量参数的浮点绝对值 (fabs) 运算。禁用通道上的结果是poison value。
示例:¶
%r = call <4 x float> @llvm.vp.fabs.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.fabs.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.sqrt.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.sqrt.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.sqrt.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.sqrt.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点平方根运算,作用于浮点数值向量。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sqrt
’ intrinsic 对每个启用的通道执行第一个向量参数的浮点平方根 (sqrt) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.sqrt.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.sqrt.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fma.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fma.v16f32 (<16 x float> <left_op>, <16 x float> <middle_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fma.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <middle_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fma.v256f64 (<256 x double> <left_op>, <256 x double> <middle_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点融合乘加运算,作用于两个浮点数值向量。
参数:¶
前三个参数和结果具有相同的浮点向量类型。第四个参数是向量掩码,其元素数量与结果向量类型相同。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fma
’ intrinsic 对每个启用的通道执行第一个、第二个和第三个向量参数的浮点融合乘加 (llvm.fma) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fma.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.fma(<4 x float> %a, <4 x float> %b, <4 x float> %c)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fmuladd.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fmuladd.v16f32 (<16 x float> <left_op>, <16 x float> <middle_op>, <16 x float> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.fmuladd.nxv4f32 (<vscale x 4 x float> <left_op>, <vscale x 4 x float> <middle_op>, <vscale x 4 x float> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.fmuladd.v256f64 (<256 x double> <left_op>, <256 x double> <middle_op>, <256 x double> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点乘加运算,作用于两个浮点数值向量。如果代码生成器确定 (a) 目标指令集支持融合操作,并且 (b) 融合操作比等效的、单独的乘法和加法指令对更有效,则可以融合此操作。
参数:¶
前三个参数和结果具有相同的浮点向量类型。第四个参数是向量掩码,其元素数量与结果向量类型相同。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fmuladd
’ intrinsic 对每个启用的通道执行第一个、第二个和第三个向量参数的浮点乘加 (llvm.fuladd) 运算。禁用通道上的结果是poison value。此操作在默认浮点环境下执行。
示例:¶
%r = call <4 x float> @llvm.vp.fmuladd.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.fmuladd(<4 x float> %a, <4 x float> %b, <4 x float> %c)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.reduce.add.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.add.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.add.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的整数 ADD
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.add
’ intrinsic 对每个启用的通道执行向量参数 val
的整数 ADD
归约 (llvm.vector.reduce.add) 运算,并将其加到标量 start_value
上。禁用通道被视为包含中性值 0
(即对归约操作没有影响)。如果向量长度为零,则结果等于 start_value
。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.add.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> zeroinitializer
%reduction = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %masked.a)
%also.r = add i32 %reduction, %start
‘llvm.vp.reduce.fadd.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fadd.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fadd.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点 ADD
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fadd
’ intrinsic 对每个启用的通道执行向量参数 val
的浮点 ADD
归约 (llvm.vector.reduce.fadd) 运算,并将其加到标量 start_value
上。禁用通道被视为包含中性值 -0.0
(即对归约操作没有影响)。如果未启用任何通道,则结果值将等于 start_value
。
要忽略起始值,可以使用中性值。
有关归约语义的更多详细信息,请参阅未断言版本 (llvm.vector.reduce.fadd)。
示例:¶
%r = call float @llvm.vp.reduce.fadd.v4f32(float %start, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float -0.0, float -0.0, float -0.0, float -0.0>
%also.r = call float @llvm.vector.reduce.fadd.v4f32(float %start, <4 x float> %masked.a)
‘llvm.vp.reduce.mul.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.mul.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.mul.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的整数 MUL
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.mul
’ intrinsic 对每个启用的通道执行向量参数 val
的整数 MUL
归约 (llvm.vector.reduce.mul) 运算,并将其乘以标量 start_value
。禁用通道被视为包含中性值 1
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.mul.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 1, i32 1, i32 1, i32 1>
%reduction = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> %masked.a)
%also.r = mul i32 %reduction, %start
‘llvm.vp.reduce.fmul.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fmul.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fmul.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的浮点 MUL
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fmul
’ intrinsic 对每个启用的通道执行向量参数 val
的浮点 MUL
归约 (llvm.vector.reduce.fmul) 运算,并将其乘以标量 start_value`。禁用通道被视为包含中性值 1.0
(即对归约操作没有影响)。如果未启用任何通道,则结果值将等于起始值。
要忽略起始值,可以使用中性值。
有关语义的更多详细信息,请参阅未断言版本 (llvm.vector.reduce.fmul)。
示例:¶
%r = call float @llvm.vp.reduce.fmul.v4f32(float %start, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float 1.0, float 1.0, float 1.0, float 1.0>
%also.r = call float @llvm.vector.reduce.fmul.v4f32(float %start, <4 x float> %masked.a)
‘llvm.vp.reduce.and.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.and.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.and.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的整数 AND
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.and
’ intrinsic 对每个启用的通道执行向量参数 val
的整数 AND
归约 (llvm.vector.reduce.and) 运算,并将其与标量 start_value
进行 ‘and
’ 运算。禁用通道被视为包含中性值 UINT_MAX
或 -1
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.and.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 -1, i32 -1, i32 -1, i32 -1>
%reduction = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> %masked.a)
%also.r = and i32 %reduction, %start
‘llvm.vp.reduce.or.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.or.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.or.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的整数 OR
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.or
’ intrinsic 对每个启用的通道执行向量参数 val
的整数 OR
归约 (llvm.vector.reduce.or) 运算,并将其与标量 start_value
进行 ‘or
’ 运算。禁用通道被视为包含中性值 0
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.or.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 0, i32 0, i32 0, i32 0>
%reduction = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> %masked.a)
%also.r = or i32 %reduction, %start
‘llvm.vp.reduce.xor.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.xor.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.xor.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的整数 XOR
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.xor
’ intrinsic 对每个启用的通道执行向量参数 val
的整数 XOR
归约 (llvm.vector.reduce.xor) 运算,并将其与标量 start_value
进行 ‘xor
’ 运算。禁用通道被视为包含中性值 0
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.xor.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 0, i32 0, i32 0, i32 0>
%reduction = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> %masked.a)
%also.r = xor i32 %reduction, %start
‘llvm.vp.reduce.smax.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.smax.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.smax.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的带符号整数 MAX
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.smax
’ intrinsic 对每个启用的通道执行向量参数 val
的带符号整数 MAX
归约 (llvm.vector.reduce.smax) 运算,并取其与标量 start_value
的最大值。禁用通道被视为包含中性值 INT_MIN
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i8 @llvm.vp.reduce.smax.v4i8(i8 %start, <4 x i8> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i8> %a, <4 x i8> <i8 -128, i8 -128, i8 -128, i8 -128>
%reduction = call i8 @llvm.vector.reduce.smax.v4i8(<4 x i8> %masked.a)
%also.r = call i8 @llvm.smax.i8(i8 %reduction, i8 %start)
‘llvm.vp.reduce.smin.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.smin.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.smin.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的带符号整数 MIN
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.smin
’ intrinsic 对每个启用的通道执行向量参数 val
的带符号整数 MIN
归约 (llvm.vector.reduce.smin) 运算,并取其与标量 start_value
的最小值。禁用通道被视为包含中性值 INT_MAX
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i8 @llvm.vp.reduce.smin.v4i8(i8 %start, <4 x i8> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i8> %a, <4 x i8> <i8 127, i8 127, i8 127, i8 127>
%reduction = call i8 @llvm.vector.reduce.smin.v4i8(<4 x i8> %masked.a)
%also.r = call i8 @llvm.smin.i8(i8 %reduction, i8 %start)
‘llvm.vp.reduce.umax.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.umax.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.umax.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的无符号整数 MAX
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.umax
’ intrinsic 对每个启用的通道执行向量参数 val
的无符号整数 MAX
归约 (llvm.vector.reduce.umax) 运算,并取其与标量 start_value
的最大值。禁用通道被视为包含中性值 0
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.umax.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 0, i32 0, i32 0, i32 0>
%reduction = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> %masked.a)
%also.r = call i32 @llvm.umax.i32(i32 %reduction, i32 %start)
‘llvm.vp.reduce.umin.*
’ Intrinsic¶
语法:¶
这是一个重载的内联函数。
declare i32 @llvm.vp.reduce.umin.v4i32(i32 <start_value>, <4 x i32> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare i16 @llvm.vp.reduce.umin.nxv8i16(i16 <start_value>, <vscale x 8 x i16> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
断言的无符号整数 MIN
归约运算,作用于向量和一个标量起始值,返回标量结果。
参数:¶
第一个参数是归约的起始值,它必须是标量整数类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是整数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.umin
’ 本征函数对每个启用的通道执行向量参数 val
的无符号整数 MIN
归约 (llvm.vector.reduce.umin),取其与标量 start_value
的最小值。禁用的通道被视为包含中性值 UINT_MAX
或 -1
(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
要忽略起始值,可以使用中性值。
示例:¶
%r = call i32 @llvm.vp.reduce.umin.v4i32(i32 %start, <4 x i32> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x i32> %a, <4 x i32> <i32 -1, i32 -1, i32 -1, i32 -1>
%reduction = call i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> %masked.a)
%also.r = call i32 @llvm.umin.i32(i32 %reduction, i32 %start)
‘llvm.vp.reduce.fmax.*
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fmax.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fmax.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
向量和标量起始值的谓词浮点 MAX
归约,将结果作为标量返回。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fmax
’ 本征函数对每个启用的通道执行向量参数 val
的浮点 MAX
归约 (llvm.vector.reduce.fmax),取其与标量 start_value
的最大值。禁用的通道被视为包含中性值(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
中性值取决于 fast-math flags。如果未设置任何标志,则中性值为 -QNAN
。如果 nnan
和 ninf
都已设置,则中性值是结果类型的最小浮点值。如果仅设置了 nnan
,则中性值为 -Infinity
。
此指令与 llvm.vector.reduce.fmax 本征函数(以及 ‘llvm.maxnum.*
’ 本征函数)具有相同的比较语义。
要忽略起始值,可以使用中性值。
示例:¶
%r = call float @llvm.vp.reduce.fmax.v4f32(float %float, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float QNAN, float QNAN, float QNAN, float QNAN>
%reduction = call float @llvm.vector.reduce.fmax.v4f32(<4 x float> %masked.a)
%also.r = call float @llvm.maxnum.f32(float %reduction, float %start)
‘llvm.vp.reduce.fmin.*
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fmin.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fmin.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
向量和标量起始值的谓词浮点 MIN
归约,将结果作为标量返回。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fmin
’ 本征函数对每个启用的通道执行向量参数 val
的浮点 MIN
归约 (llvm.vector.reduce.fmin),取其与标量 start_value
的最小值。禁用的通道被视为包含中性值(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
中性值取决于 fast-math flags。如果未设置任何标志,则中性值为 +QNAN
。如果 nnan
和 ninf
都已设置,则中性值是结果类型的最大浮点值。如果仅设置了 nnan
,则中性值为 +Infinity
。
此指令与 llvm.vector.reduce.fmin 本征函数(以及 ‘llvm.minnum.*
’ 本征函数)具有相同的比较语义。
要忽略起始值,可以使用中性值。
示例:¶
%r = call float @llvm.vp.reduce.fmin.v4f32(float %start, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float QNAN, float QNAN, float QNAN, float QNAN>
%reduction = call float @llvm.vector.reduce.fmin.v4f32(<4 x float> %masked.a)
%also.r = call float @llvm.minnum.f32(float %reduction, float %start)
‘llvm.vp.reduce.fmaximum.*
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fmaximum.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fmaximum.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
向量和标量起始值的谓词浮点 MAX
归约,将结果作为标量返回。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fmaximum
’ 本征函数对每个启用的通道执行向量参数 val
的浮点 MAX
归约 (llvm.vector.reduce.fmaximum),取其与标量 start_value
的最大值。禁用的通道被视为包含中性值(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
中性值取决于 fast-math flags。如果未设置任何标志或仅设置了 nnan
,则中性值为 -Infinity
。如果设置了 ninf
,则中性值是结果类型的最小浮点值。
此指令与 llvm.vector.reduce.fmaximum 本征函数(以及 ‘llvm.maximum.*
’ 本征函数)具有相同的比较语义。也就是说,除非向量或起始值中的任何元素为 NaN
,否则结果将始终是一个数字。即,此本征函数传播 NaN
。此外,-0.0 被认为小于 +0.0。
要忽略起始值,可以使用中性值。
示例:¶
%r = call float @llvm.vp.reduce.fmaximum.v4f32(float %float, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float -infinity, float -infinity, float -infinity, float -infinity>
%reduction = call float @llvm.vector.reduce.fmaximum.v4f32(<4 x float> %masked.a)
%also.r = call float @llvm.maximum.f32(float %reduction, float %start)
‘llvm.vp.reduce.fminimum.*
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare float @llvm.vp.reduce.fminimum.v4f32(float <start_value>, <4 x float> <val>, <4 x i1> <mask>, i32 <vector_length>)
declare double @llvm.vp.reduce.fminimum.nxv8f64(double <start_value>, <vscale x 8 x double> <val>, <vscale x 8 x i1> <mask>, i32 <vector_length>)
概述:¶
向量和标量起始值的谓词浮点 MIN
归约,将结果作为标量返回。
参数:¶
第一个参数是归约的起始值,它必须是标量浮点类型,且与结果类型相同。第二个参数是要执行归约的向量,它必须是浮点数值向量,其元素类型是结果/起始类型。第三个参数是向量掩码,它是一个布尔值向量,其元素数量与向量参数相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.reduce.fminimum
’ 本征函数对每个启用的通道执行向量参数 val
的浮点 MIN
归约 (llvm.vector.reduce.fminimum),取其与标量 start_value
的最小值。禁用的通道被视为包含中性值(即对归约操作没有影响)。如果向量长度为零,则结果为起始值。
中性值取决于 fast-math flags。如果未设置任何标志或仅设置了 nnan
,则中性值为 +Infinity
。如果设置了 ninf
,则中性值是结果类型的最大浮点值。
此指令与 llvm.vector.reduce.fminimum 本征函数(以及 ‘llvm.minimum.*
’ 本征函数)具有相同的比较语义。也就是说,除非向量或起始值中的任何元素为 NaN
,否则结果将始终是一个数字。即,此本征函数传播 NaN
。此外,-0.0 被认为小于 +0.0。
要忽略起始值,可以使用中性值。
示例:¶
%r = call float @llvm.vp.reduce.fminimum.v4f32(float %start, <4 x float> %a, <4 x i1> %mask, i32 %evl)
; %r is equivalent to %also.r, where lanes greater than or equal to %evl
; are treated as though %mask were false for those lanes.
%masked.a = select <4 x i1> %mask, <4 x float> %a, <4 x float> <float infinity, float infinity, float infinity, float infinity>
%reduction = call float @llvm.vector.reduce.fminimum.v4f32(<4 x float> %masked.a)
%also.r = call float @llvm.minimum.f32(float %reduction, float %start)
‘llvm.get.active.lane.mask.*
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x i1> @llvm.get.active.lane.mask.v4i1.i32(i32 %base, i32 %n)
declare <8 x i1> @llvm.get.active.lane.mask.v8i1.i64(i64 %base, i64 %n)
declare <16 x i1> @llvm.get.active.lane.mask.v16i1.i64(i64 %base, i64 %n)
declare <vscale x 16 x i1> @llvm.get.active.lane.mask.nxv16i1.i64(i64 %base, i64 %n)
概述:¶
创建表示活动和非活动向量通道的掩码。
参数:¶
两个参数都具有相同的标量整数类型。结果是具有 i1 元素类型的向量。
语义:¶
‘llvm.get.active.lane.mask.*
’ 本征函数在语义上等同于
%m[i] = icmp ult (%base + i), %n
其中 %m
是一个向量(掩码),表示活动/非活动通道,其元素由 i
索引,%base
、%n
是 llvm.get.active.lane.mask.*
的两个参数,%icmp
是整数比较,ult
是无符号小于比较运算符。 (%base + i)
中及其与 %n
的比较中不会发生溢出,因为它是在整数而不是机器数中执行的。如果 %n
为 0
,则结果为 poison 值。以上等同于
%m = @llvm.get.active.lane.mask(%base, %n)
例如,这可以由循环向量化器发出,在这种情况下,%base
是向量归纳变量 (VIV) 的第一个元素,%n
是循环迭代计数。因此,这些本征函数执行 VIV 与循环迭代计数的元素级小于比较,生成表示活动/非活动向量通道的 true/false 值掩码,除非 VIV 溢出,在这种情况下,它们在 VIV 溢出的通道中返回 false。参数是标量类型,以适应可伸缩向量类型,对于可伸缩向量类型,未知步进向量的类型需要是什么,才能在不溢出的情况下枚举其通道。
此掩码 %m
例如可以在掩码加载/存储指令中使用。这些本征函数为后端提供提示。即,对于向量循环,原始标量循环的回边采取计数显式地作为第二个参数。
示例:¶
%active.lane.mask = call <4 x i1> @llvm.get.active.lane.mask.v4i1.i64(i64 %elem0, i64 429)
%wide.masked.load = call <4 x i32> @llvm.masked.load.v4i32.p0v4i32(<4 x i32>* %3, i32 4, <4 x i1> %active.lane.mask, <4 x i32> poison)
‘llvm.experimental.vp.splice
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <2 x double> @llvm.experimental.vp.splice.v2f64(<2 x double> %vec1, <2 x double> %vec2, i32 %imm, <2 x i1> %mask, i32 %evl1, i32 %evl2)
declare <vscale x 4 x i32> @llvm.experimental.vp.splice.nxv4i32(<vscale x 4 x i32> %vec1, <vscale x 4 x i32> %vec2, i32 %imm, <vscale x 4 x i1> %mask, i32 %evl1, i32 %evl2)
概述:¶
‘llvm.experimental.vp.splice.*
’ 本征函数是 ‘llvm.vector.splice.*
’ 本征函数的向量长度谓词版本。
参数:¶
结果和前两个参数 vec1
和 vec2
是具有相同类型的向量。第三个参数 imm
是一个立即有符号整数,指示偏移索引。第四个参数 mask
是一个向量掩码,并且具有与结果相同数量的元素。最后两个参数 evl1
和 evl2
是无符号整数,分别指示 vec1
和 vec2
的显式向量长度。imm
、evl1
和 evl2
应遵守以下约束:-evl1 <= imm < evl1
,0 <= evl1 <= VL
和 0 <= evl2 <= VL
,其中 VL
是运行时向量因子。如果这些约束不满足,则本征函数具有未定义的行为。
语义:¶
实际上,此本征函数连接 vec1[0..evl1-1]
和 vec2[0..evl2-1]
,并通过选择大小为 evl2
的窗口中的元素来创建结果向量,该窗口从连接向量的索引 imm
(对于正立即数)开始。结果向量中超出 evl2
的元素是 undef
。如果 imm
为负数,则起始索引为 evl1 + imm
。活动向量长度为 evl2
的结果向量包含来自 vec1
的索引 [imm..evl1 - 1]
(对于负 imm
为 [evl1 + imm..evl1 -1]
)的 evl1 - imm
(负 imm
为 -imm
)元素,后跟来自 vec2
的前 evl2 - (evl1 - imm)
(负 imm
为 evl2 + imm
)个元素。如果 evl1 - imm
(-imm
) >= evl2
,则仅考虑前 evl2
个元素,其余元素为 undef
。结果向量中被 mask
禁用的通道是 poison
。
示例:¶
llvm.experimental.vp.splice(<A,B,C,D>, <E,F,G,H>, 1, 2, 3); ==> <B, E, F, poison> index
llvm.experimental.vp.splice(<A,B,C,D>, <E,F,G,H>, -2, 3, 2); ==> <B, C, poison, poison> trailing elements
‘llvm.experimental.vp.splat
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <2 x double> @llvm.experimental.vp.splat.v2f64(double %scalar, <2 x i1> %mask, i32 %evl)
declare <vscale x 4 x i32> @llvm.experimental.vp.splat.nxv4i32(i32 %scalar, <vscale x 4 x i1> %mask, i32 %evl)
概述:¶
‘llvm.experimental.vp.splat.*
’ 本征函数用于创建具有特定有效向量长度的谓词 splat。
参数:¶
结果是一个向量,它是第一个标量参数的 splat。第二个参数 mask
是一个向量掩码,并且具有与结果相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
此本征函数使用标量参数的 evl
个元素 splat 向量。结果向量中被 mask
禁用的通道是 poison
。超出 evl
的元素是 poison。
示例:¶
%r = call <4 x float> @llvm.vp.splat.v4f32(float %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%e = insertelement <4 x float> poison, float %a, i32 0
%s = shufflevector <4 x float> %e, <4 x float> poison, <4 x i32> zeroinitializer
%also.r = select <4 x i1> %mask, <4 x float> %s, <4 x float> poison
‘llvm.experimental.vp.reverse
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <2 x double> @llvm.experimental.vp.reverse.v2f64(<2 x double> %vec, <2 x i1> %mask, i32 %evl)
declare <vscale x 4 x i32> @llvm.experimental.vp.reverse.nxv4i32(<vscale x 4 x i32> %vec, <vscale x 4 x i1> %mask, i32 %evl)
概述:¶
‘llvm.experimental.vp.reverse.*
’ 本征函数是 ‘llvm.vector.reverse.*
’ 本征函数的向量长度谓词版本。
参数:¶
结果和第一个参数 vec
是具有相同类型的向量。第二个参数 mask
是一个向量掩码,并且具有与结果相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
此本征函数反转向量中前 evl
个元素的顺序。结果向量中被 mask
禁用的通道是 poison
。超出 evl
的元素是 poison。
‘llvm.vp.load
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x float> @llvm.vp.load.v4f32.p0(ptr %ptr, <4 x i1> %mask, i32 %evl)
declare <vscale x 2 x i16> @llvm.vp.load.nxv2i16.p0(ptr %ptr, <vscale x 2 x i1> %mask, i32 %evl)
declare <8 x float> @llvm.vp.load.v8f32.p1(ptr addrspace(1) %ptr, <8 x i1> %mask, i32 %evl)
declare <vscale x 1 x i64> @llvm.vp.load.nxv1i64.p6(ptr addrspace(6) %ptr, <vscale x 1 x i1> %mask, i32 %evl)
概述:¶
‘llvm.vp.load.*
’ 本征函数是 llvm.masked.load 本征函数的向量长度谓词版本。
参数:¶
第一个参数是加载的基指针。第二个参数是布尔值向量,其元素数量与返回类型相同。第三个参数是操作的显式向量长度。返回类型和基指针的底层类型是相同的向量类型。
可以为第一个参数提供 align 参数属性。
语义:¶
‘llvm.vp.load
’ 本征函数以与 ‘llvm.masked.load
’ 本征函数相同的方式从内存中读取向量,其中掩码取自 ‘mask
’ 和 ‘evl
’ 参数的组合,采用通常的 VP 方式。某些 ‘llvm.masked.load
’ 参数在 ‘llvm.vp.load
’ 中没有对应的参数:‘passthru
’ 参数隐式为 poison
;‘alignment
’ 参数被视为 align
参数属性(如果提供)。默认对齐方式被视为 datalayout string 指定的返回类型的 ABI 对齐方式。
示例:¶
%r = call <8 x i8> @llvm.vp.load.v8i8.p0(ptr align 2 %ptr, <8 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%also.r = call <8 x i8> @llvm.masked.load.v8i8.p0(ptr %ptr, i32 2, <8 x i1> %mask, <8 x i8> poison)
‘llvm.vp.store
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare void @llvm.vp.store.v4f32.p0(<4 x float> %val, ptr %ptr, <4 x i1> %mask, i32 %evl)
declare void @llvm.vp.store.nxv2i16.p0(<vscale x 2 x i16> %val, ptr %ptr, <vscale x 2 x i1> %mask, i32 %evl)
declare void @llvm.vp.store.v8f32.p1(<8 x float> %val, ptr addrspace(1) %ptr, <8 x i1> %mask, i32 %evl)
declare void @llvm.vp.store.nxv1i64.p6(<vscale x 1 x i64> %val, ptr addrspace(6) %ptr, <vscale x 1 x i1> %mask, i32 %evl)
概述:¶
‘llvm.vp.store.*
’ 本征函数是 llvm.masked.store 本征函数的向量长度谓词版本。
参数:¶
第一个参数是要写入内存的向量值。第二个参数是存储的基指针。它具有与 value 参数相同的底层类型。第三个参数是布尔值向量,其元素数量与返回类型相同。第四个参数是操作的显式向量长度。
可以为第二个参数提供 align 参数属性。
语义:¶
‘llvm.vp.store
’ 本征函数以与 ‘llvm.masked.store
’ 本征函数相同的方式从内存中读取向量,其中掩码取自 ‘mask
’ 和 ‘evl
’ 参数的组合,采用通常的 VP 方式。操作的对齐方式(对应于 ‘llvm.masked.store
’ 的 ‘alignment
’ 参数)由 align
参数属性指定(见上文)。如果未提供,则使用 datalayout string 指定的 ‘value
’ 参数类型的 ABI 对齐方式。
示例:¶
call void @llvm.vp.store.v8i8.p0(<8 x i8> %val, ptr align 4 %ptr, <8 x i1> %mask, i32 %evl)
;; For all lanes below %evl, the call above is lane-wise equivalent to the call below.
call void @llvm.masked.store.v8i8.p0(<8 x i8> %val, ptr %ptr, i32 4, <8 x i1> %mask)
‘llvm.experimental.vp.strided.load
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x float> @llvm.experimental.vp.strided.load.v4f32.i64(ptr %ptr, i64 %stride, <4 x i1> %mask, i32 %evl)
declare <vscale x 2 x i16> @llvm.experimental.vp.strided.load.nxv2i16.i64(ptr %ptr, i64 %stride, <vscale x 2 x i1> %mask, i32 %evl)
概述:¶
‘llvm.experimental.vp.strided.load
’ 本征函数将标量值从内存位置加载到向量中,这些内存位置以 ‘stride
’ 字节数均匀间隔,从 ‘ptr
’ 开始。
参数:¶
第一个参数是加载的基指针。第二个参数是以字节表示的步幅值。第三个参数是布尔值向量,其元素数量与返回类型相同。第四个参数是操作的显式向量长度。基指针底层类型与返回参数的标量元素类型匹配。
可以为第一个参数提供 align 参数属性。
语义:¶
‘llvm.experimental.vp.strided.load
’ 本征函数以与 llvm.vp.gather 本征函数相同的方式将多个标量值从内存加载到向量中,其中指针向量的形式为
%ptrs = <%ptr, %ptr + %stride, %ptr + 2 * %stride, ... >
,
其中 ‘ptr
’ 先前已强制转换为指针 ‘i8
’,‘stride
’ 始终解释为有符号整数,所有算术运算都在指针类型中进行。
示例:¶
%r = call <8 x i64> @llvm.experimental.vp.strided.load.v8i64.i64(i64* %ptr, i64 %stride, <8 x i64> %mask, i32 %evl)
;; The operation can also be expressed like this:
%addr = bitcast i64* %ptr to i8*
;; Create a vector of pointers %addrs in the form:
;; %addrs = <%addr, %addr + %stride, %addr + 2 * %stride, ...>
%ptrs = bitcast <8 x i8* > %addrs to <8 x i64* >
%also.r = call <8 x i64> @llvm.vp.gather.v8i64.v8p0i64(<8 x i64* > %ptrs, <8 x i64> %mask, i32 %evl)
‘llvm.experimental.vp.strided.store
’ 本征函数¶
语法:¶
这是一个重载的内联函数。
declare void @llvm.experimental.vp.strided.store.v4f32.i64(<4 x float> %val, ptr %ptr, i64 %stride, <4 x i1> %mask, i32 %evl)
declare void @llvm.experimental.vp.strided.store.nxv2i16.i64(<vscale x 2 x i16> %val, ptr %ptr, i64 %stride, <vscale x 2 x i1> %mask, i32 %evl)
概述:¶
‘@llvm.experimental.vp.strided.store
’ 本征函数将 ‘val
’ 的元素存储到内存位置中,这些内存位置以 ‘stride
’ 字节数均匀间隔,从 ‘ptr
’ 开始。
参数:¶
第一个参数是要写入内存的向量值。第二个参数是存储的基指针。它的底层类型与 value 参数的标量元素类型匹配。第三个参数是以字节表示的步幅值。第四个参数是布尔值向量,其元素数量与返回类型相同。第五个参数是操作的显式向量长度。
可以为第二个参数提供 align 参数属性。
语义:¶
‘llvm.experimental.vp.strided.store
’ 本征函数以与 llvm.vp.scatter 本征函数相同的方式存储 ‘val
’ 的元素,其中指针向量的形式为
%ptrs = <%ptr, %ptr + %stride, %ptr + 2 * %stride, ... >
,
其中 ‘ptr
’ 先前已强制转换为指针 ‘i8
’,‘stride
’ 始终解释为有符号整数,所有算术运算都在指针类型中进行。
示例:¶
call void @llvm.experimental.vp.strided.store.v8i64.i64(<8 x i64> %val, i64* %ptr, i64 %stride, <8 x i1> %mask, i32 %evl)
;; The operation can also be expressed like this:
%addr = bitcast i64* %ptr to i8*
;; Create a vector of pointers %addrs in the form:
;; %addrs = <%addr, %addr + %stride, %addr + 2 * %stride, ...>
%ptrs = bitcast <8 x i8* > %addrs to <8 x i64* >
call void @llvm.vp.scatter.v8i64.v8p0i64(<8 x i64> %val, <8 x i64*> %ptrs, <8 x i1> %mask, i32 %evl)
‘llvm.vp.gather
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <4 x double> @llvm.vp.gather.v4f64.v4p0(<4 x ptr> %ptrs, <4 x i1> %mask, i32 %evl)
declare <vscale x 2 x i8> @llvm.vp.gather.nxv2i8.nxv2p0(<vscale x 2 x ptr> %ptrs, <vscale x 2 x i1> %mask, i32 %evl)
declare <2 x float> @llvm.vp.gather.v2f32.v2p2(<2 x ptr addrspace(2)> %ptrs, <2 x i1> %mask, i32 %evl)
declare <vscale x 4 x i32> @llvm.vp.gather.nxv4i32.nxv4p4(<vscale x 4 x ptr addrspace(4)> %ptrs, <vscale x 4 x i1> %mask, i32 %evl)
概述:¶
‘llvm.vp.gather.*
’ 内建函数是 llvm.masked.gather 内建函数的向量长度谓词版本。
参数:¶
第一个参数是指针向量,其中包含要读取的所有内存地址。第二个参数是布尔值向量,其元素数量与返回类型相同。第三个参数是操作的显式向量长度。返回类型和指针向量的底层类型是相同的向量类型。
可以为第一个参数提供 align 参数属性。
语义:¶
‘llvm.vp.gather
’ 内建函数从内存中读取多个标量值,方式与 ‘llvm.masked.gather
’ 内建函数相同,其中掩码取自 ‘mask
’ 和 ‘evl
’ 参数的组合,采用通常的 VP 方式。某些 ‘llvm.masked.gather
’ 参数在 ‘llvm.vp.gather
’ 中没有对应的参数:‘passthru
’ 参数隐式为 poison
;‘alignment
’ 参数被视为 align
参数(如果提供)。默认对齐方式采用 datalayout string 指定的源地址的 ABI 对齐方式。
示例:¶
%r = call <8 x i8> @llvm.vp.gather.v8i8.v8p0(<8 x ptr> align 8 %ptrs, <8 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%also.r = call <8 x i8> @llvm.masked.gather.v8i8.v8p0(<8 x ptr> %ptrs, i32 8, <8 x i1> %mask, <8 x i8> poison)
‘llvm.vp.scatter
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare void @llvm.vp.scatter.v4f64.v4p0(<4 x double> %val, <4 x ptr> %ptrs, <4 x i1> %mask, i32 %evl)
declare void @llvm.vp.scatter.nxv2i8.nxv2p0(<vscale x 2 x i8> %val, <vscale x 2 x ptr> %ptrs, <vscale x 2 x i1> %mask, i32 %evl)
declare void @llvm.vp.scatter.v2f32.v2p2(<2 x float> %val, <2 x ptr addrspace(2)> %ptrs, <2 x i1> %mask, i32 %evl)
declare void @llvm.vp.scatter.nxv4i32.nxv4p4(<vscale x 4 x i32> %val, <vscale x 4 x ptr addrspace(4)> %ptrs, <vscale x 4 x i1> %mask, i32 %evl)
概述:¶
‘llvm.vp.scatter.*
’ 内建函数是 llvm.masked.scatter 内建函数的向量长度谓词版本。
参数:¶
第一个参数是要写入内存的向量值。第二个参数是指针向量,指向应存储值元素的位置。第三个参数是布尔值向量,其元素数量与返回类型相同。第四个参数是操作的显式向量长度。
可以为第二个参数提供 align 参数属性。
语义:¶
‘llvm.vp.scatter
’ 内建函数将多个标量值写入内存,方式与 ‘llvm.masked.scatter
’ 内建函数相同,其中掩码取自 ‘mask
’ 和 ‘evl
’ 参数的组合,采用通常的 VP 方式。‘llvm.masked.scatter
’ 的 ‘alignment
’ 参数在 ‘llvm.vp.scatter
’ 中没有对应的参数:它通过向量指针参数上的可选 align
参数属性提供。否则,它将采用 datalayout string 指定的目标地址的 ABI 对齐方式。
示例:¶
call void @llvm.vp.scatter.v8i8.v8p0(<8 x i8> %val, <8 x ptr> align 1 %ptrs, <8 x i1> %mask, i32 %evl)
;; For all lanes below %evl, the call above is lane-wise equivalent to the call below.
call void @llvm.masked.scatter.v8i8.v8p0(<8 x i8> %val, <8 x ptr> %ptrs, i32 1, <8 x i1> %mask)
‘llvm.vp.trunc.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i16> @llvm.vp.trunc.v16i16.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i16> @llvm.vp.trunc.nxv4i16.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.trunc
’ 内建函数将其第一个参数截断为返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.trunc
’ 内建函数接受要转换的值作为其第一个参数。返回类型是要将值转换成的类型。两种类型都必须是 integer 类型的向量。该值的位大小必须大于返回类型的位大小。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.trunc
’ 内建函数截断值中的高位,并将剩余位转换为返回类型。由于源大小必须大于目标大小,因此 ‘llvm.vp.trunc
’ 不能是空操作转换。它将始终截断位。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i16> @llvm.vp.trunc.v4i16.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = trunc <4 x i32> %a to <4 x i16>
%also.r = select <4 x i1> %mask, <4 x i16> %t, <4 x i16> poison
‘llvm.vp.zext.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.zext.v16i32.v16i16 (<16 x i16> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.zext.nxv4i32.nxv4i16 (<vscale x 4 x i16> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.zext
’ 内建函数将其第一个参数零扩展为返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.zext
’ 内建函数接受要转换的值作为其第一个参数。返回类型是要将值转换成的类型。两种类型都必须是 integer 类型的向量。该值的位大小必须小于返回类型的位大小。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.zext
’ 内建函数用零位填充值的高位,直到达到返回类型的大小。从 i1 进行零扩展时,结果将始终为 0 或 1。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i32> @llvm.vp.zext.v4i32.v4i16(<4 x i16> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = zext <4 x i16> %a to <4 x i32>
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.sext.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.sext.v16i32.v16i16 (<16 x i16> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.sext.nxv4i32.nxv4i16 (<vscale x 4 x i16> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.sext
’ 内建函数将其第一个参数符号扩展为返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.sext
’ 内建函数接受要转换的值作为其第一个参数。返回类型是要将值转换成的类型。两种类型都必须是 integer 类型的向量。该值的位大小必须小于返回类型的位大小。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sext
’ 内建函数通过复制值的符号位(最高位)执行符号扩展,直到达到返回类型的大小。从 i1 进行符号扩展时,结果将始终为 -1 或 0。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i32> @llvm.vp.sext.v4i32.v4i16(<4 x i16> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = sext <4 x i16> %a to <4 x i32>
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.fptrunc.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.fptrunc.v16f32.v16f64 (<16 x double> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.trunc.nxv4f32.nxv4f64 (<vscale x 4 x double> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.fptrunc
’ 内建函数将其第一个参数截断为返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.fptrunc
’ 内建函数接受要转换的值作为其第一个参数。返回类型是要将值转换成的类型。两种类型都必须是 floating-point 类型的向量。该值的位大小必须大于返回类型的位大小。这意味着 ‘llvm.vp.fptrunc
’ 不能用于进行空操作转换。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fptrunc
’ 内建函数将 value
从较大的 floating-point 类型转换为较小的 floating-point 类型。此指令假定在默认 floating-point environment 中执行。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x float> @llvm.vp.fptrunc.v4f32.v4f64(<4 x double> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fptrunc <4 x double> %a to <4 x float>
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.fpext.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x double> @llvm.vp.fpext.v16f64.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x double> @llvm.vp.fpext.nxv4f64.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.fpext
’ 内建函数将其第一个参数扩展为返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.fpext
’ 内建函数接受要转换的值作为其第一个参数。返回类型是要将值转换成的类型。两种类型都必须是 floating-point 类型的向量。该值的位大小必须小于返回类型的位大小。这意味着 ‘llvm.vp.fpext
’ 不能用于进行空操作转换。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fpext
’ 内建函数将 value
从较小的 floating-point 类型扩展为较大的 floating-point 类型。‘llvm.vp.fpext
’ 不能用于进行空操作转换,因为它始终会更改位。使用 bitcast
进行浮点转换的空操作转换。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x double> @llvm.vp.fpext.v4f64.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fpext <4 x float> %a to <4 x double>
%also.r = select <4 x i1> %mask, <4 x double> %t, <4 x double> poison
‘llvm.vp.fptoui.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.fptoui.v16i32.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.fptoui.nxv4i32.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.fptoui.v256i64.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.fptoui
’ 内建函数将 floating-point 参数转换为无符号整数返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.fptoui
’ 内建函数接受要转换的值作为其第一个参数。要转换的值必须是 floating-point 类型的向量。返回类型是要将值转换成的类型。返回类型必须是 integer 类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fptoui
’ 内建函数将其 floating-point 参数转换为最接近的(向零舍入)无符号整数值,其中通道位置低于显式向量长度且向量掩码为真。被掩码关闭的通道是 poison
。在启用转换发生的通道上,如果值无法容纳在返回类型中,则该通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.fptoui.v4i32.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fptoui <4 x float> %a to <4 x i32>
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.fptosi.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.fptosi.v16i32.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.fptosi.nxv4i32.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.fptosi.v256i64.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.fptosi
’ 内建函数将 floating-point 参数转换为有符号整数返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.fptosi
’ 内建函数接受要转换的值作为其第一个参数。要转换的值必须是 floating-point 类型的向量。返回类型是要将值转换成的类型。返回类型必须是 integer 类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fptosi
’ 内建函数将其 floating-point 参数转换为最接近的(向零舍入)有符号整数值,其中通道位置低于显式向量长度且向量掩码为真。被掩码关闭的通道是 poison
。在启用转换发生的通道上,如果值无法容纳在返回类型中,则该通道上的结果是 poison value。
示例:¶
%r = call <4 x i32> @llvm.vp.fptosi.v4i32.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fptosi <4 x float> %a to <4 x i32>
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.uitofp.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.uitofp.v16f32.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.uitofp.nxv4f32.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.uitofp.v256f64.v256i64 (<256 x i64> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.uitofp
’ 内建函数将其无符号整数参数转换为 floating-point 返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.uitofp
’ 内建函数接受要转换的值作为其第一个参数。要转换的值必须是 integer 类型的向量。返回类型是要将值转换成的类型。返回类型必须是 floating-point 类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.uitofp
’ 内建函数将其第一个参数解释为无符号整数,并将其转换为相应的浮点值。如果该值无法精确表示,则使用默认舍入模式进行舍入。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x float> @llvm.vp.uitofp.v4f32.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = uitofp <4 x i32> %a to <4 x float>
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.sitofp.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.sitofp.v16f32.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.sitofp.nxv4f32.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.sitofp.v256f64.v256i64 (<256 x i64> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.sitofp
’ 内建函数将其有符号整数参数转换为 floating-point 返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.sitofp
’ 内建函数接受要转换的值作为其第一个参数。要转换的值必须是 integer 类型的向量。返回类型是要将值转换成的类型。返回类型必须是 floating-point 类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sitofp
’ 内建函数将其第一个参数解释为有符号整数,并将其转换为相应的浮点值。如果该值无法精确表示,则使用默认舍入模式进行舍入。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x float> @llvm.vp.sitofp.v4f32.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = sitofp <4 x i32> %a to <4 x float>
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.ptrtoint.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i8> @llvm.vp.ptrtoint.v16i8.v16p0(<16 x ptr> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i8> @llvm.vp.ptrtoint.nxv4i8.nxv4p0(<vscale x 4 x ptr> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.ptrtoint.v16i64.v16p0(<256 x ptr> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.ptrtoint
’ 内建函数将其指针转换为整数返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.ptrtoint
’ 内建函数接受要转换的值作为其第一个参数,该参数必须是指针向量,以及要将其转换为返回类型的类型,该类型必须是 integer 类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.ptrtoint
’ 内建函数通过将指针值解释为整数,并截断或零扩展该值以适应整数类型的大小,从而将值转换为返回类型。如果 value
小于返回类型,则执行零扩展。如果 value
大于返回类型,则执行截断。如果它们的大小相同,则除了类型更改之外,不执行任何操作(空操作转换)。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i8> @llvm.vp.ptrtoint.v4i8.v4p0i32(<4 x ptr> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = ptrtoint <4 x ptr> %a to <4 x i8>
%also.r = select <4 x i1> %mask, <4 x i8> %t, <4 x i8> poison
‘llvm.vp.inttoptr.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x ptr> @llvm.vp.inttoptr.v16p0.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x ptr> @llvm.vp.inttoptr.nxv4p0.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x ptr> @llvm.vp.inttoptr.v256p0.v256i32 (<256 x i32> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.inttoptr
’ 内建函数将其整数值转换为指针返回类型。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.inttoptr
’ 内建函数接受要转换的值作为其第一个参数,该参数必须是 integer 类型的向量,以及要将其转换为返回类型的类型,该类型必须是指针类型的向量。第二个参数是向量掩码。返回类型、要转换的值和向量掩码具有相同数量的元素。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.inttoptr
’ 内建函数通过应用零扩展或截断(取决于整数 value
的大小),从而将 value
转换为返回类型。如果 value
大于指针的大小,则执行截断。如果 value
小于指针的大小,则执行零扩展。如果它们的大小相同,则不执行任何操作(空操作转换)。转换在显式向量长度以下的通道位置以及向量掩码为真的位置执行。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x ptr> @llvm.vp.inttoptr.v4p0i32.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = inttoptr <4 x i32> %a to <4 x ptr>
%also.r = select <4 x i1> %mask, <4 x ptr> %t, <4 x ptr> poison
‘llvm.vp.fcmp.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i1> @llvm.vp.fcmp.v16f32(<16 x float> <left_op>, <16 x float> <right_op>, metadata <condition code>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i1> @llvm.vp.fcmp.nxv4f32(<vscale x 4 x float> <left_op>, <vscale x 4 x float> <right_op>, metadata <condition code>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i1> @llvm.vp.fcmp.v256f64(<256 x double> <left_op>, <256 x double> <right_op>, metadata <condition code>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.fcmp
’ 内建函数基于其参数的比较返回布尔值向量。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.fcmp
’ 内建函数接受要比较的两个值作为其第一个和第二个参数。这两个值必须是 floating-point 类型的向量。返回类型是比较的结果。返回类型必须是 i1 类型的向量。第四个参数是向量掩码。返回类型、要比较的值和向量掩码具有相同数量的元素。第三个参数是条件代码,指示要执行的比较类型。它必须是元数据字符串,其中包含 受支持的浮点条件代码值之一。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fcmp
’ 根据作为第三个参数给出的条件代码比较其前两个参数。参数在每个启用的通道上逐元素进行比较,其中比较的语义 根据条件代码定义。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i1> @llvm.vp.fcmp.v4f32(<4 x float> %a, <4 x float> %b, metadata !"oeq", <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = fcmp oeq <4 x float> %a, %b
%also.r = select <4 x i1> %mask, <4 x i1> %t, <4 x i1> poison
‘llvm.vp.icmp.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <32 x i1> @llvm.vp.icmp.v32i32(<32 x i32> <left_op>, <32 x i32> <right_op>, metadata <condition code>, <32 x i1> <mask>, i32 <vector_length>)
declare <vscale x 2 x i1> @llvm.vp.icmp.nxv2i32(<vscale x 2 x i32> <left_op>, <vscale x 2 x i32> <right_op>, metadata <condition code>, <vscale x 2 x i1> <mask>, i32 <vector_length>)
declare <128 x i1> @llvm.vp.icmp.v128i8(<128 x i8> <left_op>, <128 x i8> <right_op>, metadata <condition code>, <128 x i1> <mask>, i32 <vector_length>)
概述:¶
‘llvm.vp.icmp
’ 内建函数基于其参数的比较返回布尔值向量。该操作具有掩码和显式向量长度参数。
参数:¶
‘llvm.vp.icmp
’ 内建函数接受要比较的两个值作为其第一个和第二个参数。这两个值必须是 integer 类型的向量。返回类型是比较的结果。返回类型必须是 i1 类型的向量。第四个参数是向量掩码。返回类型、要比较的值和向量掩码具有相同数量的元素。第三个参数是条件代码,指示要执行的比较类型。它必须是元数据字符串,其中包含 受支持的整数条件代码值之一。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.icmp
’ 根据作为第三个参数给出的条件代码比较其前两个参数。参数在每个启用的通道上逐元素进行比较,其中比较的语义 根据条件代码定义。被掩码关闭的通道是 poison
。
示例:¶
%r = call <4 x i1> @llvm.vp.icmp.v4i32(<4 x i32> %a, <4 x i32> %b, metadata !"ne", <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = icmp ne <4 x i32> %a, %b
%also.r = select <4 x i1> %mask, <4 x i1> %t, <4 x i1> poison
‘llvm.vp.ceil.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.ceil.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.ceil.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.ceil.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点值的谓词浮点 ceiling 运算。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.ceil
’ 内建函数对每个启用的通道上的第一个向量参数执行浮点 ceiling 运算 (ceil)。禁用通道上的结果是 poison value。
示例:¶
%r = call <4 x float> @llvm.vp.ceil.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.ceil.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.floor.*
’ 内建函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.floor.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.floor.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.floor.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点向下取整。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.floor
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点向下取整 (floor) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.floor.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.floor.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.rint.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.rint.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.rint.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.rint.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点最近整数舍入。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.rint
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点最近整数舍入 (rint) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.rint.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.rint.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.nearbyint.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.nearbyint.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.nearbyint.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.nearbyint.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点舍入到最近整数。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.nearbyint
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点舍入到最近整数 (nearbyint) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.nearbyint.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.nearbyint.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.round.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.round.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.round.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.round.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点四舍五入。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.round
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点四舍五入 (round) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.round.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.round.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.roundeven.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.roundeven.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.roundeven.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.roundeven.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点舍入到最接近的偶数。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.roundeven
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点舍入到最接近的偶数 (roundeven) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.roundeven.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.roundeven.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.roundtozero.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x float> @llvm.vp.roundtozero.v16f32 (<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x float> @llvm.vp.roundtozero.nxv4f32 (<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x double> @llvm.vp.roundtozero.v256f64 (<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言浮点向零舍入。
参数:¶
第一个参数和结果具有相同的浮点向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.roundtozero
’ 内联函数对每个启用的通道上的第一个向量参数执行浮点舍入到最接近的偶数 (llvm.trunc) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x float> @llvm.vp.roundtozero.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x float> @llvm.trunc.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x float> %t, <4 x float> poison
‘llvm.vp.lrint.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.lrint.v16i32.v16f32(<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.lrint.nxv4i32.nxv4f32(<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.lrint.v256i64.v256f64(<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言 lrint。
参数:¶
结果是一个整数向量,第一个参数是一个 浮点 类型的向量,其元素数量与结果向量类型相同。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.lrint
’ 内联函数对每个启用的通道上的第一个向量参数执行 lrint (lrint) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.lrint.v4i32.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.lrint.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.llrint.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.llrint.v16i32.v16f32(<16 x float> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.llrint.nxv4i32.nxv4f32(<vscale x 4 x float> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.llrint.v256i64.v256f64(<256 x double> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
向量浮点数值的断言 llrint。
参数:¶
结果是一个整数向量,第一个参数是一个 浮点 类型的向量,其元素数量与结果向量类型相同。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.llrint
’ 内联函数对每个启用的通道上的第一个向量参数执行 lrint (llrint) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.llrint.v4i32.v4f32(<4 x float> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.llrint.v4f32(<4 x float> %a)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.bitreverse.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.bitreverse.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.bitreverse.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.bitreverse.v256i64 (<256 x i64> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的断言位反转。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.bitreverse
’ 内联函数对每个启用通道上的第一个参数执行位反转 (bitreverse) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.bitreverse.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.bitreverse.v4i32(<4 x i32> %a)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.bswap.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.bswap.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.bswap.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.bswap.v256i64 (<256 x i64> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的断言字节交换。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.bswap
’ 内联函数对每个启用通道上的第一个参数执行字节交换 (bswap) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.bswap.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.bswap.v4i32(<4 x i32> %a)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.ctpop.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.ctpop.v16i32 (<16 x i32> <op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.ctpop.nxv4i32 (<vscale x 4 x i32> <op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.ctpop.v256i64 (<256 x i64> <op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的断言 ctpop。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数是向量掩码,其元素数量与结果向量类型相同。第三个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.ctpop
’ 内联函数对每个启用通道上的第一个参数执行 ctpop (ctpop) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.ctpop.v4i32(<4 x i32> %a, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.ctpop.v4i32(<4 x i32> %a)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.ctlz.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.ctlz.v16i32 (<16 x i32> <op>, i1 <is_zero_poison>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.ctlz.nxv4i32 (<vscale x 4 x i32> <op>, i1 <is_zero_poison>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.ctlz.v256i64 (<256 x i64> <op>, i1 <is_zero_poison>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的断言 ctlz。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数是一个常量标志,指示如果第一个参数为零,内联函数是否返回有效结果。第三个参数是向量掩码,其元素数量与结果向量类型相同。第四个参数是操作的显式向量长度。如果第一个参数为零且第二个参数为 true,则结果为 poison 值。
语义:¶
‘llvm.vp.ctlz
’ 内联函数对每个启用通道上的第一个参数执行 ctlz (ctlz) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.ctlz.v4i32(<4 x i32> %a, i1 false, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.ctlz.v4i32(<4 x i32> %a, i1 false)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.cttz.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.cttz.v16i32 (<16 x i32> <op>, i1 <is_zero_poison>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.cttz.nxv4i32 (<vscale x 4 x i32> <op>, i1 <is_zero_poison>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.cttz.v256i64 (<256 x i64> <op>, i1 <is_zero_poison>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
整数向量的断言 cttz。
参数:¶
第一个参数和结果具有相同的整数向量类型。第二个参数是一个常量标志,指示如果第一个参数为零,内联函数是否返回有效结果。第三个参数是向量掩码,其元素数量与结果向量类型相同。第四个参数是操作的显式向量长度。如果第一个参数为零且第二个参数为 true,则结果为 poison 值。
语义:¶
‘llvm.vp.cttz
’ 内联函数对每个启用通道上的第一个参数执行 cttz (cttz) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.cttz.v4i32(<4 x i32> %a, i1 false, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.cttz.v4i32(<4 x i32> %a, i1 false)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.cttz.elts.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。您可以在任何整数元素向量上使用 `llvm.vp.cttz.elts`
,包括固定宽度和可伸缩的向量。
declare i32 @llvm.vp.cttz.elts.i32.v16i32 (<16 x i32> <op>, i1 <is_zero_poison>, <16 x i1> <mask>, i32 <vector_length>)
declare i64 @llvm.vp.cttz.elts.i64.nxv4i32 (<vscale x 4 x i32> <op>, i1 <is_zero_poison>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare i64 @llvm.vp.cttz.elts.i64.v256i1 (<256 x i1> <op>, i1 <is_zero_poison>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
此 ‘`llvm.vp.cttz.elts`
’ 内联函数计算向量尾随零元素的数量。这基本上是 ‘`llvm.experimental.cttz.elts`
’ 的向量断言版本。
参数:¶
第一个参数是要计数的向量。此参数必须是具有整数元素类型的向量。返回类型也必须是足够宽的整数类型,以容纳源向量的最大元素数。如果返回类型对于输入向量中的元素数不够宽,则此内部函数的行为未定义。
第二个参数是一个常量标志,指示如果第一个参数全为零,内联函数是否返回有效结果。
第三个参数是向量掩码,其元素数量与输入向量类型相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.cttz.elts
’ 内联函数计算每个启用通道上的第一个参数中尾随(最低有效位 / 编号最小的)零元素。如果第一个参数全为零且第二个参数为 true,则结果为 poison 值。否则,它返回显式向量长度(即第四个参数)。
‘llvm.vp.sadd.sat.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.sadd.sat.v16i32 (<16 x i32> <left_op> <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.sadd.sat.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.sadd.sat.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的断言有符号饱和加法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.sadd.sat
’ 内联函数对每个启用通道上的第一个和第二个向量参数执行 sadd.sat (sadd.sat) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.sadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.uadd.sat.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.uadd.sat.v16i32 (<16 x i32> <left_op> <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.uadd.sat.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.uadd.sat.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的断言无符号饱和加法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.uadd.sat
’ 内联函数对每个启用通道上的第一个和第二个向量参数执行 uadd.sat (uadd.sat) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.uadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.ssub.sat.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.ssub.sat.v16i32 (<16 x i32> <left_op> <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.ssub.sat.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.ssub.sat.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的断言有符号饱和减法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.ssub.sat
’ 内联函数对每个启用通道上的第一个和第二个向量参数执行 ssub.sat (ssub.sat) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.ssub.sat.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.usub.sat.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.usub.sat.v16i32 (<16 x i32> <left_op> <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.usub.sat.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.usub.sat.v256i64 (<256 x i64> <left_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
两个整数向量的断言无符号饱和减法。
参数:¶
前两个参数和结果具有相同的整数向量类型。第三个参数是向量掩码,并且具有与结果向量类型相同数量的元素。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.usub.sat
’ 内联函数对每个启用通道上的第一个和第二个向量参数执行 usub.sat (usub.sat) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.usub.sat.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %a, <4 x i32> %b)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.fshl.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.fshl.v16i32 (<16 x i32> <left_op>, <16 x i32> <middle_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.fshl.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <middle_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.fshl.v256i64 (<256 x i64> <left_op>, <256 x i64> <middle_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
三个整数向量的断言循环移位左移。
参数:¶
前三个参数和结果具有相同的整数向量类型。第四个参数是向量掩码,其元素数量与结果向量类型相同。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fshl
’ 内联函数对每个启用通道上的第一个、第二个和第三个向量参数执行循环移位左移 (fshl) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.fshl.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.fshl.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.fshr.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <16 x i32> @llvm.vp.fshr.v16i32 (<16 x i32> <left_op>, <16 x i32> <middle_op>, <16 x i32> <right_op>, <16 x i1> <mask>, i32 <vector_length>)
declare <vscale x 4 x i32> @llvm.vp.fshr.nxv4i32 (<vscale x 4 x i32> <left_op>, <vscale x 4 x i32> <middle_op>, <vscale x 4 x i32> <right_op>, <vscale x 4 x i1> <mask>, i32 <vector_length>)
declare <256 x i64> @llvm.vp.fshr.v256i64 (<256 x i64> <left_op>, <256 x i64> <middle_op>, <256 x i64> <right_op>, <256 x i1> <mask>, i32 <vector_length>)
概述:¶
三个整数向量的断言循环移位右移。
参数:¶
前三个参数和结果具有相同的整数向量类型。第四个参数是向量掩码,其元素数量与结果向量类型相同。第五个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.fshr
’ 内联函数对每个启用通道上的第一个、第二个和第三个向量参数执行循环移位右移 (fshr) 操作。禁用通道上的结果是 poison 值。
示例:¶
%r = call <4 x i32> @llvm.vp.fshr.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c, <4 x i1> %mask, i32 %evl)
;; For all lanes below %evl, %r is lane-wise equivalent to %also.r
%t = call <4 x i32> @llvm.fshr.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c)
%also.r = select <4 x i1> %mask, <4 x i32> %t, <4 x i32> poison
‘llvm.vp.is.fpclass.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。
declare <vscale x 2 x i1> @llvm.vp.is.fpclass.nxv2f32(<vscale x 2 x float> <op>, i32 <test>, <vscale x 2 x i1> <mask>, i32 <vector_length>)
declare <2 x i1> @llvm.vp.is.fpclass.v2f16(<2 x half> <op>, i32 <test>, <2 x i1> <mask>, i32 <vector_length>)
概述:¶
断言 llvm.is.fpclass llvm.is.fpclass
参数:¶
第一个参数是一个浮点向量,结果类型是一个布尔向量,其元素数量与第一个参数相同。第二个参数指定要执行的测试 llvm.is.fpclass。第三个参数是向量掩码,其元素数量与结果向量类型相同。第四个参数是操作的显式向量长度。
语义:¶
‘llvm.vp.is.fpclass
’ 内联函数执行 llvm.is.fpclass (llvm.is.fpclass)。
示例:¶
%r = call <2 x i1> @llvm.vp.is.fpclass.v2f16(<2 x half> %x, i32 3, <2 x i1> %m, i32 %evl)
%t = call <vscale x 2 x i1> @llvm.vp.is.fpclass.nxv2f16(<vscale x 2 x half> %x, i32 3, <vscale x 2 x i1> %m, i32 %evl)
掩码向量加载和存储内联函数¶
LLVM 提供了用于断言向量加载和存储操作的内联函数。断言由掩码参数指定,掩码参数每个向量元素持有一位,用于开启或关闭关联的向量通道。不访问与“关闭”通道对应的内存地址。当掩码的所有位都开启时,内联函数与常规向量加载或存储相同。当所有位都关闭时,不访问任何内存。
‘llvm.masked.load.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。加载的数据是任何整数、浮点或指针数据类型的向量。
declare <16 x float> @llvm.masked.load.v16f32.p0(ptr <ptr>, i32 <alignment>, <16 x i1> <mask>, <16 x float> <passthru>)
declare <2 x double> @llvm.masked.load.v2f64.p0(ptr <ptr>, i32 <alignment>, <2 x i1> <mask>, <2 x double> <passthru>)
;; The data is a vector of pointers
declare <8 x ptr> @llvm.masked.load.v8p0.p0(ptr <ptr>, i32 <alignment>, <8 x i1> <mask>, <8 x ptr> <passthru>)
概述:¶
根据提供的掩码从内存中读取向量。掩码为每个向量通道保存一位,用于阻止对掩码关闭的通道进行内存访问。结果向量中掩码关闭的通道取自 ‘passthru
’ 参数的相应通道。
参数:¶
第一个参数是加载的基指针。第二个参数是源位置的对齐方式。它必须是 2 的幂的常量整数值。第三个参数 mask 是一个布尔值向量,其元素数量与返回类型相同。第四个是传递值,用于填充结果中掩码关闭的通道。返回类型、基指针的底层类型和 ‘passthru
’ 参数的类型是相同的向量类型。
语义:¶
‘llvm.masked.load
’ 内联函数旨在在单个 IR 操作中条件读取选定的向量元素。它对于支持向量掩码加载的目标非常有用,并允许在这些目标上向量化断言基本块。其他目标可能以不同的方式支持此内联函数,例如,通过将其降低为一系列分支,以保护标量加载操作。此操作的结果等效于常规向量加载指令,后跟加载值和传递值之间的 ‘select’,并以相同的掩码为条件,但掩码关闭的通道不被访问。只有向量的掩码开启的通道需要在分配的边界内(但所有这些通道都需要在同一分配的边界内)。特别是,使用此内联函数可以防止对掩码关闭的通道进行内存访问时发生异常。对于数据竞争或 noalias
约束,掩码关闭的通道也不被视为已访问。
%res = call <16 x float> @llvm.masked.load.v16f32.p0(ptr %ptr, i32 4, <16 x i1>%mask, <16 x float> %passthru)
;; The result of the two following instructions is identical aside from potential memory access exception
%loadlal = load <16 x float>, ptr %ptr, align 4
%res = select <16 x i1> %mask, <16 x float> %loadlal, <16 x float> %passthru
‘llvm.masked.store.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。存储在内存中的数据是任何整数、浮点或指针数据类型的向量。
declare void @llvm.masked.store.v8i32.p0 (<8 x i32> <value>, ptr <ptr>, i32 <alignment>, <8 x i1> <mask>)
declare void @llvm.masked.store.v16f32.p0(<16 x float> <value>, ptr <ptr>, i32 <alignment>, <16 x i1> <mask>)
;; The data is a vector of pointers
declare void @llvm.masked.store.v8p0.p0 (<8 x ptr> <value>, ptr <ptr>, i32 <alignment>, <8 x i1> <mask>)
概述:¶
根据提供的掩码将向量写入内存。掩码为每个向量通道保存一位,用于阻止对掩码关闭的通道进行内存访问。
参数:¶
第一个参数是要写入内存的向量值。第二个参数是存储的基指针,它与值参数具有相同的底层类型。第三个参数是目标位置的对齐方式。它必须是 2 的幂的常量整数值。第四个参数 mask 是一个布尔值向量。掩码和值参数的类型必须具有相同数量的向量元素。
语义:¶
‘llvm.masked.store
’ 内联函数旨在在单个 IR 操作中条件写入选定的向量元素。它对于支持向量掩码存储的目标非常有用,并允许在这些目标上向量化断言基本块。其他目标可能以不同的方式支持此内联函数,例如,通过将其降低为一系列分支,以保护标量存储操作。此操作的结果等效于加载-修改-存储序列,但掩码关闭的通道不被访问。只有向量的掩码开启的通道需要在分配的边界内(但所有这些通道都需要在同一分配的边界内)。特别是,使用此内联函数可以防止对掩码关闭的通道进行内存访问时发生异常。对于数据竞争或 noalias
约束,掩码关闭的通道也不被视为已访问。
call void @llvm.masked.store.v16f32.p0(<16 x float> %value, ptr %ptr, i32 4, <16 x i1> %mask)
;; The result of the following instructions is identical aside from potential data races and memory access exceptions
%oldval = load <16 x float>, ptr %ptr, align 4
%res = select <16 x i1> %mask, <16 x float> %value, <16 x float> %oldval
store <16 x float> %res, ptr %ptr, align 4
掩码向量收集和分散内联函数¶
LLVM 提供了用于向量收集和分散操作的内联函数。它们类似于 掩码向量加载和存储,但它们是为任意内存访问而设计的,而不是顺序内存访问。收集和分散也采用掩码参数,掩码参数每个向量元素持有一位,用于开启或关闭关联的向量通道。不访问与“关闭”通道对应的内存地址。当所有位都关闭时,不访问任何内存。
‘llvm.masked.gather.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。加载的数据是多个标量值,这些标量值是任何整数、浮点或指针数据类型,并收集到一个向量中。
declare <16 x float> @llvm.masked.gather.v16f32.v16p0(<16 x ptr> <ptrs>, i32 <alignment>, <16 x i1> <mask>, <16 x float> <passthru>)
declare <2 x double> @llvm.masked.gather.v2f64.v2p1(<2 x ptr addrspace(1)> <ptrs>, i32 <alignment>, <2 x i1> <mask>, <2 x double> <passthru>)
declare <8 x ptr> @llvm.masked.gather.v8p0.v8p0(<8 x ptr> <ptrs>, i32 <alignment>, <8 x i1> <mask>, <8 x ptr> <passthru>)
概述:¶
从任意内存位置读取标量值,并将它们收集到一个向量中。内存位置在指针向量 ‘ptrs
’ 中提供。根据提供的掩码访问内存。掩码为每个向量通道保存一位,用于阻止对掩码关闭的通道进行内存访问。结果向量中掩码关闭的通道取自 ‘passthru
’ 参数的相应通道。
参数:¶
第一个参数是指针向量,它保存要读取的所有内存地址。第二个参数是源地址的对齐方式。它必须是 0 或 2 的幂的常量整数值。第三个参数 mask 是一个布尔值向量,其元素数量与返回类型相同。第四个是传递值,用于填充结果中掩码关闭的通道。返回类型、指针向量的底层类型和 ‘passthru
’ 参数的类型是相同的向量类型。
语义:¶
‘llvm.masked.gather
’ 内联函数旨在在单个 IR 操作中条件读取来自任意内存位置的多个标量值。它对于支持向量掩码收集的目标非常有用,并允许向量化具有数据和控制发散的基本块。其他目标可能以不同的方式支持此内联函数,例如,通过将其降低为一系列标量加载操作。此操作的语义等效于一系列条件标量加载,随后将所有加载的值收集到一个向量中。掩码限制了对某些通道的内存访问,并有助于向量化断言基本块。
%res = call <4 x double> @llvm.masked.gather.v4f64.v4p0(<4 x ptr> %ptrs, i32 8, <4 x i1> <i1 true, i1 true, i1 true, i1 true>, <4 x double> poison)
;; The gather with all-true mask is equivalent to the following instruction sequence
%ptr0 = extractelement <4 x ptr> %ptrs, i32 0
%ptr1 = extractelement <4 x ptr> %ptrs, i32 1
%ptr2 = extractelement <4 x ptr> %ptrs, i32 2
%ptr3 = extractelement <4 x ptr> %ptrs, i32 3
%val0 = load double, ptr %ptr0, align 8
%val1 = load double, ptr %ptr1, align 8
%val2 = load double, ptr %ptr2, align 8
%val3 = load double, ptr %ptr3, align 8
%vec0 = insertelement <4 x double> poison, %val0, 0
%vec01 = insertelement <4 x double> %vec0, %val1, 1
%vec012 = insertelement <4 x double> %vec01, %val2, 2
%vec0123 = insertelement <4 x double> %vec012, %val3, 3
‘llvm.masked.scatter.*
’ 内联函数¶
语法:¶
这是一个重载的内联函数。存储在内存中的数据是任何整数、浮点或指针数据类型的向量。每个向量元素都存储在任意内存地址中。保证具有重叠地址的分散操作按从最低有效元素到最高有效元素的顺序执行。
declare void @llvm.masked.scatter.v8i32.v8p0 (<8 x i32> <value>, <8 x ptr> <ptrs>, i32 <alignment>, <8 x i1> <mask>)
declare void @llvm.masked.scatter.v16f32.v16p1(<16 x float> <value>, <16 x ptr addrspace(1)> <ptrs>, i32 <alignment>, <16 x i1> <mask>)
declare void @llvm.masked.scatter.v4p0.v4p0 (<4 x ptr> <value>, <4 x ptr> <ptrs>, i32 <alignment>, <4 x i1> <mask>)
概述:¶
将值向量中的每个元素写入到相应的内存地址。内存地址表示为指针向量。写入操作根据提供的掩码执行。掩码为每个向量通道保留一个位,用于阻止对被掩码通道的内存访问。
参数:¶
第一个参数是要写入内存的向量值。第二个参数是指针向量,指向应存储值元素的位置。它与值参数具有相同的底层类型。第三个参数是目标地址的对齐方式。它必须是 0 或 2 的幂次方的常数整数值。第四个参数,掩码,是一个布尔值向量。掩码和值参数的类型必须具有相同数量的向量元素。
语义:¶
‘llvm.masked.scatter
’ 内建函数旨在将选定的向量元素写入到单个 IR 操作中的任意内存地址。当并非所有掩码中的位都开启时,该操作可能是条件性的。它对于支持向量掩码散布的目标非常有用,并允许向量化具有数据和控制流发散的基本块。其他目标可能以不同的方式支持此内建函数,例如通过将其降低为一系列分支,这些分支保护标量存储操作。
;; This instruction unconditionally stores data vector in multiple addresses
call @llvm.masked.scatter.v8i32.v8p0(<8 x i32> %value, <8 x ptr> %ptrs, i32 4, <8 x i1> <true, true, .. true>)
;; It is equivalent to a list of scalar stores
%val0 = extractelement <8 x i32> %value, i32 0
%val1 = extractelement <8 x i32> %value, i32 1
..
%val7 = extractelement <8 x i32> %value, i32 7
%ptr0 = extractelement <8 x ptr> %ptrs, i32 0
%ptr1 = extractelement <8 x ptr> %ptrs, i32 1
..
%ptr7 = extractelement <8 x ptr> %ptrs, i32 7
;; Note: the order of the following stores is important when they overlap:
store i32 %val0, ptr %ptr0, align 4
store i32 %val1, ptr %ptr1, align 4
..
store i32 %val7, ptr %ptr7, align 4
掩码向量扩展加载和压缩存储内建函数¶
LLVM 提供了用于扩展加载和压缩存储操作的内建函数。根据掩码从向量中选择的数据存储在连续的内存地址中(压缩存储),反之亦然(扩展加载)。这些操作有效地映射到 “if (cond.i) a[j++] = v.i” 和 “if (cond.i) v.i = a[j++]” 模式。请注意,当掩码以 ‘1’ 位开头,后跟 ‘0’ 位时,这些操作与 llvm.masked.store 和 llvm.masked.load 相同。
‘llvm.masked.expandload.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。从连续的内存地址加载多个整数、浮点或指针数据类型的值,并根据掩码将其存储到向量的元素中。
declare <16 x float> @llvm.masked.expandload.v16f32 (ptr <ptr>, <16 x i1> <mask>, <16 x float> <passthru>)
declare <2 x i64> @llvm.masked.expandload.v2i64 (ptr <ptr>, <2 x i1> <mask>, <2 x i64> <passthru>)
概述:¶
从 ‘ptr
’ 中提供的内存位置顺序读取多个标量值,并将它们分散到一个向量中。‘mask
’ 为每个向量通道保留一个位。从内存读取的元素数量等于掩码中 ‘1’ 位的数量。加载的元素根据掩码中 ‘1’ 和 ‘0’ 位的顺序放置在目标向量中。例如,如果掩码向量是 ‘10010001’,“expandload” 从内存地址 ptr、ptr+1、ptr+2 读取 3 个值,并将它们分别放置在通道 0、3 和 7 中。被掩码通道由 ‘passthru
’ 参数的相应通道中的元素填充。
参数:¶
第一个参数是加载的基本指针。它与返回向量的元素具有相同的底层类型。第二个参数,掩码,是一个布尔值向量,其元素数量与返回类型相同。第三个参数是传递值,用于填充结果中被掩码的通道。返回类型和 ‘passthru
’ 参数的类型具有相同的向量类型。
可以为第一个参数提供 align 参数属性。指针对齐默认为 1。
语义:¶
‘llvm.masked.expandload
’ 内建函数旨在将多个标量值从相邻的内存地址读取到可能不相邻的向量通道中。它对于支持向量扩展加载的目标非常有用,并允许向量化具有跨迭代依赖关系的循环,如下例所示
// In this loop we load from B and spread the elements into array A.
double *A, B; int *C;
for (int i = 0; i < size; ++i) {
if (C[i] != 0)
A[i] = B[j++];
}
; Load several elements from array B and expand them in a vector.
; The number of loaded elements is equal to the number of '1' elements in the Mask.
%Tmp = call <8 x double> @llvm.masked.expandload.v8f64(ptr %Bptr, <8 x i1> %Mask, <8 x double> poison)
; Store the result in A
call void @llvm.masked.store.v8f64.p0(<8 x double> %Tmp, ptr %Aptr, i32 8, <8 x i1> %Mask)
; %Bptr should be increased on each iteration according to the number of '1' elements in the Mask.
%MaskI = bitcast <8 x i1> %Mask to i8
%MaskIPopcnt = call i8 @llvm.ctpop.i8(i8 %MaskI)
%MaskI64 = zext i8 %MaskIPopcnt to i64
%BNextInd = add i64 %BInd, %MaskI64
其他目标可能以不同的方式支持此内建函数,例如,通过将其降低为一系列条件标量加载操作和混洗。如果所有掩码元素都是 ‘1’,则内建函数的行为等同于常规的非掩码向量加载。
‘llvm.masked.compressstore.*
’ 内建函数¶
语法:¶
这是一个重载的内建函数。从输入向量中收集多个整数、浮点或指针数据类型的标量值,并将它们存储到相邻的内存地址中。掩码定义了要从向量中收集哪些元素。
declare void @llvm.masked.compressstore.v8i32 (<8 x i32> <value>, ptr <ptr>, <8 x i1> <mask>)
declare void @llvm.masked.compressstore.v16f32 (<16 x float> <value>, ptr <ptr>, <16 x i1> <mask>)
概述:¶
根据 ‘mask
’ 从输入向量 ‘value
’ 中选择元素。所有选定的元素都从较低地址到较高地址写入到以地址 ‘ptr’ 开头的相邻内存地址中。掩码为每个向量通道保留一个位,用于选择要存储的元素。要存储的元素数量等于掩码中活动位的数量。
参数:¶
第一个参数是输入向量,从中收集元素并写入内存。第二个参数是存储的基本指针,它与输入向量参数的元素具有相同的底层类型。第三个参数是掩码,一个布尔值向量。掩码和输入向量必须具有相同数量的向量元素。
可以为第二个参数提供 align 参数属性。指针对齐默认为 1。
语义:¶
‘llvm.masked.compressstore
’ 内建函数旨在压缩内存中的数据。它允许从向量的可能不相邻的通道中收集元素,并将它们连续地存储在一个 IR 操作中的内存中。它对于支持压缩存储操作的目标非常有用,并允许向量化具有跨迭代依赖关系的循环,如下例所示
// In this loop we load elements from A and store them consecutively in B
double *A, B; int *C;
for (int i = 0; i < size; ++i) {
if (C[i] != 0)
B[j++] = A[i]
}
; Load elements from A.
%Tmp = call <8 x double> @llvm.masked.load.v8f64.p0(ptr %Aptr, i32 8, <8 x i1> %Mask, <8 x double> poison)
; Store all selected elements consecutively in array B
call <void> @llvm.masked.compressstore.v8f64(<8 x double> %Tmp, ptr %Bptr, <8 x i1> %Mask)
; %Bptr should be increased on each iteration according to the number of '1' elements in the Mask.
%MaskI = bitcast <8 x i1> %Mask to i8
%MaskIPopcnt = call i8 @llvm.ctpop.i8(i8 %MaskI)
%MaskI64 = zext i8 %MaskIPopcnt to i64
%BNextInd = add i64 %BInd, %MaskI64
其他目标可能以不同的方式支持此内建函数,例如,通过将其降低为一系列分支,这些分支保护标量存储操作。
内存使用标记¶
此类内建函数提供有关已分配对象的生命周期和变量不可变的范围的信息。
‘llvm.lifetime.start
’ 内建函数¶
语法:¶
declare void @llvm.lifetime.start(i64 <size>, ptr captures(none) <ptr>)
概述:¶
‘llvm.lifetime.start
’ 内建函数指定内存对象的生命周期的开始。
参数:¶
第一个参数是一个常数整数,表示对象的大小;如果大小可变,则为 -1。第二个参数是指向该对象的指针。
语义:¶
如果 ptr
是栈分配的对象,并且它指向对象的第一个字节,则该对象最初被标记为死对象。如果优化管道中使用的栈着色算法无法得出 ptr
是栈分配的对象,则保守地认为 ptr
是非栈分配的对象。
在 ‘llvm.lifetime.start
’ 之后,ptr
指向的栈对象被标记为活动状态,并具有未初始化的值。当执行 llvm.lifetime.end 到 alloca 或函数返回时,栈对象被标记为死对象。
在调用 llvm.lifetime.end 后,可以再次对栈对象调用 ‘llvm.lifetime.start
’。第二次 ‘llvm.lifetime.start
’ 调用将对象标记为活动状态,但它不会更改对象的地址。
如果 ptr
是非栈分配的对象,它不指向对象的第一个字节,或者它是已处于活动状态的栈对象,则它只是用 poison
填充对象的所有字节。
‘llvm.lifetime.end
’ 内建函数¶
语法:¶
declare void @llvm.lifetime.end(i64 <size>, ptr captures(none) <ptr>)
概述:¶
‘llvm.lifetime.end
’ 内建函数指定 已分配对象的生命周期的结束。
参数:¶
第一个参数是一个常数整数,表示对象的大小;如果大小可变,则为 -1。第二个参数是指向该对象的指针。
语义:¶
如果 ptr
是栈分配的对象,并且它指向对象的第一个字节,则该对象是死对象。如果优化管道中使用的栈着色算法无法得出 ptr
是栈分配的对象,则保守地认为 ptr
是非栈分配的对象。
在已死的 alloca 上调用 llvm.lifetime.end
是空操作。
如果 ptr
是非栈分配的对象,或者它不指向对象的第一个字节,则它等效于简单地用 poison
填充对象的所有字节。
‘llvm.invariant.start
’ 内建函数¶
语法:¶
这是一个重载的内建函数。已分配对象可以属于任何地址空间。
declare ptr @llvm.invariant.start.p0(i64 <size>, ptr captures(none) <ptr>)
概述:¶
‘llvm.invariant.start
’ 内建函数指定 已分配对象的内容将不会更改。
参数:¶
第一个参数是一个常数整数,表示对象的大小;如果大小可变,则为 -1。第二个参数是指向该对象的指针。
语义:¶
此内建函数指示,在直到使用返回值的 llvm.invariant.end
之前,引用的内存位置是常量且不可更改的。
‘llvm.invariant.end
’ 内建函数¶
语法:¶
这是一个重载的内建函数。已分配对象可以属于任何地址空间。
declare void @llvm.invariant.end.p0(ptr <start>, i64 <size>, ptr captures(none) <ptr>)
概述:¶
‘llvm.invariant.end
’ 内建函数指定 已分配对象的内容是可变的。
参数:¶
第一个参数是匹配的 llvm.invariant.start
内建函数。第二个参数是一个常数整数,表示对象的大小;如果大小可变,则为 -1;第三个参数是指向该对象的指针。
语义:¶
此内建函数指示内存再次变为可变的。
‘llvm.launder.invariant.group
’ 内建函数¶
语法:¶
这是一个重载的内建函数。已分配对象可以属于任何地址空间。返回的指针必须与参数属于相同的地址空间。
declare ptr @llvm.launder.invariant.group.p0(ptr <ptr>)
概述:¶
当由 invariant.group
元数据建立的不变性不再成立时,可以使用 ‘llvm.launder.invariant.group
’ 内建函数来获取携带新的不变组信息的新的指针值。这是一个实验性内建函数,这意味着其语义在未来可能会发生变化。
参数:¶
llvm.launder.invariant.group
仅接受一个参数,即指向内存的指针。
语义:¶
返回另一个与其参数别名的指针,但为了 load
/store
invariant.group
元数据的目的,该指针被认为是不同的。它不读取任何可访问的内存,并且可以推测执行。
‘llvm.strip.invariant.group
’ 内建函数¶
语法:¶
这是一个重载的内建函数。已分配对象可以属于任何地址空间。返回的指针必须与参数属于相同的地址空间。
declare ptr @llvm.strip.invariant.group.p0(ptr <ptr>)
概述:¶
当由 invariant.group
元数据建立的不变性不再成立时,可以使用 ‘llvm.strip.invariant.group
’ 内建函数来获取不携带不变信息的新的指针值。这是一个实验性内建函数,这意味着其语义在未来可能会发生变化。
参数:¶
llvm.strip.invariant.group
仅接受一个参数,即指向内存的指针。
语义:¶
返回另一个与其参数别名的指针,但该指针没有关联的 invariant.group
元数据。它不读取任何内存,并且可以推测执行。
受约束的浮点内建函数¶
当需要对浮点运算进行特殊处理,例如特定的舍入模式或浮点异常行为时,可以使用这些内建函数。默认情况下,LLVM 优化过程假设舍入模式为四舍五入到最近,并且不会监控浮点异常。受约束的 FP 内建函数用于支持非默认的舍入模式,并准确地保留异常行为,而不会损害 LLVM 在使用默认行为时优化 FP 代码的能力。
如果函数中的任何 FP 运算是受约束的,则它们都必须是受约束的。这是正确 LLVM IR 所必需的。如果混合使用受约束和正常操作,则移动代码的优化可能会创建错误编译。混合使用受约束和较少约束的操作的正确方法是使用舍入模式和异常处理元数据来标记具有 LLVM 默认行为的受约束内建函数。
这些内建函数中的每一个都对应于一个正常的浮点运算。数据参数和返回值与相应的 FP 运算相同。
舍入模式参数是一个元数据字符串,指定优化器在转换常量值时可以做出的任何假设(如果有)。某些受约束的 FP 内建函数省略了此参数。如果内建函数需要此参数,则此参数必须是以下字符串之一
"round.dynamic"
"round.tonearest"
"round.downward"
"round.upward"
"round.towardzero"
"round.tonearestaway"
如果此参数是 “round.dynamic”,则优化过程必须假设舍入模式是未知的,并且可能在运行时更改。在这种情况下,不得执行任何依赖于舍入模式的转换。
舍入模式参数的其他可能值对应于类似命名的 IEEE 舍入模式。如果参数是这些值中的任何一个,则优化过程可以执行转换,只要它们与指定的舍入模式一致即可。
例如,如果舍入模式为 “round.downward” 或 “round.dynamic”,则 ‘x-0’->’x’ 不是有效的转换,因为如果 ‘x’ 的值为 +0,则向下舍入时 ‘x-0’ 应评估为 ‘-0’。但是,对于所有其他舍入模式,此转换都是合法的。
对于 “round.dynamic” 以外的值,优化过程可以假设实际的运行时舍入模式(在目标特定的方式中定义)与指定的舍入模式匹配,但这不能保证。使用与运行时实际舍入模式不匹配的特定非动态舍入模式会导致未定义的行为。
异常行为参数是一个元数据字符串,描述了内建函数所需的浮点异常语义。此参数必须是以下字符串之一
"fpexcept.ignore"
"fpexcept.maytrap"
"fpexcept.strict"
如果此参数是 “fpexcept.ignore”,则优化过程可以假设异常状态标志不会被读取,并且浮点异常将被屏蔽。这允许执行可能会更改原始代码的异常语义的转换。例如,在这种情况下,可以推测执行 FP 操作,而对于此参数的其他可能值,则不得这样做。
如果异常行为参数是 “fpexcept.maytrap”,则优化过程必须避免可能引发原始代码不会引发的异常的转换(例如,推测执行 FP 操作),但过程不需要保留原始代码隐含的所有异常。例如,异常可能被常量折叠潜在地隐藏。
如果异常行为参数是 “fpexcept.strict”,则所有转换都必须严格保留原始代码的浮点异常语义。原始代码会引发的任何 FP 异常都必须由转换后的代码引发,并且转换后的代码不得引发原始代码不会引发的任何 FP 异常。如果正在编译的代码读取 FP 异常状态标志,则将使用此异常行为参数,但此模式也可以与取消屏蔽 FP 异常的代码一起使用。
浮点异常的数量和顺序不保证。例如,一系列可能引发异常的 FP 操作可以向量化为单个指令,该指令每次引发每个唯一异常一次。
为了使受约束的内建函数正常工作,需要正确使用函数属性。
在使用受约束的浮点内建函数的函数中完成的所有函数调用都必须在调用指令或被调用函数的声明或定义上具有 strictfp
属性。
所有使用受约束的浮点内建函数的函数定义都必须具有 strictfp
属性。
‘llvm.experimental.constrained.fadd
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fadd(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fadd
’ 内建函数返回其两个参数的和。
参数:¶
‘llvm.experimental.constrained.fadd
’ 内建函数的前两个参数必须是浮点或浮点值向量。两个参数必须具有相同的类型。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的值是两个值参数的浮点和,并且与参数具有相同的类型。
‘llvm.experimental.constrained.fsub
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fsub(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fsub
’ 内建函数返回其两个参数的差。
参数:¶
‘llvm.experimental.constrained.fsub
’ 内建函数的前两个参数必须是浮点或浮点值向量。两个参数必须具有相同的类型。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的值是两个值参数的浮点差,并且与参数具有相同的类型。
‘llvm.experimental.constrained.fmul
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fmul(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fmul
’ 内建函数返回其两个参数的积。
参数:¶
‘llvm.experimental.constrained.fmul
’ 内建函数的前两个参数必须是浮点或浮点值向量。两个参数必须具有相同的类型。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的值是两个值参数的浮点积,并且与参数具有相同的类型。
‘llvm.experimental.constrained.fdiv
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fdiv(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fdiv
’ 内建函数返回其两个参数的商。
参数:¶
‘llvm.experimental.constrained.fdiv
’ 内建函数的前两个参数必须是浮点或浮点值向量。两个参数必须具有相同的类型。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的值是两个值参数的浮点商,并且与参数具有相同的类型。
‘llvm.experimental.constrained.frem
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.frem(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.frem
’ 内建函数返回其两个参数相除的余数。
参数:¶
‘llvm.experimental.constrained.frem
’ 内建函数的前两个参数必须是浮点或浮点值向量。两个参数必须具有相同的类型。
第三个和第四个参数指定舍入模式和异常行为,如上所述。舍入模式参数不起作用,因为 frem 的结果永远不会舍入,但为了与其他受约束的浮点内建函数保持一致,包含此参数。
语义:¶
产生的值是两个值参数相除的浮点余数,并且与参数具有相同的类型。余数与被除数具有相同的符号。
‘llvm.experimental.constrained.fma
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fma(<type> <op1>, <type> <op2>, <type> <op3>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fma
’ 内建函数返回对其参数执行融合乘加运算的结果。
参数:¶
‘llvm.experimental.constrained.fma
’ 内建函数的前三个参数必须是浮点或浮点值向量。所有参数必须具有相同的类型。
第四个和第五个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的结果是前两个参数的乘积与第三个参数的和,以无限精度计算,然后舍入到目标精度。
‘llvm.experimental.constrained.fptoui
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.fptoui(<type> <value>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fptoui
’ 内建函数将浮点数 value
转换为类型为 ty2
的无符号整数等价物。
参数:¶
‘llvm.experimental.constrained.fptoui
’ 内建函数的第一个参数必须是浮点数或浮点数向量。
第二个参数指定异常行为,如上所述。
语义:¶
产生的结果是从浮点参数转换而来的无符号整数。该值被截断,因此它是向零舍入的。
‘llvm.experimental.constrained.fptosi
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.fptosi(<type> <value>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fptosi
’ 内建函数将浮点数 value
转换为类型 ty2
。
参数:¶
‘llvm.experimental.constrained.fptosi
’ 内建函数的第一个参数必须是浮点数或浮点数向量。
第二个参数指定异常行为,如上所述。
语义:¶
产生的结果是从浮点参数转换而来的有符号整数。该值被截断,因此它是向零舍入的。
‘llvm.experimental.constrained.uitofp
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.uitofp(<type> <value>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.uitofp
’ 内建函数将无符号整数 value
转换为类型为 ty2
的浮点数。
参数:¶
‘llvm.experimental.constrained.uitofp
’ 内建函数的第一个参数必须是整数或整数向量。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
如果需要舍入,将引发非精确浮点异常。产生的任何结果都是从输入整数参数转换而来的浮点数值。
‘llvm.experimental.constrained.sitofp
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.sitofp(<type> <value>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.sitofp
’ 内建函数将有符号整数 value
转换为类型为 ty2
的浮点数。
参数:¶
‘llvm.experimental.constrained.sitofp
’ 内建函数的第一个参数必须是整数或整数向量。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
如果需要舍入,将引发非精确浮点异常。产生的任何结果都是从输入整数参数转换而来的浮点数值。
‘llvm.experimental.constrained.fptrunc
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.fptrunc(<type> <value>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fptrunc
’ 内建函数将 value
截断为类型 ty2
。
参数:¶
‘llvm.experimental.constrained.fptrunc
’ 内建函数的第一个参数必须是浮点数或浮点数向量。此参数的尺寸必须大于结果。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
产生的结果是一个浮点数值,它被截断为尺寸小于参数。
‘llvm.experimental.constrained.fpext
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.fpext(<type> <value>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fpext
’ 内建函数将浮点数 value
扩展为更大的浮点数值。
参数:¶
‘llvm.experimental.constrained.fpext
’ 内建函数的第一个参数必须是浮点数或浮点数向量。此参数的尺寸必须小于结果。
第二个参数指定异常行为,如上所述。
语义:¶
产生的结果是一个浮点数值,它被扩展为尺寸大于参数。应用于 fpext 指令的所有限制也适用于此内建函数。
‘llvm.experimental.constrained.fcmp
’ 和 ‘llvm.experimental.constrained.fcmps
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.experimental.constrained.fcmp(<type> <op1>, <type> <op2>,
metadata <condition code>,
metadata <exception behavior>)
declare <ty2>
@llvm.experimental.constrained.fcmps(<type> <op1>, <type> <op2>,
metadata <condition code>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fcmp
’ 和 ‘llvm.experimental.constrained.fcmps
’ 内建函数基于其参数的比较返回布尔值或布尔值向量。
如果参数是浮点标量,则结果类型为布尔值 (i1)。
如果参数是浮点向量,则结果类型是布尔值向量,其元素数量与被比较的参数相同。
‘llvm.experimental.constrained.fcmp
’ 内建函数执行静默比较操作,而 ‘llvm.experimental.constrained.fcmps
’ 内建函数执行信号比较操作。
参数:¶
‘llvm.experimental.constrained.fcmp
’ 和 ‘llvm.experimental.constrained.fcmps
’ 内建函数的前两个参数必须是浮点数或浮点数向量。两个参数必须具有相同的类型。
第三个参数是条件代码,指示要执行的比较类型。它必须是具有以下值之一的元数据字符串
“
oeq
”:有序且相等“
ogt
”:有序且大于“
oge
”:有序且大于或等于“
olt
”:有序且小于“
ole
”:有序且小于或等于“
one
”:有序且不相等“
ord
”:有序(无 NaN)“
ueq
”:无序或相等“
ugt
”:无序或大于“
uge
”:无序或大于或等于“
ult
”:无序或小于“
ule
”:无序或小于或等于“
une
”:无序或不相等“
uno
”:无序(任何一个是 NaN)
有序 表示两个参数都不是 NAN,而 无序 表示任何一个参数都可能是 NAN。
第四个参数指定异常行为,如上所述。
语义:¶
op1
和 op2
根据作为第三个参数给出的条件代码进行比较。如果参数是向量,则向量是逐元素比较的。每个执行的比较总是产生一个 i1 结果,如下所示
“
oeq
”:如果两个参数都不是 NAN 且op1
等于op2
,则产生true
。“
ogt
”:如果两个参数都不是 NAN 且op1
大于op2
,则产生true
。“
oge
”:如果两个参数都不是 NAN 且op1
大于或等于op2
,则产生true
。“
olt
”:如果两个参数都不是 NAN 且op1
小于op2
,则产生true
。“
ole
”:如果两个参数都不是 NAN 且op1
小于或等于op2
,则产生true
。“
one
”:如果两个参数都不是 NAN 且op1
不等于op2
,则产生true
。“
ord
”:如果两个参数都不是 NAN,则产生true
。“
ueq
”:如果任何一个参数是 NAN 或op1
等于op2
,则产生true
。“
ugt
”:如果任何一个参数是 NAN 或op1
大于op2
,则产生true
。“
uge
”:如果任何一个参数是 NAN 或op1
大于或等于op2
,则产生true
。“
ult
”:如果任何一个参数是 NAN 或op1
小于op2
,则产生true
。“
ule
”:如果任何一个参数是 NAN 或op1
小于或等于op2
,则产生true
。“
une
”:如果任何一个参数是 NAN 或op1
不等于op2
,则产生true
。“
uno
”:如果任何一个参数是 NAN,则产生true
。
‘llvm.experimental.constrained.fcmp
’ 执行的静默比较操作仅在任何一个参数是 SNAN 时才会引发异常。‘llvm.experimental.constrained.fcmps
’ 执行的信号比较操作将在任何一个参数是 NAN(QNAN 或 SNAN)时引发异常。这样的异常并不排除产生结果(例如,异常可能只设置一个标志),因此有序和无序比较之间的区别也与 ‘llvm.experimental.constrained.fcmps
’ 内建函数相关。
‘llvm.experimental.constrained.fmuladd
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.fmuladd(<type> <op1>, <type> <op2>,
<type> <op3>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.fmuladd
’ 内建函数表示乘加表达式,如果代码生成器确定 (a) 目标指令集支持融合操作,并且 (b) 融合操作比等效的、分离的 mul 和 add 指令对更有效率,则可以融合这些表达式。
参数:¶
‘llvm.experimental.constrained.fmuladd
’ 内建函数的前三个参数必须是浮点数或浮点数向量。所有三个参数必须具有相同的类型。
第四个和第五个参数指定舍入模式和异常行为,如上所述。
语义:¶
表达式
%0 = call float @llvm.experimental.constrained.fmuladd.f32(%a, %b, %c,
metadata <rounding mode>,
metadata <exception behavior>)
等效于表达式
%0 = call float @llvm.experimental.constrained.fmul.f32(%a, %b,
metadata <rounding mode>,
metadata <exception behavior>)
%1 = call float @llvm.experimental.constrained.fadd.f32(%0, %c,
metadata <rounding mode>,
metadata <exception behavior>)
除了在乘法和加法步骤之间是否执行舍入是未指定的。即使目标平台支持融合乘加,融合也不保证发生。如果需要融合乘加,则应使用相应的 llvm.experimental.constrained.fma 内建函数。这从不设置 errno,就像 ‘llvm.experimental.constrained.fma.*
’ 一样。
受约束的 libm 等效内建函数¶
除了上面描述的受约束内建函数所针对的基本浮点运算之外,还有各种操作的受约束版本,这些版本提供与相应的 libm 函数等效的行为。这些内建函数允许控制这些操作在舍入模式和异常行为方面的精确行为。
与基本受约束浮点内建函数一样,舍入模式和异常行为参数仅控制优化器的行为。它们不改变运行时浮点环境。
‘llvm.experimental.constrained.sqrt
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.sqrt(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.sqrt
’ 内建函数返回指定值的平方根,返回与 libm ‘sqrt
’ 函数相同的值,但不设置 errno
。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定值的非负平方根。如果该值小于负零,则会发生浮点异常,并且返回值是架构特定的。
‘llvm.experimental.constrained.pow
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.pow(<type> <op1>, <type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.pow
’ 内建函数返回第一个参数的(正或负)幂,该幂由第二个参数指定。
参数:¶
前两个参数和返回值是相同类型的浮点数。第二个参数指定第一个参数应提升到的幂。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回第一个值提升到第二个幂的结果,返回与 libm pow
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.powi
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.powi(<type> <op1>, i32 <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.powi
’ 内建函数返回第一个参数的(正或负)幂,该幂由第二个参数指定。乘法运算的求值顺序未定义。当使用浮点类型向量时,第二个参数仍然是标量整数值。
参数:¶
第一个参数和返回值是相同类型的浮点数。第二个参数是一个 32 位有符号整数,指定第一个参数应提升到的幂。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回第一个值提升到第二个幂的结果,其中包含未指定的舍入操作序列。
‘llvm.experimental.constrained.ldexp
’ 内建函数¶
语法:¶
declare <type0>
@llvm.experimental.constrained.ldexp(<type0> <op1>, <type1> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.ldexp
’ 执行 ldexp 函数。
参数:¶
第一个参数和返回值是 浮点 或 向量 相同类型的浮点值。 第二个参数是具有相同元素数量的整数。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数将第一个参数乘以 2 的第二个参数次方。 如果第一个参数是 NaN 或无穷大,则返回相同的值。 如果结果下溢,则返回具有相同符号的零。 如果结果溢出,则结果是具有相同符号的无穷大。
‘llvm.experimental.constrained.sin
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.sin(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.sin
’ 内建函数返回第一个参数的正弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定参数的正弦值,返回与 libm sin
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.cos
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.cos(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.cos
’ 内建函数返回第一个参数的余弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定参数的余弦值,返回与 libm cos
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.tan
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.tan(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.tan
’ 内建函数返回第一个参数的正切值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定参数的正切值,返回与 libm tan
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.asin
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.asin(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.asin
’ 内建函数返回第一个操作数的反正弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的反正弦值,返回与 libm asin
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.acos
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.acos(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.acos
’ 内建函数返回第一个操作数的反余弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的反余弦值,返回与 libm acos
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.atan
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.atan(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.atan
’ 内建函数返回第一个操作数的反正切值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的反正切值,返回与 libm atan
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.atan2
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.atan2(<type> <op1>,
<type> <op2>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.atan2
’ 内建函数返回 <op1>
除以 <op2>
的反正切值,并考虑象限。
参数:¶
前两个参数和返回值是相同类型的浮点数。
第三和第四个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数使用指定的操作数返回象限特定的反正切值,返回与 libm atan2
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.sinh
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.sinh(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.sinh
’ 内建函数返回第一个操作数的双曲正弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的双曲正弦值,返回与 libm sinh
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.cosh
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.cosh(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.cosh
’ 内建函数返回第一个操作数的双曲余弦值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的双曲余弦值,返回与 libm cosh
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.tanh
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.tanh(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.tanh
’ 内建函数返回第一个操作数的双曲正切值。
参数:¶
第一个参数和返回类型是相同类型的浮点数。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
语义:¶
此函数返回指定操作数的双曲正切值,返回与 libm tanh
函数相同的值,并以相同的方式处理错误条件。
‘llvm.experimental.constrained.exp
’ 内建函数¶
语法:¶
declare <type>
@llvm.experimental.constrained.exp(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
概述:¶
‘llvm.experimental.constrained.exp
’ 内建函数计算指定值的以 e 为底的指数。
参数:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm exp
functions would, and handles error conditions in the same way。
‘llvm.experimental.constrained.exp2
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.exp2(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.exp2
’ intrinsic computes the base-2 exponential of the specified value。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm exp2
functions would, and handles error conditions in the same way。
‘llvm.experimental.constrained.log
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.log(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.log
’ intrinsic computes the base-e logarithm of the specified value。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm log
functions would, and handles error conditions in the same way。
‘llvm.experimental.constrained.log10
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.log10(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.log10
’ intrinsic computes the base-10 logarithm of the specified value。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm log10
functions would, and handles error conditions in the same way。
‘llvm.experimental.constrained.log2
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.log2(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.log2
’ intrinsic computes the base-2 logarithm of the specified value。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm log2
functions would, and handles error conditions in the same way。
‘llvm.experimental.constrained.rint
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.rint(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.rint
’ intrinsic returns the first argument rounded to the nearest integer. It may raise an inexact floating-point exception if the argument is not an integer。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm rint
functions would, and handles error conditions in the same way. The rounding mode is described, not determined, by the rounding mode argument. The actual rounding mode is determined by the runtime floating-point environment. The rounding mode argument is only intended as information to the compiler。
‘llvm.experimental.constrained.lrint
’ Intrinsic¶
Syntax:¶
declare <inttype>
@llvm.experimental.constrained.lrint(<fptype> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.lrint
’ intrinsic returns the first argument rounded to the nearest integer. An inexact floating-point exception will be raised if the argument is not an integer. An invalid exception is raised if the result is too large to fit into a supported integer type, and in this case the result is undefined。
Arguments:¶
The first argument is a floating-point number. The return value is an integer type. Not all types are supported on all targets. The supported types are the same as the llvm.lrint
intrinsic and the lrint
libm functions。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm lrint
functions would, and handles error conditions in the same way。
The rounding mode is described, not determined, by the rounding mode argument. The actual rounding mode is determined by the runtime floating-point environment. The rounding mode argument is only intended as information to the compiler。
If the runtime floating-point environment is using the default rounding mode then the results will be the same as the llvm.lrint intrinsic。
‘llvm.experimental.constrained.llrint
’ Intrinsic¶
Syntax:¶
declare <inttype>
@llvm.experimental.constrained.llrint(<fptype> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.llrint
’ intrinsic returns the first argument rounded to the nearest integer. An inexact floating-point exception will be raised if the argument is not an integer. An invalid exception is raised if the result is too large to fit into a supported integer type, and in this case the result is undefined。
Arguments:¶
The first argument is a floating-point number. The return value is an integer type. Not all types are supported on all targets. The supported types are the same as the llvm.llrint
intrinsic and the llrint
libm functions。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm llrint
functions would, and handles error conditions in the same way。
The rounding mode is described, not determined, by the rounding mode argument. The actual rounding mode is determined by the runtime floating-point environment. The rounding mode argument is only intended as information to the compiler。
If the runtime floating-point environment is using the default rounding mode then the results will be the same as the llvm.llrint intrinsic。
‘llvm.experimental.constrained.nearbyint
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.nearbyint(<type> <op1>,
metadata <rounding mode>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.nearbyint
’ intrinsic returns the first argument rounded to the nearest integer. It will not raise an inexact floating-point exception if the argument is not an integer。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个和第三个参数指定舍入模式和异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm nearbyint
functions would, and handles error conditions in the same way. The rounding mode is described, not determined, by the rounding mode argument. The actual rounding mode is determined by the runtime floating-point environment. The rounding mode argument is only intended as information to the compiler。
‘llvm.experimental.constrained.maxnum
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.maxnum(<type> <op1>, <type> <op2>
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.maxnum
’ intrinsic returns the maximum of the two arguments。
Arguments:¶
前两个参数和返回值是相同类型的浮点数。
The third argument specifies the exception behavior as described above。
Semantics:¶
This function follows the IEEE-754-2008 semantics for maxNum。
‘llvm.experimental.constrained.minnum
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.minnum(<type> <op1>, <type> <op2>
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.minnum
’ intrinsic returns the minimum of the two arguments。
Arguments:¶
前两个参数和返回值是相同类型的浮点数。
The third argument specifies the exception behavior as described above。
Semantics:¶
This function follows the IEEE-754-2008 semantics for minNum。
‘llvm.experimental.constrained.maximum
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.maximum(<type> <op1>, <type> <op2>
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.maximum
’ intrinsic returns the maximum of the two arguments, propagating NaNs and treating -0.0 as less than +0.0。
Arguments:¶
前两个参数和返回值是相同类型的浮点数。
The third argument specifies the exception behavior as described above。
Semantics:¶
This function follows semantics specified in the draft of IEEE 754-2019。
‘llvm.experimental.constrained.minimum
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.minimum(<type> <op1>, <type> <op2>
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.minimum
’ intrinsic returns the minimum of the two arguments, propagating NaNs and treating -0.0 as less than +0.0。
Arguments:¶
前两个参数和返回值是相同类型的浮点数。
The third argument specifies the exception behavior as described above。
Semantics:¶
This function follows semantics specified in the draft of IEEE 754-2019。
‘llvm.experimental.constrained.ceil
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.ceil(<type> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.ceil
’ intrinsic returns the ceiling of the first argument。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm ceil
functions would and handles error conditions in the same way。
‘llvm.experimental.constrained.floor
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.floor(<type> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.floor
’ intrinsic returns the floor of the first argument。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm floor
functions would and handles error conditions in the same way。
‘llvm.experimental.constrained.round
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.round(<type> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.round
’ intrinsic returns the first argument rounded to the nearest integer。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm round
functions would and handles error conditions in the same way。
‘llvm.experimental.constrained.roundeven
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.roundeven(<type> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.roundeven
’ intrinsic returns the first argument rounded to the nearest integer in floating-point format, rounding halfway cases to even (that is, to the nearest value that is an even integer), regardless of the current rounding direction。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function implements IEEE-754 operation roundToIntegralTiesToEven
. It also behaves in the same way as C standard function roundeven
and can signal the invalid operation exception for a SNAN argument。
‘llvm.experimental.constrained.lround
’ Intrinsic¶
Syntax:¶
declare <inttype>
@llvm.experimental.constrained.lround(<fptype> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.lround
’ intrinsic returns the first argument rounded to the nearest integer with ties away from zero. It will raise an inexact floating-point exception if the argument is not an integer. An invalid exception is raised if the result is too large to fit into a supported integer type, and in this case the result is undefined。
Arguments:¶
The first argument is a floating-point number. The return value is an integer type. Not all types are supported on all targets. The supported types are the same as the llvm.lround
intrinsic and the lround
libm functions。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm lround
functions would and handles error conditions in the same way。
‘llvm.experimental.constrained.llround
’ Intrinsic¶
Syntax:¶
declare <inttype>
@llvm.experimental.constrained.llround(<fptype> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.llround
’ intrinsic returns the first argument rounded to the nearest integer with ties away from zero. It will raise an inexact floating-point exception if the argument is not an integer. An invalid exception is raised if the result is too large to fit into a supported integer type, and in this case the result is undefined。
Arguments:¶
The first argument is a floating-point number. The return value is an integer type. Not all types are supported on all targets. The supported types are the same as the llvm.llround
intrinsic and the llround
libm functions。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm llround
functions would and handles error conditions in the same way。
‘llvm.experimental.constrained.trunc
’ Intrinsic¶
Syntax:¶
declare <type>
@llvm.experimental.constrained.trunc(<type> <op1>,
metadata <exception behavior>)
Overview:¶
The ‘llvm.experimental.constrained.trunc
’ intrinsic returns the first argument rounded to the nearest integer not larger in magnitude than the argument。
Arguments:¶
The first argument and the return value are floating-point numbers of the same type。
第二个参数指定异常行为,如上所述。
Semantics:¶
This function returns the same values as the libm trunc
functions would and handles error conditions in the same way。
‘llvm.experimental.noalias.scope.decl
’ Intrinsic¶
Syntax:¶
declare void @llvm.experimental.noalias.scope.decl(metadata !id.scope.list)
Overview:¶
The llvm.experimental.noalias.scope.decl
intrinsic identifies where a noalias scope is declared. When the intrinsic is duplicated, a decision must also be made about the scope: depending on the reason of the duplication, the scope might need to be duplicated as well。
Arguments:¶
The !id.scope.list
argument is metadata that is a list of noalias
metadata references. The format is identical to that required for noalias
metadata. This list must have exactly one element。
Semantics:¶
The llvm.experimental.noalias.scope.decl
intrinsic identifies where a noalias scope is declared. When the intrinsic is duplicated, a decision must also be made about the scope: depending on the reason of the duplication, the scope might need to be duplicated as well。
For example, when the intrinsic is used inside a loop body, and that loop is unrolled, the associated noalias scope must also be duplicated. Otherwise, the noalias property it signifies would spill across loop iterations, whereas it was only valid within a single iteration。
; This examples shows two possible positions for noalias.decl and how they impact the semantics:
; If it is outside the loop (Version 1), then %a and %b are noalias across *all* iterations.
; If it is inside the loop (Version 2), then %a and %b are noalias only within *one* iteration.
declare void @decl_in_loop(ptr %a.base, ptr %b.base) {
entry:
; call void @llvm.experimental.noalias.scope.decl(metadata !2) ; Version 1: noalias decl outside loop
br label %loop
loop:
%a = phi ptr [ %a.base, %entry ], [ %a.inc, %loop ]
%b = phi ptr [ %b.base, %entry ], [ %b.inc, %loop ]
; call void @llvm.experimental.noalias.scope.decl(metadata !2) ; Version 2: noalias decl inside loop
%val = load i8, ptr %a, !alias.scope !2
store i8 %val, ptr %b, !noalias !2
%a.inc = getelementptr inbounds i8, ptr %a, i64 1
%b.inc = getelementptr inbounds i8, ptr %b, i64 1
%cond = call i1 @cond()
br i1 %cond, label %loop, label %exit
exit:
ret void
}
!0 = !{!0} ; domain
!1 = !{!1, !0} ; scope
!2 = !{!1} ; scope list
Multiple calls to @llvm.experimental.noalias.scope.decl for the same scope are possible, but one should never dominate another. Violations are pointed out by the verifier as they indicate a problem in either a transformation pass or the input。
Floating Point Environment Manipulation intrinsics¶
These functions read or write floating point environment, such as rounding mode or state of floating point exceptions. Altering the floating point environment requires special care. See Floating Point Environment。
‘llvm.get.rounding
’ Intrinsic¶
Syntax:¶
declare i32 @llvm.get.rounding()
Overview:¶
The ‘llvm.get.rounding
’ intrinsic reads the current rounding mode。
Semantics:¶
The ‘llvm.get.rounding
’ intrinsic returns the current rounding mode. Encoding of the returned values is same as the result of FLT_ROUNDS
, specified by C standard
0 - toward zero
1 - to nearest, ties to even
2 - toward positive infinity
3 - toward negative infinity
4 - to nearest, ties away from zero
Other values may be used to represent additional rounding modes, supported by a target. These values are target-specific。
‘llvm.set.rounding
’ Intrinsic¶
Syntax:¶
declare void @llvm.set.rounding(i32 <val>)
Overview:¶
The ‘llvm.set.rounding
’ intrinsic sets current rounding mode。
Arguments:¶
The argument is the required rounding mode. Encoding of rounding mode is the same as used by ‘llvm.get.rounding
’。
Semantics:¶
The ‘llvm.set.rounding
’ intrinsic sets the current rounding mode. It is similar to C library function ‘fesetround’, however this intrinsic does not return any value and uses platform-independent representation of IEEE rounding modes。
‘llvm.get.fpenv
’ Intrinsic¶
Syntax:¶
declare <integer_type> @llvm.get.fpenv()
Overview:¶
The ‘llvm.get.fpenv
’ intrinsic returns bits of the current floating-point environment. The return value type is platform-specific。
Semantics:¶
The ‘llvm.get.fpenv
’ intrinsic reads the current floating-point environment and returns it as an integer value。
‘llvm.set.fpenv
’ Intrinsic¶
Syntax:¶
declare void @llvm.set.fpenv(<integer_type> <val>)
Overview:¶
The ‘llvm.set.fpenv
’ intrinsic sets the current floating-point environment。
Arguments:¶
The argument is an integer representing the new floating-point environment. The integer type is platform-specific。
Semantics:¶
The ‘llvm.set.fpenv
’ intrinsic sets the current floating-point environment to the state specified by the argument. The state may be previously obtained by a call to ‘llvm.get.fpenv
’ or synthesized in a platform-dependent way。
‘llvm.reset.fpenv
’ Intrinsic¶
Syntax:¶
declare void @llvm.reset.fpenv()
Overview:¶
The ‘llvm.reset.fpenv
’ intrinsic sets the default floating-point environment。
Semantics:¶
The ‘llvm.reset.fpenv
’ intrinsic sets the current floating-point environment to default state. It is similar to the call ‘fesetenv(FE_DFL_ENV)’, except it does not return any value。
‘llvm.get.fpmode
’ Intrinsic¶
Syntax:¶
The ‘llvm.get.fpmode
’ intrinsic returns bits of the current floating-point control modes. The return value type is platform-specific。
declare <integer_type> @llvm.get.fpmode()
Overview:¶
The ‘llvm.get.fpmode
’ intrinsic reads the current dynamic floating-point control modes and returns it as an integer value。
Arguments:¶
无。
Semantics:¶
The ‘llvm.get.fpmode
’ intrinsic reads the current dynamic floating-point control modes, such as rounding direction, precision, treatment of denormals and so on. It is similar to the C library function ‘fegetmode’, however this function does not store the set of control modes into memory but returns it as an integer value. Interpretation of the bits in this value is target-dependent。
‘llvm.set.fpmode
’ Intrinsic¶
Syntax:¶
The ‘llvm.set.fpmode
’ intrinsic sets the current floating-point control modes。
declare void @llvm.set.fpmode(<integer_type> <val>)
Overview:¶
The ‘llvm.set.fpmode
’ intrinsic sets the current dynamic floating-point control modes。
Arguments:¶
The argument is a set of floating-point control modes, represented as an integer value in a target-dependent way。
Semantics:¶
The ‘llvm.set.fpmode
’ intrinsic sets the current dynamic floating-point control modes to the state specified by the argument, which must be obtained by a call to ‘llvm.get.fpmode
’ or constructed in a target-specific way. It is similar to the C library function ‘fesetmode’, however this function does not read the set of control modes from memory but gets it as integer value。
‘llvm.reset.fpmode
’ Intrinsic¶
Syntax:¶
declare void @llvm.reset.fpmode()
Overview:¶
The ‘llvm.reset.fpmode
’ intrinsic sets the default dynamic floating-point control modes。
参数:¶
无。
语义:¶
‘llvm.reset.fpmode
’ 本征函数将当前动态浮点环境设置为默认状态。它类似于 C 库函数调用 ‘fesetmode(FE_DFL_MODE)’,但是此函数不返回任何值。
浮点测试本征函数¶
这些函数获取浮点值的属性。
‘llvm.is.fpclass
’ 本征函数¶
语法:¶
declare i1 @llvm.is.fpclass(<fptype> <op>, i32 <test>)
declare <N x i1> @llvm.is.fpclass(<vector-fptype> <op>, i32 <test>)
概述:¶
‘llvm.is.fpclass
’ 本征函数返回一个布尔值或布尔值向量,具体取决于第一个参数是否满足第二个参数指定的测试。
如果第一个参数是浮点标量,则结果类型为布尔值 (i1)。
如果第一个参数是浮点向量,则结果类型是与第一个参数具有相同元素数量的布尔向量。
参数:¶
‘llvm.is.fpclass
’ 本征函数的第一个参数必须是 浮点 或 向量 的浮点值。
第二个参数指定要执行的测试。它必须是编译时整数常量,其中的每一位都指定浮点类
位 # |
浮点类 |
---|---|
0 |
snan |
1 |
qnan |
2 |
ninf |
3 |
nnorm |
4 |
nsub |
5 |
nzero |
6 |
pzero |
7 |
psub |
8 |
pnorm |
9 |
pinf |
语义:¶
该函数检查 op
是否属于 test
指定的任何浮点类。如果 op
是向量,则逐个元素进行检查。每次检查都会产生一个 i1 结果,如果元素值满足指定的测试,则结果为 true
。参数 test
是一个位掩码,其中每一位指定要测试的浮点类。例如,值 0x108 对正常值进行测试,- 其中的第 3 位和第 8 位被设置,这意味着如果 op
是正或负的正常值,则该函数返回 true
。该函数永远不会引发浮点异常。该函数不会规范化其输入值,并且不依赖于浮点环境。如果浮点环境对次正规输入值进行归零处理(例如由 "denormal-fp-math"
属性指示),则将观察到次正规值(不会被隐式地视为零)。
通用本征函数¶
此类本征函数被设计为通用的,没有特定的目的。
‘llvm.var.annotation
’ 本征函数¶
语法:¶
declare void @llvm.var.annotation(ptr <val>, ptr <str>, ptr <str>, i32 <int>)
概述:¶
‘llvm.var.annotation
’ 本征函数。
参数:¶
第一个参数是指向值的指针,第二个参数是指向全局字符串的指针,第三个参数是指向全局字符串(即源文件名)的指针,最后一个参数是行号。
语义:¶
此本征函数允许使用任意字符串注释局部变量。这对于希望查找这些注释的特殊用途优化非常有用。这些没有其他定义的用途;它们被代码生成和优化忽略。
‘llvm.ptr.annotation.*
’ 本征函数¶
语法:¶
这是一个重载的本征函数。您可以对指向任何宽度的整数的指针使用 ‘llvm.ptr.annotation
’。注意 您必须为指针指定地址空间。默认地址空间的标识符是整数 ‘0
’。
declare ptr @llvm.ptr.annotation.p0(ptr <val>, ptr <str>, ptr <str>, i32 <int>)
declare ptr @llvm.ptr.annotation.p1(ptr addrspace(1) <val>, ptr <str>, ptr <str>, i32 <int>)
概述:¶
‘llvm.ptr.annotation
’ 本征函数。
参数:¶
第一个参数是指向任意位宽的整数值(某些表达式的结果)的指针,第二个参数是指向全局字符串的指针,第三个参数是指向全局字符串(即源文件名)的指针,最后一个参数是行号。它返回第一个参数的值。
语义:¶
此本征函数允许使用任意字符串注释指向整数的指针。这对于希望查找这些注释的特殊用途优化非常有用。这些没有其他定义的用途;转换会尽最大努力保留注释,但允许将本征函数替换为其第一个参数,而不会破坏语义,并且本征函数在指令选择期间被完全删除。
‘llvm.annotation.*
’ 本征函数¶
语法:¶
这是一个重载的本征函数。您可以对任何整数位宽使用 ‘llvm.annotation
’。
declare i8 @llvm.annotation.i8(i8 <val>, ptr <str>, ptr <str>, i32 <int>)
declare i16 @llvm.annotation.i16(i16 <val>, ptr <str>, ptr <str>, i32 <int>)
declare i32 @llvm.annotation.i32(i32 <val>, ptr <str>, ptr <str>, i32 <int>)
declare i64 @llvm.annotation.i64(i64 <val>, ptr <str>, ptr <str>, i32 <int>)
declare i256 @llvm.annotation.i256(i256 <val>, ptr <str>, ptr <str>, i32 <int>)
概述:¶
‘llvm.annotation
’ 本征函数。
参数:¶
第一个参数是整数值(某些表达式的结果),第二个参数是指向全局字符串的指针,第三个参数是指向全局字符串(即源文件名)的指针,最后一个参数是行号。它返回第一个参数的值。
语义:¶
此本征函数允许将注释放在带有任意字符串的任意表达式上。这对于希望查找这些注释的特殊用途优化非常有用。这些没有其他定义的用途;转换会尽最大努力保留注释,但允许将本征函数替换为其第一个参数,而不会破坏语义,并且本征函数在指令选择期间被完全删除。
‘llvm.codeview.annotation
’ 本征函数¶
语法:¶
此注释在其程序点发出一个标签,以及一个关联的 S_ANNOTATION
codeview 记录,其中包含一些额外的字符串元数据。这用于实现 MSVC 的 __annotation
本征函数。它被标记为 noduplicate
,因此对此本征函数的调用会阻止内联,并且应被视为开销很大。
declare void @llvm.codeview.annotation(metadata)
参数:¶
参数应为包含任意数量 MDString 的 MDTuple。
‘llvm.trap
’ 本征函数¶
语法:¶
declare void @llvm.trap() cold noreturn nounwind
概述:¶
‘llvm.trap
’ 本征函数。
参数:¶
无。
语义:¶
此本征函数被降低为目标相关的陷阱指令。如果目标没有陷阱指令,则此本征函数将被降低为对 abort()
函数的调用。
‘llvm.debugtrap
’ 本征函数¶
语法:¶
declare void @llvm.debugtrap() nounwind
概述:¶
‘llvm.debugtrap
’ 本征函数。
参数:¶
无。
语义:¶
此本征函数被降低为旨在引起执行陷阱的代码,目的是请求调试器的注意。
‘llvm.ubsantrap
’ 本征函数¶
语法:¶
declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind
概述:¶
‘llvm.ubsantrap
’ 本征函数。
参数:¶
一个描述检测到的失败类型的整数。
语义:¶
此本征函数被降低为旨在引起执行陷阱的代码,并将参数以某种方式嵌入到该陷阱的编码中,以便在可能的情况下区分崩溃。
对于不支持此行为的目标,等效于 @llvm.trap
。
‘llvm.stackprotector
’ 本征函数¶
语法:¶
declare void @llvm.stackprotector(ptr <guard>, ptr <slot>)
概述:¶
llvm.stackprotector
本征函数获取 guard
并将其存储到 slot
的堆栈上。堆栈槽位被调整以确保它放置在堆栈上,位于局部变量之前。
参数:¶
llvm.stackprotector
本征函数需要两个指针参数。第一个参数是从堆栈保护 @__stack_chk_guard
加载的值。第二个变量是一个 alloca
,它有足够的空间来容纳保护值。
语义:¶
此本征函数使 prologue/epilogue 插入器强制 AllocaInst
堆栈槽位的位置位于堆栈上的局部变量之前。这是为了确保如果堆栈上的局部变量被覆盖,它将破坏保护值。llvm.stackprotectorcheck
在函数退出时检查堆栈上的保护值是否与原始保护值不同。如果它们不同,则 llvm.stackprotectorcheck
会导致程序通过调用 __stack_chk_fail()
函数中止。
‘llvm.stackguard
’ 本征函数¶
语法:¶
declare ptr @llvm.stackguard()
概述:¶
llvm.stackguard
本征函数返回系统堆栈保护值。
它不应由前端生成,因为它仅供内部使用。我们创建此本征函数的原因是我们在 FastISel 中仍然支持 IR 形式的堆栈保护。
参数:¶
无。
语义:¶
在某些平台上,此本征函数返回的值在同一线程中的加载之间保持不变。在其他平台上,它返回相同的全局变量值(如果有),例如 @__stack_chk_guard
。
目前,某些平台具有 IR 级别的自定义堆栈保护加载(例如 X86 Linux),这不由 llvm.stackguard()
处理,而将来应该由其处理。
‘llvm.objectsize
’ 本征函数¶
语法:¶
declare i32 @llvm.objectsize.i32(ptr <object>, i1 <min>, i1 <nullunknown>, i1 <dynamic>)
declare i64 @llvm.objectsize.i64(ptr <object>, i1 <min>, i1 <nullunknown>, i1 <dynamic>)
概述:¶
llvm.objectsize
本征函数旨在向优化器提供信息,以确定 a) 诸如 memcpy 之类的操作是否会溢出与对象对应的缓冲区,或者 b) 运行时溢出检查是否不必要。此上下文中的对象是指特定类、结构、数组或其他对象的分配。
参数:¶
llvm.objectsize
本征函数接受四个参数。第一个参数是指向或进入 object
的指针。第二个参数确定当对象大小未知时,llvm.objectsize
是返回 0(如果为 true)还是 -1(如果为 false)。第三个参数控制当地址空间 0 中的 null
用作其指针参数时,llvm.objectsize
的行为方式。如果为 false
,则当给定 null
时,llvm.objectsize
报告 0 个可用字节。否则,如果 null
位于非零地址空间中,或者如果为 llvm.objectsize
的第三个参数给定了 true
,我们假设其大小未知。 llvm.objectsize
的第四个参数确定是否应在运行时评估该值。
第二、第三和第四个参数仅接受常量。
语义:¶
llvm.objectsize
本征函数被降低为表示相关对象大小的值。如果无法确定大小,则 llvm.objectsize
返回 i32/i64 -1 或 0
(取决于 min
参数)。
‘llvm.expect
’ 本征函数¶
语法:¶
这是一个重载的本征函数。您可以对任何整数位宽使用 llvm.expect
。
declare i1 @llvm.expect.i1(i1 <val>, i1 <expected_val>)
declare i32 @llvm.expect.i32(i32 <val>, i32 <expected_val>)
declare i64 @llvm.expect.i64(i64 <val>, i64 <expected_val>)
概述:¶
llvm.expect
本征函数提供关于 val
的预期值(最可能的值)的信息,优化器可以使用这些信息。
参数:¶
llvm.expect
本征函数接受两个参数。第一个参数是一个值。第二个参数是一个预期值。
语义:¶
此本征函数被降低为 val
。
‘llvm.expect.with.probability
’ 本征函数¶
语法:¶
此本征函数类似于 llvm.expect
。这是一个重载的本征函数。您可以对任何整数位宽使用 llvm.expect.with.probability
。
declare i1 @llvm.expect.with.probability.i1(i1 <val>, i1 <expected_val>, double <prob>)
declare i32 @llvm.expect.with.probability.i32(i32 <val>, i32 <expected_val>, double <prob>)
declare i64 @llvm.expect.with.probability.i64(i64 <val>, i64 <expected_val>, double <prob>)
概述:¶
llvm.expect.with.probability
本征函数提供关于 val
的预期值以及概率(或置信度)prob
的信息,优化器可以使用这些信息。
参数:¶
llvm.expect.with.probability
本征函数接受三个参数。第一个参数是一个值。第二个参数是一个预期值。第三个参数是一个概率。
语义:¶
此本征函数被降低为 val
。
‘llvm.assume
’ 本征函数¶
语法:¶
declare void @llvm.assume(i1 %cond)
概述:¶
llvm.assume
允许优化器假设提供的条件为真。然后,此信息可用于简化代码的其他部分。
更复杂的假设可以编码为 assume 操作数束。
参数:¶
调用的参数是优化器可以假设始终为真的条件。
语义:¶
当控制流到达本征函数调用时,此本征函数允许优化器假设提供的条件始终为真。不会为此本征函数生成代码,并且仅对提供的条件有贡献的指令不会用于代码生成。如果在执行期间违反了条件,则行为未定义。
请注意,优化器可能会限制对 llvm.assume
本征函数使用的值执行的转换,以便保留仅用于形成本征函数的输入参数的指令。如果 llvm.assume
本征函数提供的额外信息没有在代码质量上引起足够的总体改进,则这可能证明是不希望的。因此,llvm.assume
不应用于记录优化器可以推导出的基本数学不变量或对优化器几乎没有用处的事实。
‘llvm.ssa.copy
’ 本征函数¶
语法:¶
declare type @llvm.ssa.copy(type returned %operand) memory(none)
参数:¶
第一个参数是一个操作数,它用作返回的值。
概述:¶
llvm.ssa.copy
本征函数可用于通过复制操作并为其指定新名称来将信息附加到操作。例如,PredicateInfo 实用程序使用它来构建扩展 SSA 形式,并将各种形式的信息附加到支配特定用途的操作数。它并非旨在用于通用用途,仅用于构建需要在特定点进行值拆分的临时重命名形式。
‘llvm.type.test
’ 本征函数¶
语法:¶
declare i1 @llvm.type.test(ptr %ptr, metadata %type) nounwind memory(none)
参数:¶
第一个参数是要测试的指针。第二个参数是表示 类型标识符 的元数据对象。
概述:¶
llvm.type.test
本征函数测试给定的指针是否与给定的类型标识符关联。
‘llvm.type.checked.load
’ 本征函数¶
语法:¶
declare {ptr, i1} @llvm.type.checked.load(ptr %ptr, i32 %offset, metadata %type) nounwind memory(argmem: read)
参数:¶
第一个参数是要从中加载函数指针的指针。第二个参数是要从中加载函数指针的字节偏移量。第三个参数是表示 类型标识符 的元数据对象。
概述:¶
llvm.type.checked.load
本征函数使用类型元数据从虚函数表指针安全地加载函数指针。此本征函数用于结合虚拟调用优化来实现控制流完整性。虚拟调用优化Pass 将优化掉与去虚拟化调用关联的 llvm.type.checked.load
本征函数,从而在不需要强制执行控制流完整性约束的情况下删除类型检查。
如果给定指针与类型元数据标识符关联,则此函数返回 true 作为其返回值的第二个元素。(请注意,如果给定指针未与类型元数据标识符关联,则该函数也可能返回 true。)如果该函数的返回值的第二个元素为 true,则以下规则适用于第一个元素
如果给定指针与给定的类型元数据标识符关联,则它是从给定指针的给定字节偏移量加载的函数指针。
如果给定指针未与给定的类型元数据标识符关联,则它是以下之一(选择哪个未指定)
将从与类型元数据关联的任意选择的(通过未指定的机制)指针加载的函数指针。
如果该函数具有非 void 返回类型,则指向返回未指定值而不引起副作用的函数的指针。
如果函数的返回值的第二个元素为 false,则第一个元素的值未定义。
‘llvm.type.checked.load.relative
’ 本征函数¶
语法:¶
declare {ptr, i1} @llvm.type.checked.load.relative(ptr %ptr, i32 %offset, metadata %type) nounwind memory(argmem: read)
概述:¶
llvm.type.checked.load.relative
本征函数使用元数据从虚函数表指针加载指向函数的相对指针。否则,其语义与 llvm.type.checked.load
本征函数相同。
相对指针是指向指向值的偏移量的指针。相对指针的底层指针的地址通过将偏移量添加到偏移量值的地址来获得。
‘llvm.arithmetic.fence
’ 本征函数¶
语法:¶
declare <type>
@llvm.arithmetic.fence(<type> <op>)
概述:¶
llvm.arithmetic.fence
本征函数的目的是防止优化器在参数和包含该参数的表达式之间执行快速数学优化,特别是重结合。它可以用于保留源语言中的括号。
参数:¶
llvm.arithmetic.fence
本征函数仅接受一个参数。参数和返回值是相同类型的浮点数或向量浮点数。
语义:¶
此本征函数返回其操作数的值。优化器可以优化参数,但优化器不能将操作数的任何组件提升到包含上下文,并且优化器不能将包含上下文中任何表达式的计算移动到操作数中。
‘llvm.donothing
’ 本征函数¶
语法:¶
declare void @llvm.donothing() nounwind memory(none)
概述:¶
llvm.donothing
本征函数不执行任何操作。它是仅有的三个本征函数之一(除了 llvm.experimental.patchpoint
和 llvm.experimental.gc.statepoint
),可以使用 invoke 指令调用。
参数:¶
无。
语义:¶
此本征函数不执行任何操作,并且会被优化器删除,并被代码生成忽略。
‘llvm.experimental.deoptimize
’ 本征函数¶
语法:¶
declare type @llvm.experimental.deoptimize(...) [ "deopt"(...) ]
概述:¶
此本征函数与 反优化操作数束 一起,允许前端表达控制权和帧局部状态从当前正在执行的(通常更专业,因此更快)函数版本转移到另一个(通常更通用,因此更慢)版本。
在具有完全集成的托管运行时(如 Java 和 JavaScript)的语言中,此本征函数可用于实现类似于 “uncommon trap” 或 “side exit” 的功能。在非托管语言(如 C 和 C++)中,此本征函数可用于表示专用函数的慢速路径。
参数:¶
此本征函数接受任意数量的参数,其含义由 降低策略 决定。
语义:¶
@llvm.experimental.deoptimize
本征函数执行附加的反优化延续(使用 反优化操作数束 表示),并返回反优化延续返回的值。定义延续本身的语义属性超出了语言参考的范围 – 就 LLVM 而言,反优化延续可以调用任意副作用,包括从整个堆中读取和写入。
使用 "deopt"
操作数束表示的反优化延续始终继续执行到包含它们的物理帧的末尾,因此对 @llvm.experimental.deoptimize
的所有调用都必须处于 “尾部位置”
@llvm.experimental.deoptimize
无法被调用。调用必须紧接在 ret 指令之前。
ret
指令必须返回@llvm.experimental.deoptimize
调用产生的值(如果存在),否则返回 void。
请注意,以上限制意味着调用 @llvm.experimental.deoptimize
的返回类型将与其直接调用者的返回类型匹配。
内联器将调用者的 "deopt"
continuation 合并到被内联函数的 "deopt"
continuation 中,并且还会更新对此内部函数的调用,使其直接从内联到其中的函数的帧返回。
所有 @llvm.experimental.deoptimize
的声明都必须共享相同的调用约定。
降低 (Lowering):¶
对 @llvm.experimental.deoptimize
的调用会降低为对符号 __llvm_deoptimize
的调用(前端有责任确保定义了此符号)。对 @llvm.experimental.deoptimize
的调用参数会像它们是指定类型的形式参数一样被降低,而不是作为 varargs。
‘llvm.experimental.guard
’ 内部函数¶
语法:¶
declare void @llvm.experimental.guard(i1, ...) [ "deopt"(...) ]
概述:¶
此内部函数与deoptimization operand bundles一起,允许前端表达在编译期间对乐观假设进行的保护或检查。 @llvm.experimental.guard
的语义根据 @llvm.experimental.deoptimize
定义 - 其主体定义为等效于
define void @llvm.experimental.guard(i1 %pred, <args...>) {
%realPred = and i1 %pred, undef
br i1 %realPred, label %continue, label %leave [, !make.implicit !{}]
leave:
call void @llvm.experimental.deoptimize(<args...>) [ "deopt"() ]
ret void
continue:
ret void
}
可选的 [, !make.implicit !{}]
仅当调用站点上存在时才存在。有关 !make.implicit
的更多详细信息,请参阅FaultMaps 和隐式检查。
换句话说,如果 @llvm.experimental.guard
的第一个参数为 false
,则执行附加的 "deopt"
continuation(但不仅当为 false
时才执行)。由于优化器允许用任意值替换 undef
,因此它可以优化 guard 以“虚假地”失败,即在原始条件不为 false 的情况下(因此是“不仅当”);这允许“检查放宽”类型的优化。
@llvm.experimental.guard
不能被调用。
在首次添加 @llvm.experimental.guard
之后,在 @llvm.experimental.widenable.condition
中发现了更通用的公式。对 @llvm.experimental.guard
的支持正在慢慢地用这种替代方案重新表达。
‘llvm.experimental.widenable.condition
’ 内部函数¶
语法:¶
declare i1 @llvm.experimental.widenable.condition()
概述:¶
此内部函数表示“可放宽的条件”,即具有以下属性的布尔表达式:无论此表达式是 true 还是 false,程序都是正确且良好定义的。
与deoptimization operand bundles一起,@llvm.experimental.widenable.condition
允许前端表达在编译期间对乐观假设进行的保护或检查,并将它们表示为特殊条件下的分支指令。
虽然这在语义上可能与 undef 相似,但它非常不同,因为调用会产生特定的、单一的值。它也旨在稍后降低,并保持可用状态,以用于可以从其特殊属性中获益的特定优化和转换。
参数:¶
无。
语义:¶
内部函数 @llvm.experimental.widenable.condition()
返回 true 或 false。对于每次调用此内部函数的评估,如果它返回 true 和返回 false,程序都必须是有效且正确的。这允许转换 pass 在任何一个值有利时,用该值替换对此内部函数的评估。
当在分支条件中使用时,它允许我们在同一问题的两个备选正确解决方案之间进行选择,如下例所示
%cond = call i1 @llvm.experimental.widenable.condition()
br i1 %cond, label %fast_path, label %slow_path
fast_path:
; Apply memory-consuming but fast solution for a task.
slow_path:
; Cheap in memory but slow solution.
无论内部函数调用的结果是 true 还是 false,选择任一解决方案都应该是正确的。我们可以通过用不同的 i1 表达式替换 @llvm.experimental.widenable.condition
的结果来在它们之间切换。
这就是它如何用于将 guard 表示为可放宽分支的方式
block:
; Unguarded instructions
call void @llvm.experimental.guard(i1 %cond, <args...>) ["deopt"(<deopt_args...>)]
; Guarded instructions
可以用使用 @llvm.experimental.widenable.condition
的显式分支的替代等效形式表示
block:
; Unguarded instructions
%widenable_condition = call i1 @llvm.experimental.widenable.condition()
%guard_condition = and i1 %cond, %widenable_condition
br i1 %guard_condition, label %guarded, label %deopt
guarded:
; Guarded instructions
deopt:
call type @llvm.experimental.deoptimize(<args...>) [ "deopt"(<deopt_args...>) ]
因此,只有当 %cond
为 true 时,块 guarded 才是可达的,并且无论 %cond
为 true 还是 false,都应该可以转到块 deopt。
@llvm.experimental.widenable.condition
永远不会抛出异常,因此它不能被调用。
Guard 放宽:¶
当 @llvm.experimental.widenable.condition()
用于表示为显式分支的 guard 的条件时,用任何附加条件放宽 guard 的条件是合法的。
Guard 放宽看起来像是替换
%widenable_cond = call i1 @llvm.experimental.widenable.condition()
%guard_cond = and i1 %cond, %widenable_cond
br i1 %guard_cond, label %guarded, label %deopt
为
%widenable_cond = call i1 @llvm.experimental.widenable.condition()
%new_cond = and i1 %any_other_cond, %widenable_cond
%new_guard_cond = and i1 %cond, %new_cond
br i1 %new_guard_cond, label %guarded, label %deopt
对于此分支。这里 %any_other_cond 是任意选择的良好定义的 i1 值。通过进行 guard 放宽,我们可以对 guarded 块施加更严格的条件,并在不满足新条件时 bail 到 deopt。
降低 (Lowering):¶
默认的降低策略是用常量 true 替换 @llvm.experimental.widenable.condition
调用的结果。但是,用任何其他 i1 值替换它始终是正确的。任何 pass 都可以自由地这样做,如果它可以从非默认降低中获益。
‘llvm.allow.ubsan.check
’ 内部函数¶
语法:¶
declare i1 @llvm.allow.ubsan.check(i8 immarg %kind)
概述:¶
此内部函数返回 true
当且仅当编译器选择在当前基本块中启用 ubsan 检查时。
允许 ubsan 检查的规则不是内部函数声明的一部分,而是由编译器选项控制。
此内部函数是 @llvm.allow.runtime.check()
的 ubsan 特定版本。
参数:¶
一个整数,描述由内部函数保护的 ubsan 检查的类型。
语义:¶
内部函数 @llvm.allow.ubsan.check()
返回 true
或 false
,具体取决于编译器选项。
对于每次调用此内部函数的评估,如果它返回 true
和返回 false
,程序都必须是有效且正确的。
当在分支条件中使用时,它选择两条路径之一
true`: 执行 UBSan 检查并报告任何故障。
false: 绕过检查,假设它总是成功。
示例
%allow = call i1 @llvm.allow.ubsan.check(i8 5)
%not.allow = xor i1 %allow, true
%cond = or i1 %ubcheck, %not.allow
br i1 %cond, label %cont, label %trap
cont:
; Proceed
trap:
call void @llvm.ubsantrap(i8 5)
unreachable
‘llvm.allow.runtime.check
’ 内部函数¶
语法:¶
declare i1 @llvm.allow.runtime.check(metadata %kind)
概述:¶
此内部函数返回 true
当且仅当编译器选择在当前基本块中启用运行时检查时。
允许运行时检查的规则不是内部函数声明的一部分,而是由编译器选项控制。
此内部函数是 @llvm.allow.ubsan.check()
的非 ubsan 特定版本。
参数:¶
一个字符串,标识由内部函数保护的运行时检查的类型。该字符串可用于控制允许检查的规则。
语义:¶
内部函数 @llvm.allow.runtime.check()
返回 true
或 false
,具体取决于编译器选项。
对于每次调用此内部函数的评估,如果它返回 true
和返回 false
,程序都必须是有效且正确的。
当在分支条件中使用时,它允许我们在同一问题的两个备选正确解决方案之间进行选择。
如果内部函数评估为 true
,程序应执行受保护的检查。如果内部函数评估为 false
,程序应避免任何不必要的检查。
示例
%allow = call i1 @llvm.allow.runtime.check(metadata !"my_check")
br i1 %allow, label %fast_path, label %slow_path
fast_path:
; Omit diagnostics.
slow_path:
; Additional diagnostics.
‘llvm.load.relative
’ 内部函数¶
语法:¶
declare ptr @llvm.load.relative.iN(ptr %ptr, iN %offset) nounwind memory(argmem: read)
概述:¶
此内部函数从地址 %ptr + %offset
加载一个 32 位值,将 %ptr
添加到该值并返回它。常量折叠器专门识别此内部函数的形式及其可能加载的常量初始值设定项;如果已知加载的常量初始值设定项具有 i32 trunc(x - %ptr)
的形式,则内部函数调用会折叠为 x
。
LLVM 提供,如果 x
是 unnamed_addr
函数,则在中等代码模型下,此类常量初始值设定项的计算在链接时不会溢出。但是,它不为折叠到函数体中的常量初始值设定项提供此保证。当从此类常量加载时,可以使用此内部函数来避免溢出的可能性。
‘llvm.sideeffect
’ 内部函数¶
语法:¶
declare void @llvm.sideeffect() inaccessiblememonly nounwind willreturn
概述:¶
llvm.sideeffect
内部函数不执行任何操作。优化器将其视为具有副作用,因此可以将其插入循环中以指示不应假定循环终止(这可能会导致整个循环被优化掉),即使它是没有其他副作用的无限循环。
参数:¶
无。
语义:¶
此内部函数实际上不执行任何操作,但优化器必须假定它具有外部可观察的副作用。
‘llvm.is.constant.*
’ 内部函数¶
语法:¶
这是一个重载的内部函数。您可以将 llvm.is.constant 与任何参数类型一起使用。
declare i1 @llvm.is.constant.i32(i32 %operand) nounwind memory(none)
declare i1 @llvm.is.constant.f32(float %operand) nounwind memory(none)
declare i1 @llvm.is.constant.TYPENAME(TYPE %operand) nounwind memory(none)
概述:¶
如果参数已知为显式编译时常量,则 ‘llvm.is.constant
’ 内部函数将返回 true。保证在生成机器代码之前将其折叠为 true 或 false。
语义:¶
此内部函数不生成任何代码。如果其参数已知为显式编译时常量值,则内部函数将转换为常量 true 值。否则,它将转换为常量 false 值。
特别注意,如果参数是一个常量表达式,该表达式引用一个全局变量(其地址 _是_ 常量,但在编译期间不是显式的),则内部函数评估为 false。
结果也有意取决于优化 pass 的结果 - 例如,结果可能会根据函数是否内联而改变。函数的参数显然不是常量。但是,如果传递给函数参数的值是常量,则在函数内联后,像 llvm.is.constant.i32(i32 %param)
这样的调用可以返回 true。
‘llvm.ptrmask
’ 内部函数¶
语法:¶
declare ptrty llvm.ptrmask(ptrty %ptr, intty %mask) speculatable memory(none)
参数:¶
第一个参数是指针或指针向量。第二个参数是整数或整数向量,其位宽与第一个参数的索引类型大小相同。
概述:¶
llvm.ptrmask
内部函数根据掩码屏蔽指针的位。这允许从标记指针中剥离数据,而无需将它们转换为整数 (ptrtoint/inttoptr)。因此,我们可以保留更多信息,以方便别名分析和底层对象检测。
语义:¶
ptrmask(%ptr, %mask)
的结果等效于以下扩展,其中 iPtrIdx
是指针的索引类型大小
%intptr = ptrtoint ptr %ptr to iPtrIdx ; this may truncate
%masked = and iPtrIdx %intptr, %mask
%diff = sub iPtrIdx %masked, %intptr
%result = getelementptr i8, ptr %ptr, iPtrIdx %diff
如果指针索引类型大小小于指针类型大小,则意味着超出索引大小的指针位不受此内部函数的影响。对于整数指针,它的行为就像掩码用 1 位扩展到指针类型大小一样。
返回的指针和第一个参数都基于相同的底层对象(有关 基于 术语的更多信息,请参阅指针别名规则)。
内部函数仅通过返回值捕获指针参数。
‘llvm.threadlocal.address
’ 内部函数¶
语法:¶
declare ptr @llvm.threadlocal.address(ptr) nounwind willreturn memory(none)
参数:¶
llvm.threadlocal.address
内部函数需要一个全局值参数(全局变量或别名),该参数是线程局部的。
语义:¶
线程局部全局变量的地址不是常量,因为它取决于调用线程。 llvm.threadlocal.address
内部函数返回给定线程局部全局变量在调用线程中的地址。
‘llvm.vscale
’ 内部函数¶
语法:¶
declare i32 llvm.vscale.i32()
declare i64 llvm.vscale.i64()
概述:¶
llvm.vscale
内部函数返回可缩放向量(例如
)中 vscale
的值。
语义:¶
vscale
是一个正值,在程序执行期间是恒定的,但在编译时是未知的。如果结果值不适合结果类型,则结果是poison value。
‘llvm.fake.use
’ 内部函数¶
语法:¶
declare void @llvm.fake.use(...)
概述:¶
llvm.fake.use
内部函数是一个空操作。它接受一个值作为操作数,并被视为该操作数的使用,以强制优化器在 fake use 之前保留该值。这用于延长变量的生命周期,其中放置在变量作用域末尾的此内部函数有助于防止该变量被优化掉。
参数:¶
llvm.fake.use
内部函数接受一个参数,该参数可以是任何函数局部 SSA 值。请注意,签名是可变的,以便内部函数可以接受任何类型的参数,但传递多个参数将导致错误。
语义:¶
此内部函数不执行任何操作,但优化器必须将其视为其单个操作数的使用,并应尝试保留内部函数及其在函数中的位置。
堆栈映射内部函数¶
LLVM 提供了实验性内部函数,以支持动态语言 JIT 中常见的运行时修补机制。这些内部函数在LLVM 中的堆栈映射和补丁点中进行了描述。
元素级原子内存内部函数¶
这些内部函数与标准库内存内部函数类似,不同之处在于它们执行内存传输作为一系列原子内存访问。
‘llvm.memcpy.element.unordered.atomic
’ 内部函数¶
语法:¶
这是一个重载的内部函数。您可以将 llvm.memcpy.element.unordered.atomic
用于任何整数位宽和不同的地址空间。但是,并非所有目标都支持所有位宽。
declare void @llvm.memcpy.element.unordered.atomic.p0.p0.i32(ptr <dest>,
ptr <src>,
i32 <len>,
i32 <element_size>)
declare void @llvm.memcpy.element.unordered.atomic.p0.p0.i64(ptr <dest>,
ptr <src>,
i64 <len>,
i32 <element_size>)
概述:¶
‘llvm.memcpy.element.unordered.atomic.*
’ 内部函数是 ‘llvm.memcpy.*
’ 内部函数的专门化。它的不同之处在于 dest
和 src
被视为元素大小正好为 element_size
字节的数组,并且缓冲区之间的复制使用一系列无序原子加载/存储操作,这些操作的大小是 element_size
的正整数倍。
参数:¶
前三个参数与@llvm.memcpy内部函数中的参数相同,但添加了约束,即 len
必须是 element_size
的正整数倍。如果 len
不是 element_size
的正整数倍,则内部函数的行为是未定义的。
element_size
必须是编译时常量,正的 2 的幂,且不大于目标特定的原子访问大小限制。
对于每个输入指针,必须指定 align
参数属性。它必须是 2 的幂,且不小于 element_size
。调用者保证源指针和目标指针都对齐到该边界。
语义:¶
‘llvm.memcpy.element.unordered.atomic.*
’ 内部函数将 len
字节的内存从源位置复制到目标位置。不允许这些位置重叠。内存复制作为一系列加载/存储操作执行,其中每个访问都保证是 element_size
字节宽的倍数,并且在 element_size
边界对齐。
复制的顺序未指定。可以多次从源缓冲区读取相同的值,但每个元素仅对目标缓冲区发出一次写入。当指定时,如果对源和目标的并发读取和写入是无序原子的,则是良好定义的。
此内部函数不提供任何超出从源位置进行一组无序加载和存储到目标位置所提供的其他排序保证。
降低 (Lowering):¶
在最一般的情况下,对 ‘llvm.memcpy.element.unordered.atomic.*
’ 的调用会降低为对符号 __llvm_memcpy_element_unordered_atomic_*
的调用。其中 ‘*’ 被实际的元素大小替换。有关 GC 特定降低的详细信息,请参阅RewriteStatepointsForGC 内部函数降低。
当内联内存复制有利可图时,优化器允许内联内存复制。
‘llvm.memmove.element.unordered.atomic
’ 内部函数¶
语法:¶
这是一个重载的内部函数。您可以将 llvm.memmove.element.unordered.atomic
用于任何整数位宽和不同的地址空间。但是,并非所有目标都支持所有位宽。
declare void @llvm.memmove.element.unordered.atomic.p0.p0.i32(ptr <dest>,
ptr <src>,
i32 <len>,
i32 <element_size>)
declare void @llvm.memmove.element.unordered.atomic.p0.p0.i64(ptr <dest>,
ptr <src>,
i64 <len>,
i32 <element_size>)
概述:¶
‘llvm.memmove.element.unordered.atomic.*
’ 内部函数是 ‘llvm.memmove.*
’ 内部函数的专门化。它的不同之处在于 dest
和 src
被视为元素大小正好为 element_size
字节的数组,并且缓冲区之间的复制使用一系列无序原子加载/存储操作,这些操作的大小是 element_size
的正整数倍。
参数:¶
前三个参数与@llvm.memmove内部函数中的参数相同,但添加了约束,即 len
必须是 element_size
的正整数倍。如果 len
不是 element_size
的正整数倍,则内部函数的行为是未定义的。
element_size
必须是编译时常量,正的 2 的幂,且不大于目标特定的原子访问大小限制。
对于每个输入指针,必须指定 align
参数属性。它必须是 2 的幂,且不小于 element_size
。调用者保证源指针和目标指针都对齐到该边界。
语义:¶
‘llvm.memmove.element.unordered.atomic.*
’ 内部函数将 len
字节的内存从源位置复制到目标位置。允许这些位置重叠。内存复制作为一系列加载/存储操作执行,其中每个访问都保证是 element_size
字节宽的倍数,并且在 element_size
边界对齐。
复制的顺序未指定。可以多次从源缓冲区读取相同的值,但每个元素仅对目标缓冲区发出一次写入。当指定时,如果对源和目标的并发读取和写入是无序原子的,则是良好定义的。
此内部函数不提供任何超出从源位置进行一组无序加载和存储到目标位置所提供的其他排序保证。
降低 (Lowering):¶
在最一般的情况下,对 ‘llvm.memmove.element.unordered.atomic.*
’ 的调用会降低为对符号 __llvm_memmove_element_unordered_atomic_*
的调用。其中 ‘*’ 被实际的元素大小替换。有关 GC 特定降低的详细信息,请参阅RewriteStatepointsForGC 内部函数降低。
当内联内存复制有利可图时,优化器允许内联内存复制。
‘llvm.memset.element.unordered.atomic
’ 内部函数¶
语法:¶
这是一个重载的内部函数。您可以将 llvm.memset.element.unordered.atomic
用于任何整数位宽和不同的地址空间。但是,并非所有目标都支持所有位宽。
declare void @llvm.memset.element.unordered.atomic.p0.i32(ptr <dest>,
i8 <value>,
i32 <len>,
i32 <element_size>)
declare void @llvm.memset.element.unordered.atomic.p0.i64(ptr <dest>,
i8 <value>,
i64 <len>,
i32 <element_size>)
概述:¶
‘llvm.memset.element.unordered.atomic.*
’ 内部函数是 ‘llvm.memset.*
’ 内部函数的专门化。它的不同之处在于 dest
被视为元素大小正好为 element_size
字节的数组,并且对该数组的赋值使用一系列无序原子存储操作,这些操作的大小是 element_size
的正整数倍。
参数:¶
前三个参数与@llvm.memset内部函数中的参数相同,但添加了约束,即 len
必须是 element_size
的正整数倍。如果 len
不是 element_size
的正整数倍,则内部函数的行为是未定义的。
element_size
必须是编译时常量,正的 2 的幂,且不大于目标特定的原子访问大小限制。
dest
输入指针必须指定 align
参数属性。它必须是 2 的幂,且不小于 element_size
。调用者保证目标指针对齐到该边界。
语义:¶
‘llvm.memset.element.unordered.atomic.*
’ 内部函数将从目标位置开始的 len
字节的内存设置为给定的 value
。内存使用一系列存储操作设置,其中每个访问都保证是 element_size
字节宽的倍数,并且在 element_size
边界对齐。
赋值的顺序未指定。每个元素仅对目标缓冲区发出一次写入。当指定时,如果对目标的并发读取和写入是无序原子的,则是良好定义的。
此内部函数不提供任何超出对目标进行一组无序存储所提供的其他排序保证。
降低 (Lowering):¶
在最一般的情况下,对 ‘llvm.memset.element.unordered.atomic.*
’ 的调用会降低为对符号 __llvm_memset_element_unordered_atomic_*
的调用。其中 ‘*’ 被实际的元素大小替换。
当内联内存赋值有利可图时,优化器允许内联内存赋值。
Objective-C ARC 运行时内部函数¶
LLVM 提供了降低到 Objective-C ARC 运行时入口点的内部函数。 LLVM 了解这些函数的语义,并基于该知识进行优化。您可以在此处阅读有关 Objective-C ARC 详细信息的更多信息。
‘llvm.objc.autorelease
’ 内部函数¶
语法:¶
declare ptr @llvm.objc.autorelease(ptr)
降低 (Lowering):¶
降低为对 objc_autorelease 的调用。
‘llvm.objc.autoreleasePoolPop
’ 内部函数¶
语法:¶
declare void @llvm.objc.autoreleasePoolPop(ptr)
降低 (Lowering):¶
降低为对 objc_autoreleasePoolPop 的调用。
‘llvm.objc.autoreleasePoolPush
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.autoreleasePoolPush()
底层实现:¶
降低为对 objc_autoreleasePoolPush 的调用。
‘llvm.objc.autoreleaseReturnValue
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.autoreleaseReturnValue(ptr)
底层实现:¶
降低为对 objc_autoreleaseReturnValue 的调用。
‘llvm.objc.copyWeak
’ 内建函数¶
语法:¶
declare void @llvm.objc.copyWeak(ptr, ptr)
底层实现:¶
降低为对 objc_copyWeak 的调用。
‘llvm.objc.destroyWeak
’ 内建函数¶
语法:¶
declare void @llvm.objc.destroyWeak(ptr)
底层实现:¶
降低为对 objc_destroyWeak 的调用。
‘llvm.objc.initWeak
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.initWeak(ptr, ptr)
底层实现:¶
降低为对 objc_initWeak 的调用。
‘llvm.objc.loadWeak
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.loadWeak(ptr)
底层实现:¶
降低为对 objc_loadWeak 的调用。
‘llvm.objc.loadWeakRetained
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.loadWeakRetained(ptr)
底层实现:¶
降低为对 objc_loadWeakRetained 的调用。
‘llvm.objc.moveWeak
’ 内建函数¶
语法:¶
declare void @llvm.objc.moveWeak(ptr, ptr)
底层实现:¶
降低为对 objc_moveWeak 的调用。
‘llvm.objc.release
’ 内建函数¶
语法:¶
declare void @llvm.objc.release(ptr)
底层实现:¶
降低为对 objc_release 的调用。
‘llvm.objc.retain
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.retain(ptr)
底层实现:¶
降低为对 objc_retain 的调用。
‘llvm.objc.retainAutorelease
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.retainAutorelease(ptr)
底层实现:¶
降低为对 objc_retainAutorelease 的调用。
‘llvm.objc.retainAutoreleaseReturnValue
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.retainAutoreleaseReturnValue(ptr)
底层实现:¶
降低为对 objc_retainAutoreleaseReturnValue 的调用。
‘llvm.objc.retainAutoreleasedReturnValue
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.retainAutoreleasedReturnValue(ptr)
底层实现:¶
降低为对 objc_retainAutoreleasedReturnValue 的调用。
‘llvm.objc.retainBlock
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.retainBlock(ptr)
底层实现:¶
降低为对 objc_retainBlock 的调用。
‘llvm.objc.storeStrong
’ 内建函数¶
语法:¶
declare void @llvm.objc.storeStrong(ptr, ptr)
底层实现:¶
降低为对 objc_storeStrong 的调用。
‘llvm.objc.storeWeak
’ 内建函数¶
语法:¶
declare ptr @llvm.objc.storeWeak(ptr, ptr)
底层实现:¶
降低为对 objc_storeWeak 的调用。
保留调试信息内建函数¶
这些内建函数用于将某些调试信息与 IR 级别的操作一起携带。例如,可能需要知道结构体/联合体的名称和原始用户级字段索引。这些信息在 IR GetElementPtr 指令中丢失了,因为 IR 类型与 debugInfo 类型不同,并且联合体在 IR 中被转换为结构体。
‘llvm.preserve.array.access.index
’ 内建函数¶
语法:¶
declare <ret_type>
@llvm.preserve.array.access.index.p0s_union.anons.p0a10s_union.anons(<type> base,
i32 dim,
i32 index)
概述:¶
‘llvm.preserve.array.access.index
’ 内建函数基于数组基地址 base
、数组维度 dim
和对数组的最后访问索引 index
返回 getelementptr 地址。返回类型 ret_type
是指向数组元素的指针类型。数组 dim
和 index
被保留,这比可能受到编译器转换的 getelementptr 指令更健壮。llvm.preserve.access.index
类型的元数据附加到此调用指令,以提供数组或指针调试信息类型。元数据是一个 DICompositeType
或 DIDerivedType
,表示 type
的调试信息版本。
参数:¶
base
是数组基地址。dim
是数组维度。如果 dim
等于 0,则 base
是一个指针。index
是对数组或指针的最后访问索引。
base
参数必须在调用点使用 elementtype 属性进行注释。此属性指定 getelementptr 元素类型。
语义:¶
‘llvm.preserve.array.access.index
’ 内建函数产生与 getelementptr 相同的结果,其中基地址为 base
,访问操作数为 {dim's 0's, index}
。
‘llvm.preserve.union.access.index
’ 内建函数¶
语法:¶
declare <type>
@llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(<type> base,
i32 di_index)
概述:¶
‘llvm.preserve.union.access.index
’ 内建函数携带调试信息字段索引 di_index
并返回 base
地址。llvm.preserve.access.index
类型的元数据附加到此调用指令,以提供联合体调试信息类型。元数据是一个 DICompositeType
,表示 type
的调试信息版本。返回类型 type
与 base
类型相同。
参数:¶
base
是联合体基地址。di_index
是调试信息中的字段索引。
语义:¶
‘llvm.preserve.union.access.index
’ 内建函数返回 base
地址。
‘llvm.preserve.struct.access.index
’ 内建函数¶
语法:¶
declare <ret_type>
@llvm.preserve.struct.access.index.p0i8.p0s_struct.anon.0s(<type> base,
i32 gep_index,
i32 di_index)
概述:¶
‘llvm.preserve.struct.access.index
’ 内建函数基于结构体基地址 base
和 IR 结构体成员索引 gep_index
返回 getelementptr 地址。llvm.preserve.access.index
类型的元数据附加到此调用指令,以提供结构体调试信息类型。元数据是一个 DICompositeType
,表示 type
的调试信息版本。返回类型 ret_type
是指向结构体成员的指针类型。
参数:¶
base
是结构体基地址。gep_index
是基于 IR 结构的结构体成员索引。di_index
是基于调试信息的结构体成员索引。
base
参数必须在调用点使用 elementtype 属性进行注释。此属性指定 getelementptr 元素类型。
语义:¶
‘llvm.preserve.struct.access.index
’ 内建函数产生与 getelementptr 相同的结果,其中基地址为 base
,访问操作数为 {0, gep_index}
。
‘llvm.fptrunc.round
’ 内建函数¶
语法:¶
declare <ty2>
@llvm.fptrunc.round(<type> <value>, metadata <rounding mode>)
概述:¶
‘llvm.fptrunc.round
’ 内建函数使用指定的舍入模式将 浮点 value
截断为 ty2
类型。
参数:¶
‘llvm.fptrunc.round
’ 内建函数接受一个 浮点 值进行转换,以及一个要转换成的 浮点 类型。此参数的大小必须大于结果。
第二个参数指定舍入模式,如约束内建函数部分所述。对于此内建函数,不支持“round.dynamic”模式。
语义:¶
‘llvm.fptrunc.round
’ 内建函数将 value
从较大的 浮点 类型转换为较小的 浮点 类型。此内建函数假定在默认 浮点环境 中执行,除了 舍入模式。并非所有目标都支持此内建函数。某些目标可能不支持所有舍入模式。