【发布时间】:2018-07-28 15:06:21
【问题描述】:
为了有效地执行x = x*10 + 1,使用它可能是最佳选择
lea eax, [rax + rax*4] ; x*=5
lea eax, [1 + rax*2] ; x = x*2 + 1
3-component LEA has higher latency 在现代 Intel CPU 上,例如3 个周期与 Sandybridge 系列上的 1 个周期相比,因此 disp32 + index*2 比 SnB 系列上的 disp8 + base + index*1 快,即我们关心优化的大多数主流 x86 CPU。 (这主要只适用于 LEA,不 加载/存储,因为 LEA 运行在 ALU 执行单元上,而不是大多数现代 x86 CPU 中的 AGU。)AMD CPU 的 LEA 较慢,只有 3 个组件或scale > 1 (http://agner.org/optimize/)
但是 NASM 和 YASM 将通过对第二个 LEA 使用 [1 + rax + rax*1] 来优化代码大小,它只需要一个 disp8 而不是一个 disp32。 (寻址模式总是有一个基址寄存器或 disp32)。
即他们总是将reg*2 拆分为base+index,因为这对于代码大小来说从未如此糟糕。
我可以强制使用带有lea eax, [dword 1 + rax*2] 的 disp32,但这并不能阻止 NASM 或 YASM 拆分寻址模式。 NASM 手册似乎没有记录在比例因子上使用the strict keyword 的方法,并且[1 + strict rax*2] 没有组装。 有没有办法使用strict 或其他语法来强制对寻址模式进行所需的编码?
nasm -O0 禁用优化不起作用。显然,它只控制多通道分支置换优化,而不是 NASM 所做的所有优化。当然,您首先不想对整个源文件执行此操作,即使它确实有效。我还是得到了
8d 84 00 01 00 00 00 lea eax,[rax+rax*1+0x1]
我能想到的唯一解决方法是使用db 手动对其进行编码。这很不方便。作为记录,手动编码是:
db 0x8d, 0x04, 0x45 ; opcode, modrm, SIB for lea eax, [disp32 + rax*2]
dd 1 ; disp32
比例因子在 SIB 字节的高 2 位中编码。我组装了lea eax, [dword 1 + rax*4] 以获得正确寄存器的机器代码,因为NASM 的优化只适用于*2。 SIB 是0x85,减少字节顶部的 2 位字段会将比例因子从 4 减少到 2。
但问题是:如何以一种易于阅读的方式编写它,使更改寄存器变得容易,并让 NASM 为您编码寻址模式?(我想一个巨大的宏可以通过文本处理和手动db 编码来做到这一点,但这并不是我正在寻找的答案。我现在实际上不需要这个,我主要想知道 NASM 或 YASM 是否有语法来强制这个.)
我知道的其他优化,比如 mov rax, 1 组装成 5 字节 mov eax,1 在所有 CPU 上都是纯粹的胜利,除非你想要更长的指令来获得没有 NOP 的填充,and can be disabled 和 mov rax, strict dword 1 以获得7 字节符号扩展编码,或 strict qword 用于 10 字节 imm64。
gas 不执行此操作或大多数其他优化(仅立即数和分支位移的大小):lea 1(,%rax,2), %eax 组装为8d 04 45 01 00 00 00 lea eax,[rax*2+0x1],.intel_syntax noprefix 版本相同。
不过,MASM 或其他汇编程序的答案也会很有趣。
【问题讨论】:
标签: assembly x86 nasm micro-optimization machine-code