问题描述
我了解了一个地址,两个地址和三个地址指令,但是现在我想知道x86使用哪种地址指令?
I learned about one address, two address, and three address instruction, but now I'd like to know, what kind of address instruction does x86 use?
推荐答案
x86是一台CISC寄存器机器,其中使用<$>之类的寻址模式,一条指令的最多1个操作数可以是显式存储器地址而不是寄存器。 c $ c> [rdi + rax * 4] 。 (尽管有些指令可以具有2个内存操作数,但其中一个或两个都是隐式的:)
x86 is a CISC register machine, where at most 1 operand for any instruction can be an explicit memory address instead of a register, using an addressing mode like [rdi + rax*4]
. (There are instruction which can have 2 memory operands with one or both being implicit, though: What x86 instructions take two (or more) memory operands?)
典型的x86整数指令有2个操作数,都显式,例如 ,它执行 eax + = edx
。
和一些真正的1操作数ALU指令(无其他隐式操作数) ),例如 inc
/ dec
, neg
,不是
,它们是隐式1的加/减,0的sub或-1的XOR(有些具有不同的FLAGS语义)的快捷方式。还有 bswap
。同样,隐式计数为1的shift / rotate指令基本上是1操作数,有些汇编程序的确允许您编写 shr%eax
。
And some truly 1-operand ALU instructions (no implicit other operand) like inc
/dec
, neg
, not
which are shortcuts for add/sub of implicit 1, or sub from 0, or XOR with -1 (some with different FLAGS semantics). And there's bswap
. Also the shift/rotate instructions with an implicit 1 count are basically 1-operand, and some assemblers do let you write shr %eax
.
旧版x87 FP代码在x87堆栈中使用1-操作数指令,例如 faddp st1
,其中x87堆栈的顶部( st0
)是一个隐式操作数。 SSE2是x86-64的基线,因此不再被广泛使用。
Legacy x87 FP code uses 1-operand instructions with the x87 stack, like faddp st1
where the top of the x87 stack (st0
) is an implicit operand. SSE2 is baseline for x86-64, so it's no longer widely used.
现代FP代码使用SSE / SSE2 2操作数指令,例如 addsd xmm0,xmm1
或3种操作数的AVX编码,例如 vaddsd xmm2,xmm0,xmm1
Modern FP code uses SSE/SSE2 2-operand instructions like addsd xmm0,xmm1
or 3-operand AVX encodings like vaddsd xmm2, xmm0, xmm1
有x86指令带有0、1、2、3甚至4个显式操作数。
There are x86 instructions with 0, 1, 2, 3, and even 4 explicit operands.
有多种指令格式,但是显式的reg / memory操作数通常在操作码字节之后的ModR / M字节中编码。它具有3个字段:
There are multiple instruction formats, but explicit reg/memory operands are normally encoded in a ModR/M byte that follows the opcode byte(s). It has 3 fields:
- r / m操作数的2位模式(直接注册
reg
,间接注册[reg]
,[reg + disp8]
,[ reg + disp32]
)。带位移位的模式表示这些字节紧随ModR / M字节。 - 3位r / m字段(用于寄存器的直接或间接寄存器号,也可以是转义码)这意味着ModRM之后有一个标度/索引/基本SIB字节,可以为r / m操作数编码标度索引寻址模式。有关特殊情况的详细信息,请参见
- 3位reg字段,始终是一个寄存器号。 (或者以单操作数或
r / m,立即
的说明,,例如用于移位/旋转选择哪种操作。)
- 2-bit Mode for the r/m operand (register direct
reg
, register indirect[reg]
,[reg+disp8]
,[reg+disp32]
). The modes with displacement bits signal that those bytes follow the ModR/M byte. - 3-bit r/m field (the register number for register direct or indirect, or can be an escape code that means there's a Scale/Index/Base SIB byte after ModRM which can encode scaled-index addressing modes for the r/m operand). See rbp not allowed as SIB base? for the details of the special cases / escape codes.
- 3-bit reg field, always a register number. (Or in one-operand or
r/m, immediate
instructions, used as extra opcode bits, e.g. for shifts/rotates selects which kind.)
大多数指令至少有两种编码可用:reg /内存目标或reg /内存源。如果要使用的操作数都是寄存器,则可以使用任一操作码,添加r / m32,r32
或添加r32,r / m32
。 (某些汇编器。理论上,汇编器/
Most instructions are available in at least 2 encodings, reg/memory destination or reg/memory source. If the operands you want are both registers, you can use either opcode, either the add r/m32, r32
or add r32, r/m32
. (Some assemblers have syntax to let you select the non-default encoding. In theory an assembler / compiler could use these choices as a watermark to show which tool produced it.)
通用指令还具有用于立即源格式的其他操作码,但是通常它们使用<$ c ModR / M中的$ c> reg 字段作为额外的操作码位,因此您仍然只能获得2个操作数,例如 add eax,123
。例外情况是 imul
的立即数加上186,例如。它没有与其他立即指令共享编码空间,而是在操作码所隐含的ModR / M plus 中具有寄存器dst和ar / m源。
Common instructions also have other opcodes for immediate source forms, but typically they use the reg
field in ModR/M as extra opcode bits, so you still only get 2 operands like add eax, 123
. An exception to this is the immediate form of imul
added with 186, e.g. imul eax, [rdi + rbx*4], 12345
. Instead of sharing coding space with other immediate instructions, it has a register dst and a r/m source in ModR/M plus the immediate operand implied by the opcode.
某些单操作数指令使用相同的技巧,将 reg
字段用作额外的操作码位,但没有立即数。例如 ne r / m32
,不是r / m32
, inc r / m32
或 shl
/ shr
/旋转编码,其隐式偏移1(而不是 cl
或立即数)。因此很遗憾,您不能复制和移位(直到BMI2)。
Some one-operand instructions use the same trick of using the reg
field as extra opcode bits, but without an immediate. e.g. neg r/m32
, not r/m32
, inc r/m32
, or the shl
/shr
/rotate encodings that shift by an implicit 1 (not by cl
or an immediate). So unfortunately you can't copy-and-shift (until BMI2).
有一些特殊情况下的编码可以提高代码密度,例如 push rax
/ push rdx
将 reg
字段打包到操作码字节的低3位。在16/32位模式下,任何寄存器的 inc
/ dec
的一字节编码。但是在64位模式下,这些 0x4?
代码用作REX前缀,以扩展 reg
和 r / m
字段可提供16个体系结构寄存器。
There are some special-case encodings to improve code density, like single-byte encodings for push rax
/push rdx
that pack the reg
field into the low 3 bits of the opcode byte. And in 16/32-bit mode, one-byte encodings for inc
/dec
any register. But in 64-bit mode those 0x4?
codes are used as REX prefixes to extend the reg
and r/m
fields to provide 16 architectural registers.
有还包含一些或所有隐式操作数的指令,例如 movsb
,它将一个字节从 [rsi]
复制到 [rdi]
,并且可以与 rep
前缀一起使用以重复该 rcx
次。
There are also instructions with some or all implicit operands, like movsb
which copies a byte from [rsi]
to [rdi]
, and can be used with a rep
prefix to repeat that rcx
times.
或 mul ecx
执行 edx:eax = eax * ecx
。一个显式源操作数,一个隐式源和2个隐式目标寄存器。 div
/ idiv
与此相似。
Or mul ecx
does edx:eax = eax * ecx
. One explicit source operand, one implicit source, and 2 implicit destination registers. div
/idiv
are similar.
带有at的指令至少1个显式reg / mem操作数对其使用ModR / M编码,但显式操作数为零的指令(例如 movsb
或)没有ModR / M字节。他们只是有操作码。某些指令根本没有操作数,甚至没有隐式操作,像 mfence $ c一样,。
Instructions with at least 1 explicit reg/mem operand use a ModR/M encoding for it, but instructions with zero explicit operands (like movsb
or cdq
) have no ModR/M byte. They just have the opcode. Some instructions have no operands at all, not even implicit, like mfence
.
不能通过ModR / M发出立即操作数,只能通过操作码本身发出信号,因此有自己的操作码。隐式目标(位于 [rsp]
的内存,并且RSP本身已更新为 rsp- = 8
)。
Immediate operands can't be signalled through ModR/M, only by the opcode itself, so push imm32
or push imm8
have their own opcodes. The implicit destinations (memory at [rsp]
, and RSP itself being updated to rsp-=8
).
LEA是一种变通方法,可提供x86 3操作数的移位和添加,例如 lea eax,[rdi + rdi * 2 + 123]
在一条指令中执行 eax = rdi * 3 + 123
。参见或 vaddps ymm0,ymm1,[rsi]
。 (对于许多指令,第二个源是可选的内存,但对于某些指令,它是第一个源。)
VEX prefixes (introduced with AVX) provide 3-operand instructions like bzhi eax, [rsi], edx
or vaddps ymm0, ymm1, [rsi]
. (For many instructions, the 2nd source is the one that's optionally memory, but for some it's the first source.)
第三个操作数编码为2或3-字节VEX前缀。
The 3rd operand is encoded in the 2 or 3-byte VEX prefix.
有一些3运算符的非VEX指令,例如SSE4.1变量混合,例如其中XMM0是使用该寄存器的隐式操作数。
There are a few 3-operand non-VEX instructions, such as SSE4.1 variable blends like vpblendvb xmm1, xmm2/m128, <XMM0>
where XMM0 is an implicit operand using that register.
AVX版本使其具有非破坏性(带有单独的目标e在VEX前缀中进行n编码),和使混合控制操作数显式(在1字节立即数的高4位中进行编码)。 这为我们提供了一个包含4个显式操作数的指令, VPBLENDVB xmm1,xmm2,xmm3 / m128,xmm4
。
The AVX version makes it non-destructive (with a separate destination encoded in the VEX prefix), and makes the blend-control operand explicit (encoded in the high 4 bits of a 1-byte immediate). This gives us an instruction with 4 explicit operands, VPBLENDVB xmm1, xmm2, xmm3/m128, xmm4
.
x86非常疯狂,并且已经扩展了许多次,但是典型的整数代码主要使用2操作数指令,并且大量LEA用于保存指令
x86 is pretty wild and has been extended many times, but typical integer code uses mostly 2-operand instructions, with a good amount of LEA thrown in to save instructions.
这篇关于x86 cpu有什么样的地址指令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!