【问题标题】:How to force NASM to encode [1 + rax*2] as disp32 + index*2 instead of disp8 + base + index?如何强制 NASM 将 [1 + rax*2] 编码为 disp32 + index*2 而不是 disp8 + base + index?
【发布时间】: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 disabledmov 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


    【解决方案1】:

    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]
    

    【讨论】:

    • 谢谢,我想我记得在某处看到过这个的语法。我今天搜索时错过了它,因为我认为它会涉及strict。 (而且我并没有非常努力地搜索,因为我想在 SO 上写下性能部分:P)
    • 欢迎您@Peter。 Nasm doc 缺少 IMO 关键字词汇表,我不得不查看源代码。我在看其他汇编程序做了什么:TASM 没有优化,YASM 有 NOSPLIT,MASM 5 或更早版本不应该优化,新的 MASM 我不知道(不确定我是否可以在没有 Visual 的情况下找到它Studio 并使其在 Debian 上运行)。
    • 嗯,是的,词汇表和现有的 index 一样有用,你必须知道你在寻找什么。事后看来,看看手册的“有效地址”部分会很有意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多