GCC 如何将 switch 语句转化为高效代码

Switch Lowering in GCC

switch 语句看似简单,背后却藏着编译器的复杂优化逻辑。在 GCC 中,编译器会根据 case 标签的分布密度,智能选择跳转表、比较树或位集测试等不同策略。例如,当 case 值密集时,GCC 会生成跳转表;若分布稀疏,则转为比较树;甚至能利用 bitset 指令处理非连续值。文章通过多个代码示例,揭示了 GCC 在 switch 语句降低过程中的启发式规则,比如“每 8 个 case 标签对应一个跳转表条目”的阈值判断,以及如何处理异常值和连续块。这些细节不仅展示了编译器的智能,也为开发者优化代码提供了新思路。

编译器的直觉告诉我们,case 值越稀疏,跳转表的转换就越不划算。
  1. LarsKrimi

    > sparse_8 中的 switch 语句可以通过在跳转前先对 x 除以八来简化。有趣的是,gcc 并没有发现这种简化。

    它之所以做不到,是因为那样会导致每个 case 的 7 个值都由函数调用来处理,而不是由 default case 处理——0/8 是 0,但 3/8 也是 0

  2. erk__

    我曾经尝试过一个非常大的 switch 语句(把所有 MAC 地址制造商都放在一个 switch 里),在 LLVM 中,如果不加优化选项,它能正常编译;但一旦开启优化,编译时间就会长达半小时左右。我觉得 GCC 当时做得更快一些,不过那是很久以前的事了。在那个 Rust crate 中,正是出于这个原因,我最终采用了完美哈希(perfect hashing),编译速度快得多。

  3. rurban

    clang 和 gcc 真是丢脸!它们至今仍未为大型稀疏 switch 表实现完美哈希。我最近在我的 rcc C 编译器里实现了这个功能,只花了一天时间。这对 Unicode 工作尤其有益。LLVM 的相关补丁早在十年前就被拒绝了。GCC 有几个 Unicode 补丁一直挂在分支里,它们也同样需要这个功能。

  4. tsukikage

    为什么要有间接跳转?为什么不直接让跳转表引用 f0..fX,而不是跳转到另一个跳转指令?

  5. p-t

    我猜 compact_7() 里的 34 应该是 35,而 bittest() 里的 edx 本该在某个地方被用到(除非我漏掉了什么 lol)

同日更多故事

2026-10-31