LLVM IR 转 SMT公式

为什么需要LLVM IR 转SMT公式

LLVM IR中有两个特殊的值:

  • undef:表示"未定义",可以是任意值,而且每次读取都可能得到不同的值。
  • poison:表示"毒化",是更受限的未定义,通常由算术溢出等操作产生,一旦使用就会导致UB(未定义行为)。

在验证优化时,我们必须精确地处理这些特殊值,否则可能会把正确的优化误判为错误,或者反之。SMT编码就是用数学逻辑来描述这些语义。

编码一个寄存器的值

对于每个虚拟寄存器(比如 %a),我们用一个有序对来表示它的状态:

text 复制代码
R[%a] = (实际值, is_poison)
  • 实际值:如果该寄存器当前是undef,则用一个新创建的SMT变量(如undef_1)表示;否则就是它原本的LLVM值(也可能是poison,但poison单独用标志表示)。
  • is_poison:布尔值,表示该寄存器当前是否处于poison状态。

例子:简单函数

考虑以下LLVM函数

llvm 复制代码
define i32 @test(i32 %a) {
  %t = add i32 %a, %a          ; t = a + a
  %c = icmp eq i32 %t, 0       ; c = (t == 0)
  %q = shl i32 %a, 2           ; q = a << 2
  %r = and i32 %a, 1           ; r = a & 1
  ret i32 %r                   ; 注意这里返回的是%r,但原文有分支
}

改成带分支

llvm 复制代码
define i32 @test(i32 %a) {
  %t = add i32 %a, %a
  %c = icmp eq i32 %t, 0
  br i1 %c, label %then, label %else

then:
  %q = shl i32 %a, 2
  ret i32 %q

else:
  %r = and i32 %a, 1
  ret i32 %r
}

现在按照文本中的方式编码每个寄存器(假设%a不是undef也不是poison,仅用于展示):

%a:R%a = (ite(is_undef_a, undef_1, %a), is_poison_a) 意思是:

  • 如果%a是undef(is_undef_a为真),则实际值取新变量undef_1;否则取原值%a。同时用一个布尔is_poison_a记录它是否是poison。

%t = add %a, %a :加法结果的实际值 = ite(is_undef_t1, undef_2, R%a.val) + ite(is_undef_t2, undef_3, R%a.val) -

  • 即如果某个操作数是undef,就引入新变量
  • 否则用该寄存器的实际值。
  • 结果的is_poison标志根据加法规则传播(例如如果任何操作数是poison或加法溢出则设为poison)。

%c = icmp eq %t, 0:LLVM中布尔用i1表示,所以%c的实际值是一个ite:

  • ite( (R%t.val == 0), 1, 0 ) 同时其is_poison标志取决于%t是否是poison。

%q = shl %a, 2

  • 原文说"由于非负定义值上分支是UB,我们可以假设%t是负定义的,进而%a也是负定义的"。
  • 这里涉及优化:因为shl要求左移计数必须小于位宽,否则是UB。
  • 但编码中可能忽略undef/poison情况,认为%a已经是非负定义值(即不是undef也不是poison),所以直接编码为shl(%a, 2)且is_poison=false。
  • 这正是文本中"忽略了%a是undef或poison的情况"的含义。

%r = and %a, 1

  • and操作:实际值 = R[%a].val & 1,is_poison传播。

编码函数的最终状态

函数可以有多个返回点,也可能永不返回(如无限循环或noreturn函数)。因此最终状态需要编码:

  • 返回值(retval):是一个ite树,根据控制流路径选择对应返回指令的值。
  • ub:表示是否存在未定义行为(如使用了poison或执行了UB操作)。
  • noret:布尔值,表示函数是否可能不返回(例如所有路径都陷入无限循环)。

对于上述分支函数,返回值可以写成:

text 复制代码
retval = ite( %t == 0,
              R[%q].val,      // then分支的返回值
              R[%r].val )     // else分支的返回值

同时ubnoret分别根据路径条件组合。

举例说明优化

假设我们分析得知,当%t == 0时,shl %a, 2操作是UB(比如因为%a是poison或者移位计数越界),那么整个then分支的行为就是UB。

因此,在最终的ite表达式中,我们可以直接省略该分支的贡献,甚至将整个返回值简化为else分支的值,因为UB情况下的返回值无意义。

这种简化能大大降低SMT求解器的复杂度,使验证更可行。

相关推荐
咖啡八杯9 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
优橙教育9 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵9 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
腻害兔11 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
SeaTunnel11 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
碎光拾影11 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Marst Code12 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
Miao1213112 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔12 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
CRMEB系统商城13 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源