LLVM 项目博客

LLVM 项目新闻和来自实战的详细信息

LLVM 数值博客

关键词:数值、Clang、LLVM-IR、: 2019 LLVM 开发者会议、LLVMDevMtg。

本文的目的是开始关于 LLVM 中数值的讨论 - 我们身处何处、最近的工作以及尚待完成的工作。下个月在 2019 年 EuroLLVM 会议上将举行关于数值的非正式讨论。本文的一个目的是提醒大家我们在数值话题上的现状,以便重新开始讨论。

在过去一两年中,一直在推动允许对哪些优化对于任何给定的 IR 部分是合理的进行细粒度的决策。在早期,主要有两种操作模式:快速数学和精确数学。在 IEEE-754 定义的精确数学规则下运行时,许多针对算术指令序列的潜在优化是不允许的,因为它们可能导致违反标准。

例如:

重关联优化传递通常不允许在精确代码生成下进行,因为它可以改变操作顺序,从而改变在表达式级别传播的 NaN 和 Inf 值的创建,以及改变精度。

精确代码生成通常过于严格,因此通常使用另一种快速数学模式,其中允许所有可能的优化,承认这会影响结果的精度,以及可能影响 IEEE 兼容行为。在 LLVM 中,这可以通过在模块级别设置 unsafe-math 标志,或者将 -funsafe-math-optimizations 传递给 clang 来实现,clang 随后会设置它生成的 IR 上的标志。在这种情况下,编译器通常会生成更短的指令序列来计算结果,并且根据上下文,这可能是可以接受的。快速数学通常用于精度损失可以接受的计算。例如,在计算像素颜色时,即使相对低的精度也可能远远超过人眼的感知能力,使更短的指令序列成为一个有吸引力的权衡。然而,在对物理事件进行长期模拟时,精度损失可能意味着模拟偏离现实,从而使这种权衡不可接受。

几年前,LLVM IR 指令获得了被注释为标志的能力,这些标志可以比在模块级别进行非此即彼的决策更细粒度地驱动优化。  相关的 IR 标志是: 

nnan、ninf、nsz、arcp、contract、afn、reassoc、nsw、nuw、exact。 

它们的确切含义在 LLVM 语言参考手册 中有描述。  当所有标志都启用时,我们得到当前的快速数学行为。当这些标志禁用时,我们得到精确数学行为。在这些模型之间还有几个选项,可能对某些应用程序很有吸引力。在过去的一年中,LLVM 社区的几位成员致力于让 IR 优化传递了解这些标志。当未设置 unsafe-math 模块标志时,这些优化传递将通过检查单个标志来工作,允许对特定指令序列可以启用的优化进行细粒度的选择。这使供应商/实施者能够在同一个模块中混合快速和精确的计算,积极地优化某些指令序列,但不优化其他指令序列。

我们现在对 LLVM 代码库中的 IR 传递有很好的覆盖范围,特别是在以下领域
* 内在函数和 libcall 管理
* 指令组合和简化
* 指令定义
* SDNode 定义
* GlobalIsel 组合和代码生成
* 选择 DAG 代码生成
* DAG 组合
* 机器指令定义
* IR 生成器(SDNode、Instruction、MachineInstr)
* CSE 跟踪
* 重关联
* 位代码

仍然有一些领域需要重新设计以实现模块化,包括供应商特定的后端传递。

以下是过去两年开源开发中提到的贡献的一部分

