select是llvm ir中用于纯值选择的非终端指令,不改变控制流;br是终端指令,用于真正跳转,必须配对标签和phi节点合并结果。

select 和 br 都能实现条件逻辑,但它们不是替代关系——用错一个,轻则生成冗余代码,重则破坏 SSA 形式或触发后端非法指令。
select 适用于纯值选择,不改变控制流
select 是一个非终端(non-terminator)指令,它只计算一个值,不跳转、不分割基本块。典型场景是:两个分支计算出不同结果,你只想选其一赋给变量,且后续代码仍按顺序执行。
常见错误:用
select替代带副作用的分支(比如调用函数、store 内存、修改全局状态)它要求两个操作数类型完全一致,且不能含 control-dependent 行为
-
示例:
%cond = icmp eq i32 %a, 0 %val = select i1 %cond, i32 42, i32 -1
这里%val直接得到结果,整个基本块仍是线性结构。 后端注意:某些目标(如 RISC-V without Zbs)可能不支持硬件级
select指令,此时SelectionDAG会在Legalize阶段把它拆成br+phi,反而增加开销。
br 是真正的控制流分叉,必须用于跳转语义
br 是终端(terminator)指令,每个基本块只能有一个,且强制结束当前控制流路径。只要逻辑上存在“走 A 路或走 B 路”的意图,就必须用 br 或 switch。
常见错误:在循环条件判断中漏掉
br,只写icmp,导致 IR 非法(LLVM verifier 会报"Basic block must end with terminator instruction")条件分支必须配对使用:一个
br i1 %cond, label %t, label %f,后面得有%t:和%f:两个标签块-
br自身不产生值,要合并分支结果必须靠phi节点,例如:br i1 %cond, label %true, label %false true: %x = add i32 %a, 1 br label %merge false: %x = sub i32 %a, 1 br label %merge merge: %result = phi i32 [ %x, %true ], [ %x, %false ]
性能提示:现代 CPU 的分支预测器对短跳转很友好,但过度拆分基本块(比如把本可
select的地方硬写成br)会增加指令缓存压力和调度难度。
select_cc 是后端内部用的,前端一般不直接写
select_cc 不是标准 IR 指令,而是 SelectionDAG 层的 SDNode,在 XXXISelLowering.cpp 中用于合法化阶段处理带比较的条件选择(比如 x86 的 cmov 类指令)。
- 你在手写 .ll 文件或 frontend 生成 IR 时不会也不该出现
select_cc - 它由
Lower阶段从icmp+select组合推导而来,属于后端模式匹配环节的中间表示 - 如果你在调试时看到它,说明你的
select已被 lowering 成 target-specific 形式,下一步就是Legalize和Select
真正容易被忽略的是:select 的 operand 必须是 SSA 值,不能是 label、block 地址或未定义行为表达式。一旦某个 operand 本身依赖于未执行的分支路径(比如在 %t 块里定义的值,却出现在 %f 块的 select 中),LLVM verifier 就会拒绝该模块。这种错误往往在手工编写 IR 或做非常规优化 pass 时才暴露。











