将寄存器设置为零的方法有多少?
我很好奇有多少种方法可以在x86汇编中将寄存器设置为零。使用一条指令。有人告诉我,他设法找到了至少10种方法。
我能想到的是:
xor ax,ax
mov ax, 0
and ax, 0
没有找到相关结果
已邀请:
7 个回复
感秆暴壳
也许最奇怪的...... :)
和...
编辑: 对于16位x86 cpu模式,未经测试......:
和...
如果ds段寄存器不等于cs段寄存器,则cs:前缀是可选的。
磨标烫徽啪
(性能优势和较小的编码)。 我将只考虑单个指令可以将寄存器归零的方式。如果允许从内存中加载零,有太多方法,所以我们主要排除从内存加载的指令。 我找到了10个不同的单个指令,它们将32位寄存器归零(因此在长模式下为完整的64位寄存器),没有任何其他存储器的前置条件或负载。这不计算相同insn的不同编码,或不同形式的
。如果你计算从已知存储零的存储器加载,或者从段寄存器或其他任何东西加载,那么就有很多方法。零矢量寄存器也有很多种方法。 对于大多数这些版本,eax和rax版本是针对相同功能的单独编码,它们都将整个64位寄存器归零,或者隐式地将上半部分归零,或者使用REX.W前缀显式写入完整寄存器。 整数寄存器:
“将所有位移出一端”对于常规大小的GP寄存器是不可能的,只有部分寄存器是不可能的。
和
移位计数被屏蔽:
,相当于
。 (但是286和更早版本只有16位,所以
是一个“完整”寄存器。指令的
可变计数形式加了286,所以有一些CPU可以将一个整数寄存器归零。) 另请注意,向量的移位计数饱和而不是换行。
取决于其他现有条件(除了在另一个注册表中为零):
矢量注册: 其中一些SSE2整数指令也可用于MMX寄存器(
-
)。同样,最好的选择是某种形式的xor。要么
/
,要么
/
。 AVX
全部为ymm0 / zmm0,并且在AMD CPU上优于
。这些归零指令有三种编码:传统SSE,AVX(VEX前缀)和AVX512(EVEX前缀),尽管SSE版本仅将底部128归零,这不是支持AVX或AVX512的CPU上的完整寄存器。无论如何,根据你的计算方式,每个条目可以是三个不同的指令(相同的操作码,只是不同的前缀)。除了
,AVX512没有改变(并且没有zmm16-31为零)。
和类似物不起作用,因为NaN-NaN = NaN,而不是零。 此外,FP指令可以引发NaN参数的异常,因此即使您知道掩码异常,CMPPS / PD也是安全的,并且您不关心可能在MXCSR中设置异常位。即使是AVX版本,随着谓词的扩展选择,也会在SNaN上提高
。 “安静”的谓词只能抑制QNaN的
。 CMPPS / PD也可以引发Denormal异常。 (请参阅insn set refact entry for CMPPD中的表格,或者最好是英特尔原始PDF格式,因为HTML摘录会破坏该表格。) AVX512: 这里可能有几种选择,但我现在还不够好奇,要深入挖掘指令集列表,寻找所有这些选项。 但有一个值得一提的有趣的是:VPTERNLOGD / Q可以将寄存器设置为all-1,而imm8 = 0xFF。 (但在当前实现上对旧值具有错误的依赖性)。由于比较指令都比较为掩码,在我的测试中,VPTERNLOGD似乎是在Skylake-AVX512上将矢量设置为全1的最佳方法,尽管不特殊情况下imm8 = 0xFF情况以避免错误依赖。
x87 FP: 只有一个选择(因为如果旧值是无穷大或NaN,则sub不起作用)。
梆晨灸碾
编辑:我应该注意到
并不是全部为
- 它只是零
(加上前16位不能作为寄存器访问)。 至于最快,如果记忆服务
和
是等价的。它们比(大多数)其他人更快,因为它们很常见,CPU设计者为它们添加了特殊优化。具体而言,对于正常的
或
,结果取决于寄存器中的先前值。 CPU特别识别xor-with-self和subtract-from-self,因此它知道依赖链在那里被破坏。之后的任何指令都不依赖于任何先前的值,因此它可以使用重命名寄存器并行执行先前和后续指令。 特别是在较旧的处理器上,我们预计'mov reg,0'会因为它有额外的16位数据而变慢,大多数早期处理器(特别是8088)主要受限于它们从内存加载流的能力 - - 实际上,在8088上,您可以使用任何参考表非常准确地估计运行时间,并且只需注意所涉及的字节数。这确实打破了
和
指令,但就是这样。 OTOH,我应该闭嘴,因为8088确实对任何人都没什么兴趣(现在至少十年)。
坍锭嘉韭蓝
将寄存器CX设置为0。
郡晒景沧
设置为正整数,你可以用
将
设置为0(这个技巧用于着名的24字节shellcode,出现在“不安全的编程实例”中)。
骚瓤
更复杂的组合:
导力疵谜