Den*_*nis 20 assembly x86-64 cpu-registers
根据英特尔在x64中,以下寄存器称为通用寄存器(RAX,RBX,RCX,RDX,RBP,RSI,RDI,RSP和R8-R15)https://software.intel.com/en-us/articles/介绍到x64组装.
在下面的文章中,写了RBP和RSP是专用寄存器(RBP指向当前堆栈帧的基础,RSP指向当前堆栈帧的顶部). https://www.recurse.com/blog/7-understanding-c-by-learning-assembly
现在我有两个相互矛盾的陈述.英特尔声明应该是值得信赖的,但是什么是正确的,为什么RBP和RSP被称为通用目的?
谢谢你的帮助.
jll*_*gre 17
通用意味着所有这些寄存器都可以与任何使用通用寄存器进行计算的指令一起使用,例如,您无法通过指令指针(RIP)或标志寄存器(RFLAGS)执行任何操作.
其中一些寄存器被设想用于特定用途,通常是.最关键的是RSP和RBP.
如果您需要将它们用于您自己的目的,您应该在将其他内容存储之前保存其内容,并在完成后将其恢复为原始值.
如果寄存器可以是的操作数add
,或者可以在寻址模式下使用,则它是“通用”,与FS
段寄存器或RIP之类的寄存器相反。即使其他类型的寄存器也可以保存整数,GP寄存器也称为“整数寄存器”。
在计算机体系结构中,CPU内部处理整数寄存器/指令与FP / SIMD寄存器/指令分开是很常见的。例如,英特尔Sandybridge系列CPU具有单独的物理寄存器文件,用于重命名GP整数和FP /向量寄存器。这些简称为整数与FP寄存器文件。(在这里FP是内核不需要保存/恢复即可使用GP寄存器的所有事物的简写,而保留用户空间的FPU / SIMD状态不变。)FP寄存器文件中的每个条目都是256位宽(包含一个AVX ymm向量),但整数寄存器文件条目仅必须为64位宽。
在重命名段寄存器的CPU上(Skylake不会),我想那将是整数状态的一部分,RFLAGS + RIP也是如此。但是,当我们说“整数寄存器”时,通常是专门指通用寄存器。
每个寄存器对于某些指令都有一些特殊性,除了x86-64中添加的某些全新寄存器:R8-R15。这些不会将其视为通用 。原始8(低16)可追溯到8086,甚至在原始8086中也隐含使用它们。
对于RSP,它特别适用于push / pop / call / ret,因此大多数代码从不将其用于其他任何用途。(并且在内核模式下,异步用于中断,因此您真的无法像在用户空间代码中那样将其存储在某个地方以获取额外的GP寄存器:ESP是否像EAX一样通用?)
但是在受控条件下(就像没有信号处理程序一样),您不必对堆栈指针使用RSP。例如,您可以使用它来读取带有pop的循环中的数组,例如此代码高尔夫球的答案。(我实际上使用的esp
是32位代码,但有相同的区别:pop
比lodsd
Skylake 快,而两者均为1字节。)
另请参见x86 Assembly-为什么在调用约定中保留[e] bx?以获得部分清单。
我主要将其限制为用户空间指令,特别是现代编译器实际上可能从C或C ++代码发出的指令。对于具有很多隐式用法的reg,我并不想穷尽所有。
rax
:单操作数[i] mul / [i] div / cdq / cdqe,字符串指令(stos)cmpxchg
等),以及许多直接指令(如2字节cmp al, 1
或5字节)的特殊短编码add eax, 12345
(无ModRM)字节)。另请参见codegolf.SE x86 / x64机器代码中的打高尔夫球技巧。
还有xchg
-with-eax的0x90 nop
来源(nop
在x86-64中成为单独记录的指令之前,因为xchg eax,eax
eax零扩展到RAX中,因此无法使用0x90
编码。但是仍然xchg rax,rax
可以汇编为REX.W = 1 0x90。)
rcx
:移位计数,- rep
字符串计数,慢速loop
指令rdx
:rdx:rax
用于除法和乘法,并由cwd / cdq / cqo为其设置。 rdtsc
。 BMI2mulx
。rbx
:8086 xlatb
。 cpuid
使用EAX..EDX的全部四个。486 cmpxchg8b
,x86-64cmpxchg16b
。大多数32位编译器都会发出cmpxchg8
for std::atomic<long long>::compare_exchange_weak
。(不过,如果目标是奔腾或更高版本,则纯负载/纯存储可以使用SSE MOVQ或x87 fild / fistp。)64位编译器将使用64位lock cmpxchg
,而不是cmpxchg8b。
一些64位编译器会发出cmpxchg16b
的atomic<struct_16_bytes>
。RBX具有原始8的最少隐式用法,但lock cmpxchg16b
实际上是少数几个编译器之一。
rsi
/ rdi
:字符串操作,包括rep movsb
某些编译器有时内联的字符串。(gcc rep cmpsb
在某些情况下还会内联字符串文字,但这可能不是最佳选择)。rbp
:(leave
仅比mov rsp, rbp
/ 慢1uop。gcc pop rbp
实际上不能在带有帧指针的函数中使用它pop rbp
)。还有enter
没人用过的可怕的慢。rsp
:堆栈操作:push / pop / call / ret和leave
。(和enter
)。并且在内核模式(不是用户空间)中,硬件异步使用它来保存中断上下文。这就是为什么内核代码不能有红色区域的原因。
r11
:syscall
/ sysret
用它来保存/恢复用户空间的RFLAGS。(与RCX一起保存/恢复用户空间的RIP)。
寻址模式编码的特殊情况:
(另请参见不允许将rbp作为SIB的基础?这只是关于寻址模式,我在其中复制了此答案的这一部分。)
rbp
/ r13
不能是没有位移的基址寄存器:该编码的意思是:(在ModRM中)rel32
(相对于RIP),或者(在SIB中)disp32
没有基址寄存器。(r13
在ModRM / SIB中使用相同的3位,因此此选择通过不使指令长度解码器查看REX.B位以获得第4个基寄存器位来简化解码)。 [r13]
组装到[r13 + disp8=0]
。 [r13+rdx]
组装到[rdx+r13]
(如果可以的话,通过交换基数/索引来避免问题)。
rsp
/ r12
作为基址寄存器始终需要一个SIB字节。(base = RSP的ModR / M编码是转义码,用于向SIB字节发送信号,并且如果r12
处理方式不同,则更多的解码器将不得不关心REX前缀)。
rsp
不能是索引寄存器。这样就可以进行编码[rsp]
,它比有用[rsp + rsp]
。(英特尔本可以为32位寻址模式(386中的新增功能)设计ModRM / SIB编码,因此只有在base = ESP时才可以使用没有索引的SIB。这将成为[eax + esp*4]
可能,并且仅排除[esp + esp*1/2/4/8]
。但这没有用,因此他们通过使index = ESP成为不带索引的代码来简化硬件,而不考虑基础,这允许两种冗余方式对任何基础或base + disp寻址模式进行编码:有或没有SIB。
r12
可以是索引寄存器。与其他情况不同,这不会影响指令长度解码。而且,不能像其他情况一样使用更长的编码来解决。AMD希望AMD64的寄存器组尽可能正交,因此在索引/无索引解码中,他们花了一些额外的晶体管来检查REX.X才有意义。例如,[rsp + r12*4]
要求index = r12,因此r12
没有完全通用的目的会使AMD64成为较差的编译器目标。
0: 41 8b 03 mov eax,DWORD PTR [r11]
3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP
7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP
b: 41 8b 06 mov eax,DWORD PTR [r14]
e: 41 8b 07 mov eax,DWORD PTR [r15]
11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index
Run Code Online (Sandbox Code Playgroud)
当所有寄存器都可以用于任何东西时,编译器会喜欢它,只限制一些特殊情况下的寄存器分配。这就是寄存器正交性的含义。
归档时间: |
|
查看次数: |
20823 次 |
最近记录: |