为什么不允许在内存之间移动? [英] Why isn't movl from memory to memory allowed?

查看:132
本文介绍了为什么不允许在内存之间移动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以在汇编中使用

 movl (%edx) (%eax) 

我猜想它会访问第一个操作数中的内存并放入 第二个操作数的内存,类似* a = * b,但是我没有看到任何处理此类的示例,因此我猜想这是不允许的. 另外,我被告知不允许这样做

 leal %esi (%edi)

那是为什么?最后,还有其他类似的功能我不应该被禁止.

解决方案

movl (mem), (mem)

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

无效,因为 x86机器代码没有 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)( http://agner.org/optimize/.)

无论如何,我看不出这很有用. 如果需要的话,您可能没有充分利用寄存器.找出可能的情况下,如何在复制时即时处理数据.当然,有时候您只需要先进行加载,然后再进行存储,例如在排序例程中,以根据一个成员进行比较后交换结构的其余部分.使用xmm寄存器在较大的块中移动是个好主意.


leal %esi, (%edi)

这里有两个问题:

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

第二,lea的目的地必须是寄存器.没有编码需要第二个有效地址才能将目标存储到内存中.


BTW,均无效,因为您在两个操作数之间省略了,.

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

其余的答案仅在修复该语法错误之后讨论代码.

I was wondering if this is allowed in assembly,

 movl (%edx) (%eax) 

I would have guessed that it access the memory in the first operand and puts in the memory of the second operand, something like *a = *b but I haven't seen any example dealing with such so I'm guessing it's not allowable. Also, I've been told that this isn't allowed

 leal %esi (%edi)

why is that? Lastly, are there other similar functions I should be aware that aren't allowed.

解决方案

movl (mem), (mem)

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

is invalid because x86 machine code doesn't have an encoding for mov with two addresses. It has mov r32, r/m32 and mov r/m32, r32. Reg-reg moves can be encoded using either the mov r32, r/m32 opcode or the mov r/m32, r32 opcode. Many other instructions have two opcodes, one where the dest has to be a register, and one where the src has to be a register.

(And there are some specialized forms, like mov r32, imm32, or movabs r64, [64bit-absolute-address].)

See the x86 instruction set reference manual (links in the x86 tag wiki https://stackoverflow.com/tags/x86/info). I used Intel/NASM syntax here because that's what the insn ref manual does.

Very few instructions can do a load and store to two different addresses, e.g. movs (string-move), and push/pop (mem) (What x86 instructions take two (or more) memory operands?). Many ALU instructions are available with a memory destination, which makes them do a read-modify-write on a single memory location.


There are no instructions that take two arbitrary effective-addresses (i.e. specified with a flexible addressing mode). movs has implicit source and dest operands, and push has an implicit dest (esp).

An x86 instruction has at most one ModRM byte, and a ModRM can only encode one reg/memory operand (2 bits for mode, 3 bits for base register), and another register-only operand (3 bits). With an escape code, ModRM can signal a SIB byte to encode base + scaled-index for the memory operand, but there's still only room to encode one memory operand.

As I mentioned above, the memory-source and memory-destination forms of the same instruction (asm source mnemonic) use two different opcodes. As far as the hardware is concerned, they are different instructions.


The reasons for this design choice are probably partly implementation complexity: If it's possible for a single instruction to need two results from an AGU (address-generation-unit), then the wiring has to be there to make that possible. Some of this complexity is in the decoders that figure out which instruction an opcode is, and parse the remaining bits and bytes to figure out what the operands are. Since no other instruction can have multiple r/m operands, it would cost extra transistors (silicon area) to support it.

It also potentially gives an instruction five input dependencies (two-register addressing mode for the store address, same for the load address, and the load date). When 8086 / 80386 was being designed, superscalar / out-of-order / dependency tracking probably wasn't on the radar. 386 added a lot of new instructions, so a mem-to-mem encoding of mov could have been done, but wasn't. If 386 had started to forward results directly from ALU output to ALU input and stuff like that (to reduce latency compared to always committing results to the register file), then this reason would have been one of the reasons it wasn't implemented.

If it existed, Intel P6 would probably decode it to two separate uops, a load and a store. It certainly wouldn't make sense to introduce now, or any time after 1995 when P6 was designed and simpler instructions gained more of a speed advantage over complex ones. (See http://agner.org/optimize/ for stuff about making code run fast.)

I can't see this being very useful, anyway. If you want this, you're probably not making enough use of registers. Figure out how to process your data on the fly while copying, if possible. Of course, sometimes you just have to do a load and then a store, e.g. in a sort routine to swap the rest of a struct after comparing based on one member. Doing moves in larger blocks, using xmm registers, is a good idea.


leal %esi, (%edi)

Two problems here:

First, registers don't have addresses. A bare %esi is not a valid effective-address.

Second, lea's destination must be a register. There's no encoding where it takes a second effective-address to store the destination to memory.


BTW, neither are valid because you left out the , between the two operands.

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

The rest of the answer only discusses the code after fixing that syntax error.

这篇关于为什么不允许在内存之间移动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