首页 文章

如何强制NASM将[1 rax * 2]编码为disp32索引* 2而不是disp8基本索引?

提问于
浏览
6

为了有效地做到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关于现代英特尔CPU,例如3个周期与Sandybridge系列的1个周期相比,所以 disp32 + index2 is faster than disp8 + base + index1 on SnB-family ,即我们关心优化的大多数主流x86 CPU . (这主要仅适用于LEA,而不适用于加载/存储,因为LEA在ALU执行单元上运行,而不是在大多数现代x86 CPU中运行 . )AMD CPU具有3个组件的LEA较慢或 scale > 1http://agner.org/optimize/

但是NASM和YASM将使用 [1 + rax + rax*1] 为第二个LEA优化代码大小,它只需要一个disp8而不是一个disp32 . (寻址模式始终具有基址寄存器或disp32) .

即他们总是将 reg*2 分成 base+index ,因为对于代码大小来说,这永远不会更糟 .

我可以强制使用带有 lea eax, [dword 1 + rax*2] 的disp32,但是这似乎没有记录在比例因子上使用the strict keyword的方法,并且 [1 + strict rax*2] 没有汇编 . Is there a way to use strict or some other syntax to force the desired encoding of an addressing mode


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 .


但问题是: how to write it in a nicely readable way that makes it easy to change registers, and get NASM to encode the addressing mode for you? (我想一个巨大的宏可以通过文本处理和手动 db 编码实现这一点,但是's not really the answer I' m正在寻找 . 我现在实际上并不需要这个,我主要是想知道NASM还是YASM有语法强迫这个 . )

我所知道的其他优化,如 mov rax, 1 汇编到5字节 mov eax,1 是所有CPU上的纯胜利,除非你想要更长的指令来获得没有NOP的填充,and can be disabledmov rax, strict dword 1 来获得7字节符号扩展编码,或 strict qword for 10字节的imm64 .


天然气不做这个或大多数其他优化(只有立即和分支位移的大小): lea 1(,%rax,2), %eax 汇编到
8d 04 45 01 00 00 00 lea eax,[rax*2+0x1] ,和 .intel_syntax noprefix 版本相同 .

但是MASM或其他汇编程序的答案也会很有趣 .

1 回答

  • 7

    NOSPLIT

    同样,NASM会将[eax * 2]拆分为[eax eax],因为它允许偏移字段不存在并节省空间;实际上,它还会将[eax * 2 offset]拆分为[eax eax offset] . 您可以通过使用NOSPLIT关键字来解决此问题:[nosplit eax * 2]将强制生成[eax * 2 0]字面 . [nosplit eax * 1]也有同样的效果 . 换句话说,也可以使用拆分EA表单[0,eax * 2] . 但是,[nosplit eax eax]中的NOSPLIT将被忽略,因为此处的用户意图被视为[eax eax] .

    lea eax, [NOSPLIT 1+rax*2]
    lea eax, [1+rax*2]
    
    00000000  8D044501000000    lea eax,[rax*2+0x1]
    00000007  8D440001          lea eax,[rax+rax+0x1]
    

相关问题