为什么清零寄存器用 XOR 而不用 SUB?

Hacker News Top 新闻

摘要

Raymond Chen 探讨了 x86 编译器为何普遍使用“xor eax,eax”而非“sub eax,eax”来清零寄存器,原因并非技术优越,而是历史惯性和略安全的标志位行为。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/04/22 07:19

# 当然,用 xor 把寄存器与自身异或清零是惯用手法,可为啥不用 sub? - The Old New Thing 来源:https://devblogs.microsoft.com/oldnewthing/20260421-00?p=112247 Matt Godbolt(https://xania.org/MattGodbolt),最广为人知的身份是 Compiler Explorer(https://compiler-explorer.com/)的站长,曾写过一篇短文解释为何 x86 编译器钟爱 `xor eax, eax` 这条指令(https://xania.org/202512/01-xor-eax-eax)。 答案很简单:这是 x86 上将寄存器清零最紧凑的办法。比起更直观的 `mov eax, 0`,它省了好几字节,因为不必编码四字节的立即数。x86 架构没有专门的零寄存器,所以要想清零,只能“从零开始”。 但 Matt 没解释,为啥大家都选 `xor`,而不用其他必定得零的运算?具体来说,`sub eax, eax` 哪里不好?编码长度一样,周期数一样,对标志位甚至更友好: | 指令 | OF | SF | ZF | AF | PF | CF | |-----------------|-----|-----|-----|--------|-----|-----| | xor eax, eax | 清 | 清 | 置 | 未定义 | 置 | 清 | | sub eax, eax | 清 | 清 | 置 | 清 | 置 | 清 | 可见 `xor eax, eax` 把 AF 标志搞成未定义,而 `sub eax, eax` 干脆把它清零。 我不知道 `xor` 为何胜出,但怀疑只是“从众效应”。 在我脑补的历史里,`xor` 与 `sub` 起初平分秋色,可 `xor` 因某个偶然稍占上风——也许因为它看起来更“聪明”。 早期编译器一旦用 `xor` 清零,雪球就开始滚:人们看到编译器生成 `xor`,便想“编译器作者这么聪明,肯定懂我不懂的东西。既然我原本犹豫,这点小证据就足以让我倒向 `xor`。” 当这种惯用法遍地开花后,Intel 在指令解码前端给 `xor r,r` 和 `sub r,r` 都加了特殊检测,直接把目标寄存器重命名为内部零寄存器,指令干脆不送进执行单元。某种意义上,它“零周期”完成。前端检测还打破了依赖链:正常 `xor` 或 `sub` 的输出依赖输入,但自反情况下输出必为零,与输入无关。 尽管 Intel 对两条指令都做了优化,Stack Overflow 仍担心其他 CPU 厂商可能只特判了 `xor` 而没理 `sub`(https://stackoverflow.com/questions/4829937/how-many-ways-to-set-a-register-to-zero),于是 `xor` 在这场其实无所谓的战争里笑到最后。 一旦某条指令占据哪怕极微弱的优势,就足以让所有人站队。 **彩蛋闲聊**:我的一位前同事(https://github.com/jeffpar)偏爱用 `sub r,r` 清零,我看汇编代码时,只要见到 `sub` 而不是更流行的 `xor`,就知道是他写的。 **彩蛋的彩蛋**:Itanium 上 `xor` 把戏行不通,因为数学运算不会重置 NaT 位(https://devblogs.microsoft.com/oldnewthing/20150804-00/?p=91181)。好在 Itanium 有专门的零寄存器(https://devblogs.microsoft.com/oldnewthing/20150727-00/?p=90821),直接往目标寄存器里 mov 零即可。 ## 作者 Raymond Chen Raymond 参与 Windows 演化已逾 30 载。2003 年,他开了个叫“The Old New Thing”的网站,火爆程度远超他最狂野的想象,至今让他心里发毛。网站后来出了一本同名书(Addison Wesley 2007)。他偶尔在 Windows Dev Docs 推特账号上讲故事,内容毫无营养。

相似文章

Windows堆栈限制检查回顾,后续

The Old New Thing (Raymond Chen)

Raymond Chen跟进了他之前关于ARM64堆栈限制检查的文章,指出了堆栈探测函数中x15寄存器的非常规使用细节,并比较了多个架构的寄存器使用。

80386 早期启动内存访问

Hacker News Top

本文解释了 Intel 80386 中的早期启动内存访问技术,该技术通过将地址生成与前一条指令的最后一个周期重叠来隐藏内存延迟。文章描述了该技术在 z386 FPGA 核心中的实现,达到了 ao486 级别的性能,并在 Doom FPS 上提升了 39%。