我想知道这是否允许组装
movl (%edx) (%eax)
Run Code Online (Sandbox Code Playgroud)
我猜想它会访问第一个操作数的内存,然后放入第二个操作数的内存,类似* a = * b,但是我没有看到任何处理此类的示例,因此我猜想它是不允许的。另外,有人告诉我这是不允许的
leal %esi (%edi)
Run Code Online (Sandbox Code Playgroud)
这是为什么?最后,还有其他类似的功能我不应该被禁止。
movl (mem), (mem)
mov dword [eax], [ecx] ; or the equivalent in Intel-syntax
Run Code Online (Sandbox Code Playgroud)
无效,因为x86机器代码没有使用两个地址进行编码mov。它有mov r32, r/m32和mov r/m32, r32。可以使用mov r32, r/m32操作码或mov r/m32, r32操作码对Reg-reg移动进行编码。许多其他指令有两个操作码,一个在其中dest必须是一个寄存器,而另一个在src必须是一个寄存器。
(并且有一些特殊的形式,例如mov r32, imm32或movabs r64, [64bit-absolute-address]。)
请参阅《 x86指令集参考手册》(x86标签Wiki中的链接https://stackoverflow.com/tags/x86/info)。我在这里使用Intel / NASM语法是因为insn ref手册就是这样做的。
很少有指令可以加载并存储到两个不同的地址,例如movs(string-move)和push/pop (mem)(哪些x86指令需要两个(或多个)内存操作数?)。许多ALU指令可用于存储目标,这使它们可以在单个存储位置上进行读-修改-写操作。
没有指令采用两个任意的有效地址(即使用灵活的寻址模式指定)。 movs具有隐式源和dest操作数,并push具有隐式dest(esp)。
x86指令最多具有一个ModRM字节,而ModRM只能编码一个reg /内存操作数(2位用于模式,3位用于基址寄存器)和另一个仅寄存器的操作数(3位)。使用转义码,ModRM可以发信号通知SIB字节以对存储操作数的基数+标度索引进行编码,但是仍然只有空间可以对一个存储操作数进行编码。
如上所述,同一条指令(asm源助记符)的内存源和内存目标形式使用两个不同的操作码。 就硬件而言,它们是不同的指令。
选择这种设计的原因可能部分是由于实现复杂性:如果一条指令可能需要一个AGU(地址生成单元)的两个结果,那么必须进行布线以使其成为可能。这种复杂性的一部分在于解码器,该解码器确定操作码是哪条指令,然后解析其余的位和字节以找出操作数。由于没有其他指令可以具有多个r/m操作数,因此支持它会花费额外的晶体管(硅面积)。
它还可能给一条指令五个输入依赖项(商店地址的两个寄存器寻址模式,加载地址和加载日期相同)。当设计8086/80386时,超标量/无序/依赖项跟踪可能不在雷达范围内。386添加了许多新指令,因此可以完成的内存到内存编码mov,但是没有完成。如果386已经开始将结果直接从ALU输出转发到ALU输入和类似的东西(与将结果始终提交到寄存器文件相比,减少了等待时间),则该原因将是未实现的原因之一。
如果存在,Intel P6可能会将其解码为两个单独的uops,即负载和存储。现在或者在1995年之后的任何时候引入P6肯定是没有道理的,因为P6的设计比简单的指令在速度上更具优势。(有关使代码快速运行的信息,请参见http://agner.org/optimize/。)
无论如何,我看不出这很有用。 如果需要,可能没有充分利用寄存器。 弄清楚如何在复制时动态处理数据。当然,有时您只需要先进行装载然后进行存储即可,例如在排序例程中,以便在根据一个成员进行比较之后交换结构的其余部分。使用xmm寄存器在较大的块中移动是个好主意。
leal %esi, (%edi)
Run Code Online (Sandbox Code Playgroud)
这里有两个问题:
首先,登记不具有地址。裸露%esi不是有效的有效地址。
其次,lea的目的地必须是寄存器。没有编码需要第二个有效地址来将目标存储到内存中。
顺便说一句,这两个都是无效的,因为您省略了,两个操作数之间的。
valid-asm.s:2: Error: number of operands mismatch for `lea'
Run Code Online (Sandbox Code Playgroud)
答案的其余部分仅在修复该语法错误后才讨论代码。
这是无效的。除了一组有限的操作数之外,您不得在我熟悉的任何体系结构上直接执行内存到内存的移动。例如,例外情况是通过 Intel 兼容处理器上的和寄存器move进行的字符串等,尽管应该避免这些(见下文)。大多数架构确实有一些东西可以帮助这些有限的内存到内存的移动。SIDI
如果考虑到硬件,这很有意义。有地址线和数据线。处理器在地址线上发出要访问哪个存储器地址的信号,然后通过数据线读取或写入数据。因此数据必须通过缓存或处理器才能到达其他内存。事实上,如果您查看第 145 页上的此参考资料MOVS,您会看到绝对不能使用及其朋友的强烈声明:
请注意,当 REP MOVS 指令将一个字写入目标时,它会在同一时钟周期内从源读取下一个字。如果 P2 和 P3 上这两个地址中的位 2-4 相同,则可能会发生缓存组冲突。换句话说,如果 ESI+WORDSIZE-EDI 能被 32 整除,则每次迭代都会额外增加一个时钟。避免缓存组冲突的最简单方法是将源和目标对齐 8。切勿在优化的代码,即使在 16 位模式下也是如此。
在许多处理器上,REP MOVS 和 REP STOS 可以通过一次移动 16 个字节或整个缓存行来快速执行。仅当满足某些条件时才会发生这种情况。根据处理器的不同,快速字符串指令的条件通常是:计数必须很高、源和目标必须对齐、方向必须向前、源和目标之间的距离必须至少为高速缓存行大小,并且源和目标的内存类型必须是回写式或组合写式(通常可以假设满足后一个条件)。
在这些条件下,速度与矢量寄存器移动所能获得的速度一样高,甚至在某些处理器上更快。虽然字符串指令非常方便,但必须强调的是,在许多情况下其他解决方案更快。如果不满足上述快速移动的条件,那么使用其他方法可以获得很多好处。
从某种意义上说,这也解释了为什么寄存器到寄存器的移动是可以的(尽管还有其他原因)。也许我应该说,这解释了为什么他们不需要在板上使用非常特殊的硬件......寄存器都在处理器中;无需通过地址访问总线进行读写。