https://reviews.llvm.org/D45781 : MachineInst 支持将 SDNode 快速数学标志映射到后端代码生成中的支持 
https://reviews.llvm.org/D46322 : [选择 DAG] 从 IR 快速数学标志传播 'afn' 和 'reassoc'
https://reviews.llvm.org/D45710 : 快速数学标志映射到 SDNode
https://reviews.llvm.org/D46854 : [DAG] 为所有 FPMathOperators 传播 FMF
https://reviews.llvm.org/D48180 : 使用 fmf 更新 isNegatibleForFree 和 GetNegatedExpression 以用于 fadd
https://reviews.llvm.org/D48057: 简化 isNegatibleForFree 和 GetNegatedExpression 的约束
https://reviews.llvm.org/D47954 : 利用新的 SDNode 标志功能扩展对 fdiv 的当前支持
https://reviews.llvm.org/D47918 : 利用新的 SDNode 标志功能扩展对 fma 的当前支持
https://reviews.llvm.org/D47909 : 利用新的 SDNode 标志功能扩展对 fadd 的当前支持
https://reviews.llvm.org/D47910 : 利用新的 SDNode 标志功能扩展对 fsub 的当前支持
https://reviews.llvm.org/D47911 : 利用新的 SDNode 标志功能扩展对 fmul 的当前支持
https://reviews.llvm.org/D48289 : 为 AllowNewConst 案例重构 visitFADD
https://reviews.llvm.org/D47388 : 通过 IR 在 fma 和 sub 表达式中传播快速数学标志
https://reviews.llvm.org/D47389 : 使用 fmf 子标志保护 fneg
https://reviews.llvm.org/D47026 : 将具有 undef 操作数的 FP 二元运算折叠为 NaN
https://reviews.llvm.org/D47749 : 使用 fmf 子标志保护 fsqrt
https://reviews.llvm.org/D46447 : 将 SDNode 标志映射到 MachineInstr 标志
rL334970: [NFC] 使 MIFlag 访问器函数与使用模型一致
rL338604: [NFC] 对 r334242 的小补充,FMF 传播
https://reviews.llvm.org/D50195 : 将 fsub/fadd 的折叠扩展到 fneg 以用于 FMF
https://reviews.llvm.org/rL339197 : [NFC] 添加 Y - (X + Y) --> -X 的测试
https://reviews.llvm.org/D50417 : [InstCombine] 将 fneg 折叠到 fmul/fdiv 的常量操作数中
https://reviews.llvm.org/rL339357 : 将 fsub/fadd 的折叠扩展到 fneg 以用于 FMF
https://reviews.llvm.org/D50996 : 将 select 折叠的二元运算折叠扩展到 true 和 false 二元运算标志交集
https://reviews.llvm.org/rL339938 : 为 select 折叠下的二元运算 FMF 传播添加了遗漏的情况
https://reviews.llvm.org/D51145 : 通过从 FPMathOperator 中排除一些 FP 运算符来保护 FMF 上下文
https://reviews.llvm.org/rL341138 : 添加 Node 到 Instruction 关联的初始交集测试
https://reviews.llvm.org/rL341565 : 为将 nsw、nuw 和 exact 添加为 MI 标志做准备
https://reviews.llvm.org/D51738 : 将 IR 标志添加到 MI
https://reviews.llvm.org/D52006 : 为 MI 标志更新和添加了复制实用程序
https://reviews.llvm.org/rL342598 : 向 DebugInfo lit 测试添加新标志
https://reviews.llvm.org/D53874 : [InstSimplify] 当 X 不是负数时,折叠 'fcmp nnan oge X, 0.0'
https://reviews.llvm.org/D55668 : 在 GlobalIsel 中为通用 fp 内在函数添加 FMF 管理
https://reviews.llvm.org/rL352396 : [NFC] TLI 查询,其默认(开启)行为针对 fmin/fmax 目标…
https://reviews.llvm.org/rL316753 (Fold fma (fneg x), K, y -> fma x, -K, y)
https://reviews.llvm.org/D57630 : 将 IR 标志处理直接移动到构建器调用中,用于从 GlobalIsel 中的指令翻译过来的情况
https://reviews.llvm.org/rL332756 : 添加了 unsafe 开启和关闭的基线 fp 折叠测试
https://reviews.llvm.org/rL334035 : NFC: 添加了 fmf 的基线 fneg 案例
https://reviews.llvm.org/rL325832 : [InstrTypes] 添加了带有 FMF 创建器的 frem 和 fneg
https://reviews.llvm.org/D41342 : [InstCombine] 遗漏了数学表达式中的优化:简化调用 exp 函数
https://reviews.llvm.org/D52087 : [IRBuilder] 修复 CreateIntrinsic 以允许为 Mangle 指定类型。
https://reviews.llvm.org/D52075 : [InstCombine] 支持 (sub (sext x), (sext y)) --> (sext (sub x, y)) 和 (sub (zext x), (zext y)) --> (zext (sub x, y))
https://reviews.llvm.org/rL338059 : [InstCombine] 使用来自带 nuw 的 muls 的公因子折叠 udiv
Commit: e0ab896a84be9e7beb59874b30f3ac51ba14d025 : [InstCombine] 允许使用 'reassoc' 的更多 fmul 折叠
Commit: 3e5c120fbac7bdd4b0ff0a3252344ce66d5633f9 : [InstCombine] 将 fmul 分配到 fadd/fsub 上
https://reviews.llvm.org/D37427 : [InstCombine] 将带有常量的 fcmp ord/uno 规范化为 null 常量
https://reviews.llvm.org/D40130 : [InstSimplify] 当操作数已知为 nnan 时,折叠 fcmp ord/uno 的 and/or
https://reviews.llvm.org/D40150 : [LibCallSimplifier] 修复 pow(x, 0.5) -> sqrt() 转换
https://reviews.llvm.org/D39642 : [ValueTracking] readnone 是将 sqrt 转换为 llvm.sqrt 的要求;nnan 不是
https://reviews.llvm.org/D39304 : [IR] 重新定义 'reassoc' 快速数学标志并添加 'trans' 快速数学标志
https://reviews.llvm.org/D41333 : [ValueTracking] 在检测到转换为整数的 fmin/fmax 模式时,忽略 FP 符号零
https://reviews.llvm.org/D5584 : 优化平方根平方 (PR21126)
https://reviews.llvm.org/D42385 : [InstSimplify] (X * Y) / Y --> X,用于放宽的浮点运算
https://reviews.llvm.org/D43160 : [InstSimplify] 允许仅使用 'reassoc' FMF 的 exp/log 简化
https://reviews.llvm.org/D43398 : [InstCombine] 允许使用小于完全 'fast' 运算的 fdiv 折叠
https://reviews.llvm.org/D44308 : [ConstantFold] fp_binop AnyConstant, undef --> NaN
https://reviews.llvm.org/D43765 : [InstSimplify] 为 sqrt(X) * sqrt(X) --> X 放宽 FMF
https://reviews.llvm.org/D44521 : [InstSimplify] fp_binop X, NaN --> NaN
https://reviews.llvm.org/D47202 : [CodeGen] 为 abs 使用 nsw 否定
https://reviews.llvm.org/D48085 : [DAGCombiner] 限制 (float)((int) f) --> ftrunc,不带符号零
https://reviews.llvm.org/D48401 : [InstCombine] 将带有常量操作数的 binops 的向量选择折叠为 1 个 binop (PR37806)
https://reviews.llvm.org/D39669 : DAG: 在重新关联添加时保留 nuw
https://reviews.llvm.org/D39417 : InstCombine: 在重新关联 nuw 操作时保留 nuw
https://reviews.llvm.org/D51753 : [DAGCombiner] 尝试将 pow(x, 1/3) 转换为 cbrt(x)
https://reviews.llvm.org/D51630 : [DAGCombiner] 尝试将 pow(x, 0.25) 转换为 sqrt(sqrt(x))
https://reviews.llvm.org/D53650 : [FPEnv] 最后 BinaryOperator::isFNeg(...) 对 m_FNeg(...) 的更改
https://reviews.llvm.org/D54001 : [ValueTracking] 在匹配 min/max FP 时,从 select 中确定 0.0 的符号
https://reviews.llvm.org/D51942 : [InstCombine] 如果可能,将 (C/x)>0 折叠为 x>0
https://llvm.net.cn/svn/llvm-project/llvm/trunk@348016 : [SelectionDAG] 将具有 2 个 undef 操作数的 FP binops 折叠为 undef
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346242 : 在折叠 fcmp+fpext 时传播快速数学标志,第 2 部分
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346240 : 在折叠 fcmp+fpext 时传播快速数学标志
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346238 : [InstCombine] 在折叠 fcmp+fneg 时传播快速数学标志,第 2 部分
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346169 : [InstSimplify] 折叠 select (fcmp X, Y), X, Y
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346234 : 在折叠 fcmp+fneg 时传播快速数学标志
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346147 : [InstCombine] 将 fcmp 中的 -0.0 规范化为 +0.0
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=346143 : [InstCombine] 为 fcmp+select 替换放松 FP 0.0 约束
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=345734 : [InstCombine] 重构 fabs+fcmp 折叠;NFC
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=345728 : [InstSimplify] 当 X 不为负时,折叠 'fcmp nnan ult X, 0.0'
https://llvm.net.cn/viewvc/llvm-project?view=revision&revision=345727 : [InstCombine] 添加断言,表明 InstSimplify 已折叠 fabs+fcmp;NFC


