我想知道在集会中是否允许这样做,
movl (%edx) (%eax)
我猜他会在第一个操作数中访问内存并输入 第二个操作数的记忆,类似于* a = * b,但我还没有看到任何处理这样的例子,所以我猜测它是不允许的。 另外,我被告知这是不允许的
leal %esi (%edi)
为什么?最后,还有其他类似的功能我应该知道是不允许的。
答案 0 :(得分:5)
movl (mem), (mem)
mov dword [eax], [ecx] ; or the equivalent in Intel-syntax
无效,因为 x86机器代码没有an encoding for 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
(字符串移动)和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)
无效。除了一组有限的操作数之外,您可能无法直接在我熟悉的任何体系结构上执行内存到内存移动。例如,通过英特尔兼容处理器上的move
和SI
寄存器,字符串DI
等异常,但应避免这些(见下文)。大多数架构都有一些东西可以帮助这些有限的记忆内存移动。
如果考虑硬件,这很有意义。有地址线和数据线。处理器通知地址线上要访问的存储器地址,然后通过数据线读取或写入数据。因为这个数据必须通过缓存或处理器才能到达其他内存。事实上,如果你看一下第145页的this reference,你会看到MOVS
及其朋友绝对不能使用的强烈声明:
注意,REP MOVS指令将一个字写入 目的地,它在同一时钟中从源读取下一个字 周期。如果位2-4相同,则可能存在缓存库冲突 这两个地址在P2和P3上。换句话说,你会得到一个 如果ESI + WORDSIZE-EDI是,则每次迭代额外增加一个时钟 可被32整除。避免缓存库冲突的最简单方法是 将源和目标对齐8.永远不要使用MOVSB或MOVSW 优化的代码,甚至不是16位模式。
在许多处理器上,REP MOVS和REP STOS可以通过移动快速执行 一次16个字节或整个高速缓存行。这种情况只发生在 满足某些条件。取决于处理器,条件 通常,快速字符串指令必须是计数 高,源和目的地必须对齐,方向必须 向前,源和目的地之间的距离必须是 至少缓存行大小,以及源和的内存类型 目的地必须是回写或写入组合(你可以 通常假设满足后一条件。)
在这些条件下,速度与您可以获得的速度一样高 向量寄存器在某些处理器上移动甚至更快。虽然 字符串指令可以很方便,必须强调 在许多情况下,其他解决方案更快。如果符合上述条件 如果没有满足快速移动那么使用其他可以获得很多收益 方法
在某种意义上,这也解释了注册移动的注册是否正常(尽管还有其他原因)。也许我应该说,它解释了为什么他们不需要板上非常特殊的硬件......寄存器都在处理器中;没有必要通过地址访问总线进行读写。