我了解了一个地址,两个地址和三个地址指令,但是现在我想知道x86使用哪种地址指令?
答案 0 :(得分:5)
x86是一台寄存器机,使用[rdi + rax*4]
之类的寻址方式,其中一条指令的最多1个操作数可以是显式存储器地址,而不是寄存器。 (尽管有些指令可以有2个内存操作数,但其中一个或两个都是隐式的,尽管:What x86 instructions take two (or more) memory operands?)
典型的x86整数指令有2个操作数,两个操作数都是显式的,例如add eax, edx
,它执行eax+=edx
。
旧版x87 FP代码在x87堆栈中使用1-operand指令,例如faddp st1
,其中x87堆栈的顶部(st0
)是隐式操作数。 SSE2是x86-64的基线,因此不再被广泛使用。
现代FP代码使用SSE / SSE2 2操作数指令,例如addsd xmm0,xmm1
或3操作数AVX编码,例如vaddsd xmm2, xmm0, xmm1
有x86指令带有0、1、2、3甚至4个显式操作数。
有多种指令格式,但是显式的reg / memory操作数通常在操作码字节之后的ModR / M字节中进行编码。它具有3个字段:
reg
,间接注册[reg]
,[reg + disp8],[reg+disp32]
)。带位移位的模式表示这些字节跟随ModR / M字节。大多数指令至少有两种编码可用:reg /内存目标或reg /内存源。如果您想要的操作数都是寄存器,则可以使用add r/m32, r32
或add r32, r/m32
之一的操作码。
公用指令还具有用于立即源格式的其他操作码,但是通常它们将ModR / M中的reg
字段用作额外的操作码位,因此您仍然只能获得2个操作数,例如add eax, 123
。例外情况是imul
的立即数加上286,例如imul eax, [rdi + rbx*4], 12345
。它没有与其他立即指令共享编码空间,而是在操作码隐含的ModR / M plus 中具有寄存器dst和r / m源。
某些单操作数指令使用与将reg
字段用作额外的操作码位相同的技巧,但没有立即数。例如neg r/m32
,not r/m32
,inc r/m32
或shl
/ shr
/旋转将编码转换为隐式1(而不是cl
或即时)。因此,很遗憾,您无法复制和移动(直到BMI2)。
有些特殊情况下的编码可以提高代码密度,例如push rax
/ push rdx
的单字节编码会将reg
字段打包到操作码字节的低3位中。在16/32位模式下,inc
/ dec
任何寄存器的一字节编码。但是在64位模式下,这些0x4?
代码用作REX前缀,以扩展reg
和r/m
字段以提供16个体系结构寄存器。
还有一些带有某些或所有隐式操作数的指令,例如movsb
,它将一个字节从[rsi]
复制到[rdi]
,并且可以与rep
前缀重复rcx
次。
或者mul ecx
做edx:eax = eax * ecx
。一个显式源操作数,一个隐式源和2个隐式目标寄存器。 div
/ idiv
相似。
具有至少1个显式reg / mem操作数的指令对其使用ModR / M编码,但是具有零显式操作数(如movsb
或cdq
)的指令没有ModR / M字节。他们只是有操作码。有些指令根本没有like mfence
,甚至没有隐含的操作数。
不能通过ModR / M来立即发送操作数,只能通过操作码本身来发出信号,因此push imm32
or push imm8
拥有自己的操作码。隐式目标([rsp]
处的内存,RSP本身已更新为rsp-=8
)。
LEA是一种变通方法,它可以使x86 3操作数移位加法,例如lea eax, [rdi + rdi*2 + 123]
可以在一条指令中完成eax = rdi*3 + 123
。请参见Using LEA on values that aren't addresses / pointers?。目标寄存器在ModR / M的reg
字段中编码,而两个源寄存器在寻址模式下编码。 (涉及到一个SIB字节,该字节的存在通过ModR / M字节使用编码来表示,否则将表示base = RSP。)
VEX前缀(由AVX引入)提供3个操作数的指令,例如bzhi eax, [rsi], edx
或vaddps ymm0, ymm1, [rsi]
。(对于许多指令,第二个源是可选的内存,但对于某些人来说,这是第一个来源。)
第三个操作数被编码为2或3字节的VEX前缀。
有3个操作数的非VEX指令,例如vpblendvb xmm1, xmm2/m128, <XMM0>
之类的SSE4.1变量混合,其中XMM0是使用该寄存器的隐式操作数。
AVX版本使其具有非破坏性(在VEX前缀中编码有单独的目标),和使混合控制操作数显式(编码为1字节的高4位)即时)。 这为我们提供了一个包含4个显式操作数VPBLENDVB xmm1, xmm2, xmm3/m128, xmm4
的指令。
x86非常狂野,并且已经扩展了许多次,但是典型的整数代码主要使用2操作数指令,并附带大量LEA来保存指令。