虽然许多人一直在努力对快速数学优化和其他放松的数值模式进行更细粒度的控制,但也有一些初步进展,即添加对更多约束数值模型的支持。在添加和启用约束浮点内在函数方面已取得重大进展,以捕获 FENV_ACCESS ON 和类似的语义模型。

这些实验性的约束内在函数禁止某些转换,如果默认浮点环境未生效,这些转换是不安全的。从历史上看,LLVM 在实践中基本上“取中间值”来处理此类转换;它们没有被明确禁止,因为 LLVM 没有对浮点环境进行建模,但当它们对测试或软件项目造成问题时,它们会被禁用。缺乏对许可这些转换的正式模型限制了我们启用它们的能力。在整个过程中完成约束内在函数的语言和后端支持将使我们能够包含我们今天出于实际目的而禁用的转换,并允许我们为开发人员提供一个简单的安全阀(以 FENV_ACCESS ON 和类似的语言控制的形式),当他们需要更精确的控制时,而不是一组用于传递给驱动程序的临时标志。

我们应该讨论这些新的内在函数,以确保它们能够捕获 LLVM 支持的所有语言的正确模型。


以下是一些可能的讨论主题

  • 对于调用图中的边,是否应在调用级别应用专业化,其中调用者具有特殊上下文以扩展到关于标志的被调用者中?
  • 内联器是否应该对满足内联标准的调用应用类似的东西?
  • 编译器的哪些其他部分可以利用目前未涵盖的 IR 标志?
  • 关于当前实现领域的代码债务需要做哪些工作。