为什么不允许从内存到内存的移动?

时间:2015-11-19 02:23:43

标签: assembly x86

我想知道在集会中是否允许这样做,

 movl (%edx) (%eax) 

我猜他会在第一个操作数中访问内存并输入 第二个操作数的记忆,类似于* a = * b,但我还没有看到任何处理这样的例子,所以我猜测它是不允许的。 另外,我被告知这是不允许的

 leal %esi (%edi)

为什么?最后,还有其他类似的功能我应该知道是不允许的。

2 个答案:

答案 0 :(得分:5)

movl (mem), (mem)

mov dword [eax], [ecx]    ; or the equivalent in Intel-syntax

无效,因为 x86机器代码没有an encoding for mov有两个地址。它有mov r32, r/m32mov r/m32, r32。可以使用mov r32, r/m32操作码或mov r/m32, r32操作码对Reg-reg移动进行编码。许多其他指令有两个操作码,一个是dest必须是寄存器,另一个是src必须是寄存器。

(还有一些专门的表单,例如mov r32, imm32movabs r64, [64bit-absolute-address]。)

请参阅x86指令集参考手册(x86标记wiki https://stackoverflow.com/tags/x86/info中的链接)。我在这里使用了Intel / NASM语法,因为这就是insn ref手册所做的。

很少有指令可以加载并存储到两个不同的地址,例如movs(字符串移动)和push/pop (mem)What x86 instructions take two (or more) memory operands?)。许多ALU指令都有内存目标,这使得它们可以在单个内存位置进行读 - 修改 - 写入。

没有指令采用两个任意有效地址(即使用灵活的寻址模式指定)。 movs具有隐式源和dest操作数,push具有隐式dest(esp)。

x86指令最多只有一个ModRM字节,而一个ModRM只能编码一个寄存器/存储器操作数(模式为2位,基址寄存器为3位),另一个只有寄存器操作数(3位)。使用转义码,ModRM可以发信号通知SIB字节为内存操作数编码base + scaled-index,但仍然只有一个内存来编码一个内存操作数。

正如我上面提到的,同一指令的内存源和内存目的形式(asm源助记符)使用两种不同的操作码。就硬件而言,它们是不同的指令

这种设计选择的原因可能部分是实现复杂性:如果单个指令可能需要来自AGU(地址生成单元)的两个结果,那么必须使用布线来实现这一点。这种复杂性中的一些是在解码器中找出操作码是哪个指令,并解析剩余的位和字节以找出操作数是什么。由于没有其他指令可以有多个r/m操作数,因此需要额外的晶体管(硅片面积)来支持它。

它还可能给出一个指令五个输入依赖项(存储地址的双寄存器寻址模式,加载地址和加载日期相同)。当设计8086/80386时,超标量/无序/依赖性跟踪可能不在雷达上。 386添加了许多新指令,因此mov的mem-to-mem编码可能已经完成,但事实并非如此。如果386已经开始将结果直接从ALU输出转发到ALU输入和类似的东西(与总是将结果提交到寄存器文件相比减少延迟),那么这个原因将是它未被实现的原因之一。 / p>

如果它存在,英特尔P6可能会将其解码为两个单独的uop,一个加载和一个商店。现在或者在1995年之后的任何时候,当P6被设计并且更简单的指令比复杂的指令获得更多的速度优势时,它当然是没有意义的。 (有关使代码快速运行的信息,请参阅http://agner.org/optimize/。)

无论如何,我看不出这是非常有用的。 如果你想要这个,你可能没有充分利用寄存器。如果可能的话,弄清楚如何在复制时动态处理你的数据。当然,有时你只需要加载然后是商店,例如在一个排序例程中,在基于一个成员进行比较之后交换结构的其余部分。使用xmm寄存器在较大的块中移动是个好主意。

leal %esi, (%edi)

这里有两个问题:

首先,寄存器没有地址。裸%esi不是有效的有效地址。

其次,lea的目的地必须是注册。没有编码需要第二个有效地址来将目标存储到内存中。

顺便说一句,两者都没有效,因为你在两个操作数之间遗漏了,

valid-asm.s:2: Error: number of operands mismatch for `lea'

答案的其余部分仅在修复语法错误后讨论代码。

答案 1 :(得分:1)

无效。除了一组有限的操作数之外,您可能无法直接在我熟悉的任何体系结构上执行内存到内存移动。例如,通过英特尔兼容处理器上的moveSI寄存器,字符串DI等异常,但应避免这些(见下文)。大多数架构都有一些东西可以帮助这些有限的记忆内存移动。

如果考虑硬件,这很有意义。有地址线和数据线。处理器通知地址线上要访问的存储器地址,然后通过数据线读取或写入数据。因为这个数据必须通过缓存或处理器才能到达其他内存。事实上,如果你看一下第145页的this reference,你会看到MOVS及其朋友绝对不能使用的强烈声明:

  

注意,REP MOVS指令将一个字写入   目的地,它在同一时钟中从源读取下一个字   周期。如果位2-4相同,则可能存在缓存库冲突   这两个地址在P2和P3上。换句话说,你会得到一个   如果ESI + WORDSIZE-EDI是,则每次迭代额外增加一个时钟   可被32整除。避免缓存库冲突的最简单方法是   将源和目标对齐8.永远不要使用MOVSB或MOVSW   优化的代码,甚至不是16位模式。

     

在许多处理器上,REP MOVS和REP STOS可以通过移动快速执行   一次16个字节或整个高速缓存行。这种情况只发生在   满足某些条件。取决于处理器,条件   通常,快速字符串指令必须是计数   高,源和目的地必须对齐,方向必须   向前,源和目的地之间的距离必须是   至少缓存行大小,以及源和的内存类型   目的地必须是回写或写入组合(你可以   通常假设满足后一条件。)

     

在这些条件下,速度与您可以获得的速度一样高   向量寄存器在某些处理器上移动甚至更快。虽然   字符串指令可以很方便,必须强调   在许多情况下,其他解决方案更快。如果符合上述条件   如果没有满足快速移动那么使用其他可以获得很多收益   方法

在某种意义上,这也解释了注册移动的注册是否正常(尽管还有其他原因)。也许我应该说,它解释了为什么他们不需要板上非常特殊的硬件......寄存器都在处理器中;没有必要通过地址访问总线进行读写。