我知道操作数为零的ret
imm16
(C2 imm16
)指令与无操作数的ret
(C3
)指令在效果上没有区别,但是,当我显式给予汇编程序ret 0
时,既然我显式提供了操作数,它是否应该将其编码为ret imm16
指令?
如果我使用VS2019附带的ml.exe版本和命令ml file.asm /link /SUBSYSTEM:CONSOLE /ENTRY:stdMain
汇编以下代码
.386
.MODEL FLAT, STDCALL
.CODE
stdMain PROC
xor eax, eax
ret 0
stdMain ENDP
END
然后用反汇编程序打开可执行文件,我看到为ret
编码的指令是C3
:
00401000: 33 C0 xor eax,eax
00401002: C3 ret
我可以通过硬编码字节来手动强制C2
指令:
.386
.MODEL FLAT, STDCALL
.CODE
stdMain PROC
xor eax, eax
db 0c2h, 0, 0 ; ret imm16=0
stdMain ENDP
END
现在我在反汇编输出中看到C2
指令:
00401000: 33 C0 xor eax,eax
00401002: C2 00 00 ret 0
汇编程序这样“优化”是正确的吗?
1条答案
按热度按时间jdzmm42g1#
您不需要3个单独的
db
行;具有3个操作数的一个db
是等效的:汇编程序这样"优化"是正确的吗?
一般来说,是的,汇编器可以使用最短的指令编码,它具有完全相同的体系结构效果,并具有相同的助记符。
例如,NASM会将
mov rax, 123
优化为mov eax, 123
,即使其他一些(如YASM或GAS)默认情况下不会。(GAS有一个-Os
选项,GCC默认情况下不会传递给它)。此外,NASM会将lea eax, [rax*2 + 123]
优化为lea eax, [rax + rax + 123]
,除非您使用[NOSPLIT 123 + rax*2]
到spend more code size on a disp32 for the benefit of avoiding a slower 3 component LEA。不过,NASM并没有将
xor rax,rax
优化为xor eax,eax
;我猜它不会用XOR检查零化习惯用法(两个规则相同)。NASM有一个不优化
-O0
的选项,但这是非常糟糕的,例如mov rax, -1
是10字节(imm64)而不是7字节(sign_extended_imm32),add ecx, 123
使用imm32,jmp foo
使用rel32而不是rel8,即使标签就在附近。(这在旧的NASM版本中曾经是默认值。https://nasm.us/doc/nasmdoc2.html#section-2.1.24)ret
作为ret 0
发出,所以如果你曾经汇编过这样的代码,你肯定希望汇编器将其优化为普通的ret
。**显然,MS认为这种优化是一种正常的依赖。当您需要
ret
时,编写或发出ret 0
似乎是一个愚蠢的设计,但这正是MSVC所做的(MSVC并不是通过将asm提供给MASM来工作;它直接发出机器码,除非你要求一个asm清单。)NASM碰巧将
ret 0
组装为ret imm16=0
,所以您可能更喜欢使用它。我知道,在任何时候,我都会选择NASM而不是MASM;简单的语法和没有关于内存操作数的魔术规则意味着操作数大小,有时[]
没有任何意义...