我有一些在Release版本中编译的未知C ++代码,因此它已经过优化。我挣扎的一点是:
xor al, al
add esp, 8
cmp byte ptr [ebp+userinput], 31h
movzx eax, al
这是我的理解:
xor al, al ; set eax to 0x??????00 (clear last byte)
add esp, 8 ; for some unclear reason, set the stack pointer higher
cmp byte ptr [ebp+userinput], 31h ; set zero flag if user input was "1"
movzx eax, al ; set eax to AL and extend with zeros, so eax = 0x000000??
我不关心第2行和第3行。由于流水线原因,他们可能会按此顺序出现,而恕我直言与EAX无关。
但是,我不明白为什么我会首先清除AL,以便稍后清除EAX的其余部分。结果将是恕我直言EAX = 0
,所以这也可能是
xor eax, eax
代替。优势是什么?"优化"那段代码?
一些背景信息:
稍后我会得到源代码。它是一个简短的C ++控制台演示程序,可能只有20行代码,所以没有什么我称之为"复杂"码。 IDA在该程序中显示单个循环,但不包括此部分。 Stud_PE签名扫描没有找到任何内容,但可能是它的Visual Studio 2013或2015编译器。
答案 0 :(得分:3)
xor al,al
已经慢于xor eax,eax
。例如on Haswell/Skylake it needs an ALU uop and doesn't break the dependency on the old value of eax
/rax
。它在AMD CPU或Atom / Silvermont上同样糟糕。 (好吧,也许不一样,因为AMD没有消除xor eax,eax
的问题/重命名,但它仍然有一个错误的依赖,它可以使用最后使用的eax
序列化新的依赖链。
在将al
与寄存器的其余部分(Intel pre-IvyBridge)分开重命名的CPU上,xor al,al
可能仍会被识别as a zeroing idiom,但除非您主动要保留寄存器的高位字节,归零al
的最佳方法是xor eax,eax
。
在此基础上执行movzx
会让情况变得更糟。
我猜你的编译器有点混淆并决定它需要一个1字节的零,但后来意识到它需要将它提升到32位。 xor
设置了标记,因此在<{em> xor
之后无法cmp
- 零,并且它没有注意到它可能只有xor-zeroed {{ 1}}在eax
之前。
要么是something like Jester's suggestion,要么cmp
是分支目标。即使是这种情况,movzx
仍然会更好,因为在此代码路径上无条件地遵循零扩展到eax。
我很好奇编译器从什么来源产生了这个。